> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OCI Database Instances Should Not Be Publicly Accessible

### More Info:

Database instances should not be publicly accessible. Public database endpoints are continuously scanned by attackers and are a primary vector for data breaches.

### Risk Level

Critical

### Address

Compliance, Security

### Compliance Standards

* APRA CPS 234 (Australia)
* AWS Startup Security Baseline
* AWS Well Architected Framework
* BSI C5 (Germany)
* Brazil LGPD
* CCPA / CPRA (California)
* CIS Critical Security Controls v8
* CMMC 2.0
* CSA Cloud Controls Matrix v4
* DPDPA
* Digital Operational Resilience Act (EU)
* FedRAMP
* GDPR
* HIPAA
* HITRUST CSF
* ISO/IEC 27017
* ISO/IEC 27018
* ISO/IEC 27701
* KSA PDPL
* MAS Technology Risk Management (Singapore)
* MITRE ATT\&CK (Cloud)
* NIS2 Directive
* NIST
* NIST CSF
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* PCI
* Reserve Bank of India (RBI) Cyber Security Framework
* Reserve Bank of India (RBI) Master Direction – Information Technology Framework
* SOC2
* SWIFT Customer Security Controls Framework
* Sarbanes-Oxley IT General Controls
* Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
* UK NCSC Cyber Assessment Framework

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        To make sure databases used with **OCI Database Management/Monitoring are not publicly accessible**, you need to (a) stop public endpoints and (b) use private endpoints / private subnets. Below are step‑by‑step console instructions.

        ***

        ## 1. Autonomous Databases (ADB) – move to private access

        You cannot flip an existing **public** ADB to **private**; you must create (or clone) one with **private endpoint access** and then migrate.

        ### 1.1 Check if your ADB is public

        1. In the OCI Console, go to: **Menu → Oracle Database → Autonomous Database**.
        2. Select your compartment.
        3. Open your Autonomous Database.
        4. On the **Details** page, look at:
           * **Access Type / Network Access** or
           * **Access Control**\
             If it shows things like **“Allow secure access from everywhere”**, **“Allow public endpoint access”**, or similar, it is public.

        ### 1.2 Create a new ADB with private endpoint

        1. In the **Autonomous Databases** page, click **Create Autonomous Database**.
        2. Fill in basic details (name, workload type, license, etc.).
        3. Under **Network access / Access type**:
           * Select **Private endpoint access only** (or “Secure access from private endpoints only”).
           * Choose your **VCN** and **private subnet** (subnet should:
             * Have no route to an Internet Gateway.
             * Be associated with a security list/NSG that *does not* allow `0.0.0.0/0` on DB ports).
           * Optionally specify **Network Security Groups (NSG)** that limit access to only your app / monitoring subnets.
        4. Click **Create Autonomous Database**.

        ### 1.3 Migrate data and cut over

        1. Use your preferred method (Data Pump, GoldenGate, DB links, etc.) to move data from the old (public) ADB to the new (private) ADB.
        2. Update application and **Database Management monitoring** connections to point to the new private ADB (via private IP / private endpoint).
        3. When fully validated, **terminate the old public ADB**:
           * Open the old ADB → **More Actions → Terminate**.

        ***

        ## 2. DB Systems / Bare Metal / VM Databases – remove public exposure

        For DB Systems, you must ensure the underlying compute has no public IP and sits in a private subnet with restricted security rules.

        ### 2.1 Identify DB Systems with public IP

        1. Go to **Menu → Oracle Database → DB Systems** (or VM/BM DB Systems).
        2. Open each DB System and note the **subnet**.
        3. Go to **Menu → Core Infrastructure → Networking → Virtual Cloud Networks**.
        4. Open the relevant VCN → click **Subnets**, select the subnet:
           * Check if its route table has a route to an **Internet Gateway**.
           * Check **Security Lists** and/or **NSGs** for rules allowing `0.0.0.0/0` on DB ports (e.g., 1521).

        ### 2.2 Remove public IPs from the database host

        1. Go to **Menu → Compute → Instances**.
        2. Find the compute instance(s) associated with your DB System (usually named similarly).
        3. Click the instance → go to **Attached VNICs**.
        4. Click the **primary VNIC**.
        5. Under **Resources → IP Addresses**:
           * If a **Public IP** is attached:
             * Click the public IP.
             * Click **Actions → Unassign**.
           * If you cannot remove it (or the subnet is inherently public), you must:
             * Create a **new private subnet** (no IGW route, see next step), and
             * Recreate the DB System in that subnet, then migrate databases.

        ### 2.3 Ensure the subnet is private

        1. In **Networking → Virtual Cloud Networks**, open your VCN.
        2. Click **Route Tables** → open the route table used by the DB’s subnet.
        3. Confirm:
           * There is **no route** to an **Internet Gateway**.
           * If outbound internet is needed, use a **NAT Gateway**, not IGW:
             * Add route: `0.0.0.0/0` → **NAT Gateway**.
        4. Click **Subnets**, open the DB subnet, confirm it uses this route table.

        ### 2.4 Restrict security lists / NSGs

        1. In the VCN, go to **Security Lists** and/or **Network Security Groups** used by the DB subnet/DB host.
        2. Edit **Ingress Rules**:
           * Remove any rule like:
             * `Source CIDR: 0.0.0.0/0` with `Destination Port: 1521` (or other DB ports).
           * Add rules that:
             * Restrict `Source` to only:
               * Your app subnets, and/or
               * Subnets/hosts that run **Database Management/Monitoring agents**.
        3. If you use **NSGs**:
           * Go to **Network Security Groups → \[Your NSG] → Ingress Rules**.
           * Apply the same restrictions.

        ***

        ## 3. Configure Database Management (Monitoring) over private network

        Database Management does not require the database to be public; it can connect via private IP within the VCN.

        ### 3.1 Ensure a private path between DB and monitoring

        * If using **Database Management with agents**:
          1. Deploy the agent in:
             * The same subnet as the DB, or
             * A peered VCN with route and security rules allowing connectivity.
          2. Verify security lists/NSGs allow traffic between:
             * Agent subnet → DB subnet on DB listener port (e.g., 1521).
        * If using **Direct Connectivity from OCI services**:
          * Ensure the **service subnet** and **DB subnet** can communicate privately (VCN routing, no IGW needed).

        ### 3.2 Enable Database Management for the database

        1. Go to **Menu → Observability & Management → Database Management**.
        2. Click **Managed Databases**.
        3. Click **Register / Enable**:
           * Select your database (ADB or DB System).
           * Configure credentials, connection details (private host/SCAN, service name).
           * Ensure **hostname/IP** used is the **private** address, not a public IP.

        ***

        ## 4. Verification

        1. From **Networking → Public IPs**, confirm:
           * No public IP is assigned to DB hosts or DB endpoints.
        2. From your on-prem or external network:
           * Confirm DB listener ports (e.g., 1521) are **not reachable** from the public internet.
        3. In **Database Management → Managed Databases**:
           * Confirm the databases show as **Active** and metrics are being collected via the private network.

        If you share which exact OCI database type you’re using (Autonomous, VM DB System, Exadata Cloud, etc.), I can narrow these steps to that specific workflow.
      </Accordion>

      <Accordion title="Using CLI">
        Below are minimal, CLI‑only steps to make OCI database instances non‑public while keeping them usable for Database Monitoring.

        Assumptions:

        * You have `oci` CLI configured.
        * Databases are running on DB Systems (VM/BM) or on Compute with agents.
        * Goal: remove public IPs and block internet ingress on DB ports.

        ***

        ## 1. Identify DB systems and their VNICs

        ```bash theme={null}
        # 1. List DB systems in a compartment
        oci db system list \
          --compartment-id <compartment_ocid> \
          --all

        # 2. Get details for a specific DB system
        oci db system get \
          --db-system-id <db_system_ocid> \
          --query 'data["hostname","subnet-id","cluster-name"]'
        ```

        Get the compute instance(s) backing the DB system:

        ```bash theme={null}
        oci db node list \
          --compartment-id <compartment_ocid> \
          --db-system-id <db_system_ocid> \
          --all \
          --query 'data[]."vm-cluster-node-id"'
        ```

        (If you already know the instance OCID(s), skip to next step.)

        List VNIC attachments for each instance:

        ```bash theme={null}
        oci compute vnic-attachment list \
          --compartment-id <compartment_ocid> \
          --instance-id <instance_ocid> \
          --all \
          --query 'data[]."vnic-id"'
        ```

        Get VNIC details:

        ```bash theme={null}
        oci network vnic get \
          --vnic-id <vnic_ocid> \
          --query 'data["id","display-name","public-ip","private-ip","subnet-id"]'
        ```

        * If `"public-ip"` is non‑null → this DB node is publicly reachable.

        ***

        ## 2. Remove any public IP from DB VNICs

        Get private IP OCID associated with the VNIC:

        ```bash theme={null}
        oci network private-ip list \
          --vnic-id <vnic_ocid> \
          --all \
          --query 'data[].id'
        ```

        Remove the public IP from that private IP:

        ```bash theme={null}
        oci network private-ip update \
          --private-ip-id <private_ip_ocid> \
          --remove-public-ip true
        ```

        Verify:

        ```bash theme={null}
        oci network vnic get \
          --vnic-id <vnic_ocid> \
          --query 'data["public-ip","private-ip"]'
        ```

        `public-ip` should now be `null`.

        Repeat for all DB nodes.

        ***

        ## 3. Ensure subnet is not effectively public (security lists / NSGs)

        ### 3.1. Find subnet and security lists

        ```bash theme={null}
        oci network subnet get \
          --subnet-id <subnet_ocid> \
          --query 'data["id","security-list-ids","nsg-ids","route-table-id","cidr-block"]'
        ```

        List security lists:

        ```bash theme={null}
        oci network security-list get \
          --security-list-id <sec_list_ocid>
        ```

        Look for ingress rules that allow `0.0.0.0/0` to DB ports (e.g., 1521, 3306, 5432).

        ### 3.2. Replace ingress rules with restricted ones

        1. Create a file `ingress-rules.json` with only trusted sources (for example, app subnets or on‑prem CIDRs). Example pattern:

        ```json theme={null}
        [
          {
            "protocol": "6",
            "source": "<trusted_cidr_or_vcn_cidr>",
            "isStateless": false,
            "tcpOptions": {
              "destinationPortRange": {
                "min": 1521,
                "max": 1521
              }
            }
          }
        ]
        ```

        2. Export existing egress rules to reuse:

        ```bash theme={null}
        oci network security-list get \
          --security-list-id <sec_list_ocid> \
          --query 'data."egress-security-rules"' \
          > egress-rules.json
        ```

        3. Update the security list:

        ```bash theme={null}
        oci network security-list update \
          --security-list-id <sec_list_ocid> \
          --ingress-security-rules file://ingress-rules.json \
          --egress-security-rules file://egress-rules.json
        ```

        If you use NSGs instead of security lists, do the equivalent:

        ```bash theme={null}
        oci network nsg-rule list \
          --network-security-group-id <nsg_ocid>

        # Prepare JSON with restricted rules and update with:
        oci network nsg update \
          --network-security-group-id <nsg_ocid> \
          --nsg-rules file://nsg-rules.json
        ```

        ***

        ## 4. Verify Database Monitoring still works

        Database Monitoring (via Management Agent / Database Management) needs only private network reachability, not public internet.

        * Confirm DB is reachable from the Management Agent subnet or OCI service VCN.
        * Verify DB Management status (if used) via CLI:

        ```bash theme={null}
        oci db managed-database list \
          --compartment-id <compartment_ocid> \
          --all
        ```

        * Run a test metric fetch:

        ```bash theme={null}
        oci monitoring metric-data summarize-metric-data \
          --compartment-id <compartment_ocid> \
          --namespace oci_database \
          --query-text "CpuUtilization[1m]{resourceId='<db_ocid>'}.mean()"
        ```

        If metrics and DB Management targets are healthy, the DB is non‑public and still fully monitored.

        ***

        If you share whether your databases are DB Systems, Exadata VM Clusters, or autonomous DBs, I can give the exact CLI variants for that type.
      </Accordion>

      <Accordion title="Using Python">
        To make OCI database targets non‑public using Python, you remediate at the **network/VNIC** and **DB config** layers via the OCI Python SDK.

        Below is a concise, step‑by‑step approach with example code.

        ***

        ## 1. Prerequisites

        1. Install SDK:
           ```bash theme={null}
           pip install oci
           ```
        2. Have an OCI config file (`~/.oci/config`) with a profile (e.g. `DEFAULT`) that has rights to:
           * Read/update DB resources
           * Read/update VNICs / security lists / NSGs

        In Python:

        ```python theme={null}
        import oci

        config = oci.config.from_file("~/.oci/config", "DEFAULT")
        ```

        You’ll need the compartment OCID:

        ```python theme={null}
        COMPARTMENT_ID = "ocid1.compartment.oc1..xxxx"
        ```

        ***

        ## 2. Autonomous Databases – Disable Public Access / Restrict Access

        For **Autonomous DBs**, you can:

        * Disable public access entirely (where supported)
        * Or enable access control and restrict IPs/network resources

        ### 2.1. List Autonomous Databases

        ```python theme={null}
        from oci.database import DatabaseClient

        db_client = DatabaseClient(config)

        ads = oci.pagination.list_call_get_all_results(
            db_client.list_autonomous_databases,
            compartment_id=COMPARTMENT_ID
        ).data
        ```

        ### 2.2. Identify Publicly Accessible ADBs

        Check flags such as `is_access_control_enabled`, and (if present) any `is_public`‑style attribute (varies by service type/region).

        ```python theme={null}
        public_ads = []
        for ad in ads:
            # Example heuristic: no access control and using a public endpoint
            if not ad.is_access_control_enabled:
                public_ads.append(ad)

        print("Potentially public ADBs:", [ad.id for ad in public_ads])
        ```

        ### 2.3. Remediate: Enable Access Control / Remove Public Access

        **Option A – Enable access control and restrict by IP whitelist:**

        ```python theme={null}
        from oci.database.models import UpdateAutonomousDatabaseDetails

        ALLOWED_IPS = ["10.0.0.0/16"]  # adjust to your internal ranges

        for ad in public_ads:
            details = UpdateAutonomousDatabaseDetails(
                is_access_control_enabled=True,
                whitelisted_ips=ALLOWED_IPS
            )
            resp = db_client.update_autonomous_database(
                autonomous_database_id=ad.id,
                update_autonomous_database_details=details
            )
            print(f"Updated ADB {ad.id}, access control enabled")
        ```

        **Option B – If the service supports a “private endpoint only” setting**

        Some ADB flavors use a private endpoint (no public endpoint). Where supported, set the relevant field (for example, `private_endpoint_label` / private endpoint configuration; actual field names vary by ADB type and region). That often requires creating the ADB with private endpoint; you cannot always flip from public → private in-place. In those cases:

        * Create a **new ADB** with `private_endpoint_label` / private endpoint config in a private subnet.
        * Migrate data.
        * Decommission the public ADB.

        (That part is not fully automatable generically in one snippet because parameters depend heavily on your network.)

        ***

        ## 3. DB Systems (VM/BM DBs) – Remove Public IPs and Lock Down Network

        “Public” usually means:

        * VNIC has a public IP
        * Subnet routes to Internet Gateway
        * Security lists/NSGs allow 0.0.0.0/0 on DB ports

        You can:

        1. Remove public IP from the DB node VNIC.
        2. Tighten security rules.

        ### 3.1. List DB Systems and Nodes

        ```python theme={null}
        from oci.database import DatabaseClient
        from oci.core import VirtualNetworkClient

        db_client = DatabaseClient(config)
        vn_client = VirtualNetworkClient(config)

        db_systems = oci.pagination.list_call_get_all_results(
            db_client.list_db_systems,
            compartment_id=COMPARTMENT_ID
        ).data
        ```

        ### 3.2. For Each DB System, Get VNICs and Public IPs

        ```python theme={null}
        from oci.core import ComputeClient

        compute_client = oci.core.ComputeClient(config)

        for dbs in db_systems:
            nodes = oci.pagination.list_call_get_all_results(
                db_client.list_db_nodes,
                compartment_id=COMPARTMENT_ID,
                db_system_id=dbs.id
            ).data

            for node in nodes:
                vnics = compute_client.list_vnic_attachments(
                    compartment_id=COMPARTMENT_ID,
                    instance_id=node.hostname  # or node.id, depending on version
                ).data
        ```

        In practice, you usually go from `db_node.vnic_id` if present, or from instance OCID. Example using `db_node.vnic_id` (commonly available):

        ```python theme={null}
        for dbs in db_systems:
            nodes = oci.pagination.list_call_get_all_results(
                db_client.list_db_nodes,
                compartment_id=COMPARTMENT_ID,
                db_system_id=dbs.id
            ).data

            for node in nodes:
                vnic = vn_client.get_vnic(node.vnic_id).data
                if vnic.public_ip:
                    print(f"DB Node {node.id} has public IP {vnic.public_ip}")
        ```

        ### 3.3. Remediate: Remove Public IP From VNIC

        You cannot “unassign” a public IP on some primary VNICs in-place; the usual **safe** remediation patterns:

        * If it’s an **ephemeral public IP**:
          * Delete the public IP object (where applicable) or update the VNIC to `assign_public_ip="NO"` on secondary VNIC.
        * If primary VNIC:
          * Place DB System in a **private subnet** at creation time.
          * So remediation can mean: create new DB system in private subnet, migrate DB, and then terminate the old system.

        Programmatically checking and blocking any *new* DB Systems from being public is more practical than modifying all existing ones in place.

        Example (for secondary VNICs or assignable flag):

        ```python theme={null}
        from oci.core.models import UpdateVnicDetails

        for dbs in db_systems:
            nodes = oci.pagination.list_call_get_all_results(
                db_client.list_db_nodes,
                compartment_id=COMPARTMENT_ID,
                db_system_id=dbs.id
            ).data

            for node in nodes:
                vnic = vn_client.get_vnic(node.vnic_id).data
                if vnic.public_ip:
                    update_details = UpdateVnicDetails(assign_public_ip="NO")
                    vn_client.update_vnic(vnic.id, update_details)
                    print(f"Removed public IP from VNIC {vnic.id}")
        ```

        If the API rejects this on primary VNICs, you must instead use the **“recreate in private subnet”** path.

        ***

        ## 4. Tighten Security Lists / NSGs

        Even without a public IP, avoid 0.0.0.0/0 on DB ports.

        Example for security lists:

        ```python theme={null}
        sec_lists = oci.pagination.list_call_get_all_results(
            vn_client.list_security_lists,
            compartment_id=COMPARTMENT_ID
        ).data

        for sl in sec_lists:
            changed = False
            ingress = []
            for rule in sl.ingress_security_rules:
                # Detect overly broad rule on DB port (e.g., 1521)
                if (rule.source == "0.0.0.0/0" and 
                    rule.tcp_options and
                    rule.tcp_options.destination_port_range and
                    rule.tcp_options.destination_port_range.min == 1521 and
                    rule.tcp_options.destination_port_range.max == 1521):
                    changed = True
                    continue  # drop this rule
                ingress.append(rule)

            if changed:
                from oci.core.models import UpdateSecurityListDetails
                update_details = UpdateSecurityListDetails(
                    ingress_security_rules=ingress,
                    egress_security_rules=sl.egress_security_rules,
                    display_name=sl.display_name
                )
                vn_client.update_security_list(sl.id, update_details)
                print(f"Tightened security list {sl.display_name} ({sl.id})")
        ```

        You would replace `0.0.0.0/0` with your internal CIDRs.

        ***

        ## 5. Integrating With “Database Monitoring”

        If you’re using OCI Database Management / Monitoring:

        1. Use the above logic in a **reporting script** that:
           * Lists DB targets you monitor
           * Flags those with public exposure
        2. Optionally:
           * Trigger remediation functions (like the above) automatically.
           * Or create a “dry run” mode that just prints what would be changed.

        At a high level, your Python flow for monitoring + remediation:

        ```python theme={null}
        def find_public_dbs():
            # 1. list ADBs and DB Systems
            # 2. check ADB access control / public flags
            # 3. check VNIC public IP and security lists
            # 4. return objects that are public

        def remediate_public_dbs(dbs):
            # 1. For ADB: enable access control / move to private endpoint if needed
            # 2. For DB Systems: remove public IP where possible, tighten rules

        if __name__ == "__main__":
            public_targets = find_public_dbs()
            print("Public DB targets:", public_targets)
            # Uncomment to actually remediate:
            # remediate_public_dbs(public_targets)
        ```

        If you share exactly which OCI DB types you’re monitoring (ADB Shared/DED, VM DB Systems, Exadata, etc.), I can adjust the Python examples to those specific APIs and flags.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # Private subnet for the DB System: no public IPs allowed on VNICs
        resource "oci_core_subnet" "db_private_subnet" {
          compartment_id             = var.COMPARTMENT_OCID            # replace with your compartment OCID variable or value
          vcn_id                     = oci_core_vcn.DB_VCN_ID          # replace with your VCN resource or ID
          cidr_block                 = "10.0.1.0/24"                   # replace with your desired CIDR
          display_name               = "db-private-subnet"
          prohibit_public_ip_on_vnic = true                            # key setting: prevents public IP assignment
          route_table_id             = oci_core_route_table.PRIVATE_RT_ID  # ensure no default Internet route
          security_list_ids          = [oci_core_security_list.DB_SL_ID]   # lock down ingress as needed
        }

        # (Optional but recommended) NSG to further restrict DB access to only application tiers / monitoring VMs
        resource "oci_core_network_security_group" "db_nsg" {
          compartment_id = var.COMPARTMENT_OCID
          vcn_id         = oci_core_vcn.DB_VCN_ID
          display_name   = "db-nsg"
        }

        resource "oci_core_network_security_group_security_rule" "db_ingress_from_app" {
          network_security_group_id = oci_core_network_security_group.db_nsg.id
          direction                 = "INGRESS"
          protocol                  = "6" # TCP

          source      = oci_core_network_security_group.APP_NSG_ID     # replace with your app/monitoring NSG ID
          source_type = "NETWORK_SECURITY_GROUP"

          tcp_options {
            destination_port_range {
              min = 1521
              max = 1521
            }
          }
        }

        # DB System in the private subnet (no public access)
        resource "oci_database_db_system" "db_system" {
          compartment_id = var.COMPARTMENT_OCID
          availability_domain = var.AVAILABILITY_DOMAIN
          shape               = "VM.Standard2.1"         # replace with your desired shape
          subnet_id           = oci_core_subnet.db_private_subnet.id  # move DB to private subnet
          nsg_ids             = [oci_core_network_security_group.db_nsg.id]

          display_name        = "db-system-private"
          hostname            = "dbhost"
          database_edition    = "ENTERPRISE_EDITION"     # adjust as needed
          domain              = "DB_SUBNET_DNS_LABEL"    # replace with your DNS label
          cpu_core_count      = 2
          node_count          = 1
          data_storage_size_in_gb = 256

          db_home {
            display_name = "dbhome1"

            database {
              admin_password = var.DB_ADMIN_PASSWORD    # store in a secure variable
              db_name        = "DBNAME"                 # replace with your DB name
              character_set  = "AL32UTF8"
              ncharacter_set = "AL16UTF16"
              db_workload    = "OLTP"
            }
          }

          # other required arguments omitted for brevity
        }
        ```

        Changing an existing `oci_database_db_system` to use a different `subnet_id` (from a public to a private subnet) forces full replacement of the DB System, which means recreation of the database and associated downtime; plan and migration/backups must be handled carefully.

        To verify, `terraform plan` should show:

        * Creation of the new `oci_core_subnet.db_private_subnet` (and NSG resources if added).
        * An update to `oci_database_db_system.db_system` changing `subnet_id` (and `nsg_ids` if used), with a `-/+` replacement indicator on the DB System resource.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
