> ## 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 Network Subnets Should Be Private

### More Info:

Subnets should be configured as private (no public IP assignment). Public subnets expose resources directly to the internet, bypassing network security controls.

### Risk Level

Medium

### Address

Compliance, Security

### Compliance Standards

* APRA CPS 234 (Australia)
* 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)
* HIPAA
* ISO/IEC 27017
* ISO/IEC 27018
* ISO/IEC 27701
* KSA PDPL
* MAS Technology Risk Management (Singapore)
* MITRE ATT\&CK (Cloud)
* NIS2 Directive
* NIST
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* 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 OCI network subnets private (per CIS-style “subnets should be private”) using the OCI Console, you need to:

        1. **Identify subnets that are effectively public**

           1. Sign in to the OCI Console.
           2. In the left menu, go to **Networking > Virtual Cloud Networks**.
           3. Select the **Compartment** you want.
           4. Click your **VCN**.
           5. Go to the **Subnets** tab.
           6. For each subnet, note:
              * **Route table** (does it have a route to an **Internet Gateway**?)
              * **Public IP assignment** (auto-assign allowed?)

           A subnet is effectively public if:

           * Its **route table** has a route to an **Internet Gateway (IGW)**, and/or
           * It allows **public IPs** on resources and those resources have them.

        2. **Make the subnet stop assigning public IPs by default**\
           For each public subnet you want to make private:

           1. In the **Subnets** tab, click the **subnet name**.
           2. Click **Edit**.
           3. Under **Subnet Access** / **IPv4 Public IP Address Assignment**:
              * Set **“Allow public IPs”** to **No** (or uncheck “Assign a public IPv4 address” if present).
           4. Click **Save changes**.

           This prevents *new* VNICs in this subnet from getting public IPs.

        3. **Remove internet access from the subnet’s route table**

           1. From the subnet details page, note the **Route Table** name and click it, or\
              go via **VCN > Route Tables** and open the relevant table.
           2. Check for any route rule like:
              * **Target Type:** Internet Gateway
              * **Destination:** `0.0.0.0/0` or any public CIDR
           3. For each such rule:
              * Click the **Actions (⋯)** menu > **Delete**.
              * Confirm deletion.

           If workloads in that subnet still need outbound Internet, instead:

           * Create / use a **NAT Gateway**, then
           * Add a route with:
             * **Target Type:** NAT Gateway
             * **Destination CIDR Block:** `0.0.0.0/0`

        4. **Remove existing public IPs from resources in that subnet**\
           For each compute instance in that subnet:

           1. Go to **Compute > Instances**.
           2. Filter by **Subnet** or check the **Primary VNIC** details.
           3. Click the **instance**, then in the **Resources** section click **Attached VNICs**.
           4. Click the **primary VNIC**.
           5. Under **Public IP**, if a public IP is assigned:
              * Click **Edit**, then **Unassign** or **None** (you may need to first create a reserved public IP if you must keep it and then move to a bastion later).
              * Save changes.

           Do this for any **load balancers**, **NLBs**, or other services with public IPs in that subnet (change to **private** IP only or move to a different subnet).

        5. **(If needed) Move resources into a new dedicated private subnet**\
           If you cannot change an existing subnet as desired (for example, it is heavily used for public-facing workloads), create a new private subnet and move internal workloads there:
           1. In the **VCN**, go to **Subnets > Create Subnet**.
           2. Choose:
              * **Private subnet** (no IGW route, no default public IPs).
              * Attach it to a route table with **NAT Gateway only** (if outbound Internet is needed) or no external route at all.
           3. Create new instances or re-create services in this private subnet.

        6. **Verify the subnet is now private**\
           For the subnet you remediated:
           * **Route Table:** No routes to an **Internet Gateway**.
           * **Subnet settings:** Public IP auto-assignment disabled.
           * **Resources:** No public IPs on VNICs in that subnet.

        Once these conditions are met, OCI networking monitoring / security tooling will evaluate that subnet as private.
      </Accordion>

      <Accordion title="Using CLI">
        In OCI, a “private” subnet is one where `prohibitPublicIpOnVnic = true`.\
        You can remediate this via OCI CLI by updating each public subnet so it no longer allows public IPs.

        Below are step‑by‑step CLI instructions.

        ***

        ### 0. Prerequisites

        * OCI CLI installed and configured (`oci setup config`)
        * Your user has permissions to manage subnets and route tables in the target compartment/VCN.

        ***

        ### 1. Identify public subnets

        List subnets in a compartment and filter those that allow public IPs on VNICs:

        ```bash theme={null}
        COMPARTMENT_OCID="<your_compartment_ocid>"

        oci network subnet list \
          --compartment-id "$COMPARTMENT_OCID" \
          --all \
          --output json \
        | jq -r '.data[] | select(.["prohibit-public-ip-on-vnic"] == false) | "\(.id) \(.display-name)"'
        ```

        Result: list of subnet OCIDs and names that are effectively “public”.

        ***

        ### 2. (Optional) Confirm subnet details

        For a specific subnet:

        ```bash theme={null}
        SUBNET_OCID="<subnet_ocid>"

        oci network subnet get --subnet-id "$SUBNET_OCID" --output json
        ```

        Check:

        * `"prohibit-public-ip-on-vnic": false` → public
        * Associated route table may have default route (0.0.0.0/0) to an Internet Gateway.

        ***

        ### 3. Make the subnet private (disallow public IPs)

        Update the subnet:

        ```bash theme={null}
        SUBNET_OCID="<subnet_ocid>"

        oci network subnet update \
          --subnet-id "$SUBNET_OCID" \
          --prohibit-public-ip-on-vnic true \
          --force
        ```

        This prevents **new** VNICs in the subnet from getting public IPs.\
        Existing VNICs with public IPs keep them; see next step.

        ***

        ### 4. Remove existing public IPs from VNICs (if required)

        1. List VNICs in the subnet:

           ```bash theme={null}
           VCN_OCID="<vcn_ocid>"

           oci network vnic list \
             --compartment-id "$COMPARTMENT_OCID" \
             --vcn-id "$VCN_OCID" \
             --all \
             --output json \
           | jq -r '.data[] | select(.subnet-id == "'$SUBNET_OCID'") | "\(.id) \(.public-ip)"'
           ```

        2. For each VNIC that still has a public IP, find and delete the public IP resource (reserved or ephemeral):

           * List public IPs:

             ```bash theme={null}
             oci network public-ip list \
               --compartment-id "$COMPARTMENT_OCID" \
               --scope REGION \
               --all \
               --output json
             ```

           * Once you identify the public IP OCID:

             ```bash theme={null}
             PUBLIC_IP_OCID="<public_ip_ocid>"

             oci network public-ip delete \
               --public-ip-id "$PUBLIC_IP_OCID" \
               --force
             ```

        ***

        ### 5. (Recommended) Remove Internet access from route table

        A subnet is only fully private if it also does **not** route to an Internet Gateway.

        1. Get the subnet’s route table:

           ```bash theme={null}
           SUBNET_JSON=$(oci network subnet get --subnet-id "$SUBNET_OCID" --output json)
           ROUTE_TABLE_OCID=$(echo "$SUBNET_JSON" | jq -r '.data["route-table-id"]')
           ```

        2. Inspect the route rules:

           ```bash theme={null}
           oci network route-table get \
             --rt-id "$ROUTE_TABLE_OCID" \
             --output json \
           | jq '.data."route-rules"'
           ```

        3. Remove any rule with destination `0.0.0.0/0` that points to an Internet Gateway:

           * Build a new route-rules JSON **without** those entries, e.g.:

             ```bash theme={null}
             oci network route-table get \
               --rt-id "$ROUTE_TABLE_OCID" \
               --output json \
             | jq '.data."route-rules" 
                   | map(select(.destination != "0.0.0.0/0"))' \
             > new-route-rules.json
             ```

           * Update the route table:

             ```bash theme={null}
             oci network route-table update \
               --rt-id "$ROUTE_TABLE_OCID" \
               --route-rules file://new-route-rules.json \
               --force
             ```

        ***

        ### 6. Apply to all public subnets (script example)

        ```bash theme={null}
        COMPARTMENT_OCID="<your_compartment_ocid>"

        oci network subnet list \
          --compartment-id "$COMPARTMENT_OCID" \
          --all \
          --output json \
        | jq -r '.data[] | select(.["prohibit-public-ip-on-vnic"] == false) | .id' \
        | while read SUBNET_OCID; do
          echo "Making subnet private: $SUBNET_OCID"
          oci network subnet update \
            --subnet-id "$SUBNET_OCID" \
            --prohibit-public-ip-on-vnic true \
            --force
        done
        ```

        ***

        If you share your compartment/VCN IDs, I can tailor the exact CLI commands for your environment.
      </Accordion>

      <Accordion title="Using Python">
        To make OCI subnets private using Python, you need to:

        1. **Ensure VNICs in the subnet cannot get public IPs**
        2. **Ensure the subnet’s route table does not route to an Internet Gateway**

        Below is a minimal step‑by‑step remediation approach using the OCI Python SDK.

        ***

        ## 1. Prerequisites

        Install and configure the OCI Python SDK:

        ```bash theme={null}
        pip install oci
        oci setup config
        ```

        This creates `~/.oci/config` with a profile (e.g., `DEFAULT`).

        ***

        ## 2. Logic to Make a Subnet “Private”

        A subnet is “private” if:

        * `prohibit_public_ip_on_vnic = True`
        * Its route table has **no route rules** where:
          * `network_entity_id` is an Internet Gateway OCID

        We’ll:

        1. List target subnets (by compartment or VCN).
        2. For each subnet:
           * Enable `prohibit_public_ip_on_vnic`.
           * Clean its route table of Internet Gateway routes.

        ***

        ## 3. Python Script Example

        ```python theme={null}
        import oci

        # -----------------------
        # CONFIG
        # -----------------------
        PROFILE = "DEFAULT"
        COMPARTMENT_ID = "<your_compartment_ocid>"  # e.g., ocid1.compartment.oc1..xxxx
        # Optionally filter by VCN:
        VCN_ID_FILTER = None  # or "ocid1.vcn.oc1..xxxx"

        # -----------------------
        # CLIENTS
        # -----------------------
        config = oci.config.from_file("~/.oci/config", PROFILE)
        network_client = oci.core.VirtualNetworkClient(config)

        # -----------------------
        # HELPERS
        # -----------------------
        def is_internet_gateway(network_entity_id: str) -> bool:
            """Heuristic: IG OCIDs contain ':internetgateway:'."""
            return network_entity_id and ":internetgateway:" in network_entity_id

        def make_subnet_private(subnet, network_client):
            updated = False

            # 1) Ensure subnet prohibits public IPs
            if not subnet.prohibit_public_ip_on_vnic:
                update_details = oci.core.models.UpdateSubnetDetails(
                    prohibit_public_ip_on_vnic=True
                )
                network_client.update_subnet(subnet.id, update_details)
                print(f"Set prohibit_public_ip_on_vnic=True on subnet {subnet.display_name} ({subnet.id})")
                updated = True

            # 2) Clean route table from Internet Gateway routes
            if subnet.route_table_id:
                rt = network_client.get_route_table(subnet.route_table_id).data
                new_route_rules = [
                    r for r in rt.route_rules
                    if not is_internet_gateway(r.network_entity_id)
                ]

                if len(new_route_rules) != len(rt.route_rules):
                    update_rt_details = oci.core.models.UpdateRouteTableDetails(
                        route_rules=new_route_rules
                    )
                    network_client.update_route_table(rt.id, update_rt_details)
                    print(f"Removed Internet Gateway routes from route table {rt.display_name} ({rt.id}) "
                          f"for subnet {subnet.display_name} ({subnet.id})")
                    updated = True

            if not updated:
                print(f"Subnet {subnet.display_name} ({subnet.id}) is already private.")

        # -----------------------
        # MAIN
        # -----------------------
        def main():
            # List subnets in compartment (optionally filtered by VCN)
            list_kwargs = {}
            if VCN_ID_FILTER:
                list_kwargs["vcn_id"] = VCN_ID_FILTER

            subnets = oci.pagination.list_call_get_all_results(
                network_client.list_subnets,
                compartment_id=COMPARTMENT_ID,
                **list_kwargs
            ).data

            for subnet in subnets:
                print(f"Evaluating subnet {subnet.display_name} ({subnet.id})")
                make_subnet_private(subnet, network_client)

        if __name__ == "__main__":
            main()
        ```

        ***

        ## 4. How to Use This for “Networking Monitoring”

        * Run this script on a schedule (e.g., via cron, OCI Functions, or OCI DevOps) as a **remediation job**.
        * Optionally modify it to:
          * Only **log** non‑compliant subnets instead of updating them.
          * Push findings to your monitoring/alerting system (e.g., emit metrics, write to Object Storage, send to OCI Logging or an external SIEM).

        If you tell me how you currently trigger monitoring (OCI Events, Functions, external CI/CD, etc.), I can adjust this into a fully automated remediation workflow.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_core_subnet" "PRIVATE_SUBNET" {
          # Replace with your compartment, VCN, and CIDR details
          compartment_id  = var.COMPARTMENT_OCID        # e.g., "ocid1.compartment.oc1..xxxx"
          vcn_id          = oci_core_vcn.MY_VCN.id
          cidr_block      = "10.0.1.0/24"
          display_name    = "private-subnet"
          dns_label       = "privsubnet"

          # This setting makes the subnet private by preventing public IP assignment
          prohibit_public_ip_on_vnic = true

          # Optional: other typical subnet settings
          route_table_id      = oci_core_route_table.MY_RT.id
          security_list_ids   = [oci_core_security_list.MY_SL.id]
          dhcp_options_id     = oci_core_dhcp_options.MY_DHCP.id
        }
        ```

        This change does not force replacement of an existing subnet; OCI allows updating `prohibit_public_ip_on_vnic` in place.

        After updating, `terraform plan` should show an in-place `~ update in-place` on the `oci_core_subnet` resource with `prohibit_public_ip_on_vnic` changing from `false` (or `null`) to `true`.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
