> ## 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 Compute Instances Should Not Have Public IP Attached

### More Info:

Compute instances should not have public IP addresses attached. Public IPs create direct attack vectors and should be replaced with private connectivity through bastion hosts or load balancers.

### Risk Level

Critical

### 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">
        Below are step‑by‑step console instructions to fix this in OCI by **removing public IPs from compute instances** and then **setting up monitoring** so it’s detected going forward.

        ***

        ## 1. Identify compute instances with public IPs

        1. Sign in to the OCI Console.
        2. In the top-left hamburger menu, go to:\
           **Networking → Virtual Cloud Networks**.
        3. Select the **Compartment** you care about.
        4. For each VCN, click its name, then in the left menu click **Subnets**.
        5. For each subnet:
           * Click the subnet name.
           * In the subnet details, click **Resources → IP Addresses** or **Resources → Instances** (depending on your console version).
           * Open each **VNIC** or **Instance** in a new tab and check:
             * If the VNIC has a **Public IP** field populated → that compute instance has a public IP attached.

        Alternative quick check:

        1. Menu → **Compute → Instances**.
        2. Filter by **Compartment**.
        3. In the instances list, look at the **Public IP Address** column. Any non-empty value indicates the issue.

        ***

        ## 2. Remove public IPs from existing compute instances

        > Note: Instance will lose direct internet reachability. Ensure you have VPN/DRG/FASTCONNECT or a Bastion / NAT / Load Balancer path before doing this.

        For each affected instance:

        1. Menu → **Compute → Instances**.
        2. Select Compartment and click the instance name.
        3. In the instance details, under **Resources**, click **Attached VNICs**.
        4. Click the **primary VNIC** (or relevant VNIC) name.
        5. In the VNIC details:
           * Under **Public IP**, click **Actions → Unassign** (or **Edit VNIC** then unassign public IP, depending on UI).
           * If it’s:
             * **Ephemeral public IP**: choose **Unassign** (it will be released).
             * **Reserved public IP**: choose **Unassign** but keep the IP resource if needed (it just detaches from the VNIC).
        6. Click **Save changes** / **Unassign**.

        Repeat for each instance that should not have a public IP.

        If the VNIC is in a **public subnet** but you want a fully private setup:

        1. Create a **private subnet** (no public IPs):
           * Menu → **Networking → Virtual Cloud Networks**.
           * Click your VCN → **Subnets → Create Subnet**.
           * Set:
             * **Subnet Type**: Regional
             * **Public Subnet**: **No**
           * Attach the right route table (with NAT / DRG as needed).
        2. Create a **new instance** in this private subnet and migrate workloads,\
           or re-attach storage/volumes as needed (you can’t directly move an existing instance to another subnet).

        ***

        ## 3. Provide alternative internet access (optional but typical)

        If instances still need outbound internet but no public IP:

        1. Create a **NAT Gateway**:
           * Menu → **Networking → Virtual Cloud Networks**.
           * Select the VCN → **NAT Gateways → Create NAT Gateway**.
        2. Update **Route Table** used by the **private subnet**:
           * In the same VCN → **Route Tables** → select the one for the private subnet.
           * Add route rule:
             * **Destination CIDR**: `0.0.0.0/0`
             * **Target Type**: NAT Gateway
             * **Target**: your NAT gateway.
        3. Ensure **Security Lists / Network Security Groups** allow outbound traffic (e.g. `0.0.0.0/0` TCP 80/443 as needed).

        ***

        ## 4. Monitor and alert on instances with public IPs (Network Monitoring / Cloud Guard)

        ### A. Use Cloud Guard detector recipe

        1. Menu → **Security → Cloud Guard**.
        2. Make sure Cloud Guard is **Enabled** at the tenancy or target level.
        3. In Cloud Guard, go to **Detector Recipes**.
        4. Look for a detector similar to:\
           **“Compute instances should not have public IPs attached”**\
           (name may vary by tenancy/region; usually under **Configuration → Network / Compute**).
        5. Click **Create Custom Detector Recipe** (or clone existing one) if needed.
        6. In the recipe:
           * Locate the rule for compute instances with public IP.
           * Set **Condition** to identify:
             * `instance.vnic.publicIp != null` (already preconfigured).
           * Set **Detector Rule Status**: **Enabled**.
        7. Attach this detector recipe to a **Target**:
           * Go to **Targets** → create or edit a target.
           * Attach the custom detector recipe to the compartments/VCNs you want monitored.

        ### B. Configure automatic or guided remediation (Responder)

        1. In Cloud Guard, go to **Responder Recipes**.
        2. Look for a responder that:
           * **Unassigns public IP** from instance VNIC, or
           * **Moves instance to private network** (if available).
        3. Enable the appropriate responder and set:
           * **Mode**:
             * **Remediation** for auto-fix, or
             * **Detect** + manual review.
        4. Attach the responder recipe to the same **Target** as above.

        Now Cloud Guard will create problems when a compute instance has a public IP, and responders can unassign those IPs or guide you to do so.

        ***

        ## 5. Prevent future public IP assignment

        1. When creating subnets:
           * Use **Private Subnets** (no public IPs allowed).
        2. When creating instances:
           * In the **Networking** step of instance creation, ensure:
             * **Assign a public IP address** is **unchecked**.
        3. Optionally enforce via **Security Zones**:
           * Menu → **Security → Security Zones**.
           * Create a zone with a recipe that disallows public IPs on compute instances.
           * Put critical compartments into this security zone to prevent violations at creation time.

        That’s the complete console-based remediation and monitoring path for “OCI Network Compute Instances Should Not Have Public IP Attached.”
      </Accordion>

      <Accordion title="Using CLI">
        Below are CLI-focused steps to **detect** and then **remediate** compute instances with public IPs in OCI, in a way that satisfies a “no public IP on compute instances” networking/monitoring control.

        ***

        ## 1. Prerequisites

        Make sure:

        ```bash theme={null}
        oci --version
        oci setup config
        ```

        Your profile should have permissions on:

        * `compute:instance`, `compute:vnicAttachment`
        * `network:vnic`, `network:subnet`
        * (Optionally) `network:natGateway` or `network:serviceGateway` if you need private egress.

        Assume `--profile <PROFILE>` and `--compartment-id <COMP_OCID>` in the examples as needed.

        ***

        ## 2. Identify Instances with Public IPs

        ### 2.1 Get all VNIC attachments in a compartment

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

        oci compute vnic-attachment list \
          --compartment-id "$COMP_OCID" \
          --all \
          --query 'data[].{instanceId:"instance-id", vnicId:"vnic-id"}' \
          --output json > vnic_attachments.json
        ```

        ### 2.2 For each VNIC, check for public IP

        ```bash theme={null}
        jq -c '.[]' vnic_attachments.json | while read -r row; do
          INSTANCE_ID=$(echo "$row" | jq -r '.instanceId')
          VNIC_ID=$(echo "$row" | jq -r '.vnicId')

          VNIC_JSON=$(oci network vnic get --vnic-id "$VNIC_ID" --output json)
          PUBLIC_IP=$(echo "$VNIC_JSON" | jq -r '.data["public-ip"]')

          if [ "$PUBLIC_IP" != "null" ]; then
            echo "Instance $INSTANCE_ID has public IP $PUBLIC_IP on VNIC $VNIC_ID"
          fi
        done
        ```

        This gives you the list of **non-compliant instances**.

        ***

        ## 3. Prevent Future Public IPs (Subnet-Level Remediation)

        For each subnet that should not allow public IPs, set `prohibit-public-ip-on-vnic` to `true`.

        1. List subnets:

        ```bash theme={null}
        oci network subnet list \
          --compartment-id "$COMP_OCID" \
          --all \
          --query 'data[].{name:"display-name", id:id, prohibitPublicIpOnVnic:"prohibit-public-ip-on-vnic"}' \
          --output table
        ```

        2. For each target subnet:

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

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

        This **prevents new VNICs in that subnet from getting a public IP**.

        ***

        ## 4. Remediate Existing Instances with Public IPs

        Directly “removing” a public IP from the **primary VNIC** of an instance is limited; the clean pattern is:

        * Recreate the instance without a public IP, or
        * Add a new private-only VNIC and move workloads, or
        * Rebuild using an instance configuration without public IP.

        Below is a practical CLI approach via **instance configuration**.

        ### 4.1 Capture configuration of a non-compliant instance

        Pick an instance from the list above:

        ```bash theme={null}
        INSTANCE_ID="<instance_ocid>"

        oci compute instance get \
          --instance-id "$INSTANCE_ID" \
          --output json > instance_details.json
        ```

        Generate an instance configuration, **stripping public IP** in the launch details.

        1. Create instance configuration from instance:

        ```bash theme={null}
        oci compute instance-configuration create-from-instance \
          --instance-id "$INSTANCE_ID" \
          --display-name "no-public-ip-config-$INSTANCE_ID" \
          --output json > ic_create.json
        ```

        2. Fetch the instance configuration details:

        ```bash theme={null}
        IC_ID=$(jq -r '.data.id' ic_create.json)

        oci compute instance-configuration get \
          --instance-configuration-id "$IC_ID" \
          --output json > ic_details.json
        ```

        3. Edit `ic_details.json`: in `data.instance-details.launch-details.create-vnic-details` set:

        ```json theme={null}
        "assign-public-ip": false
        ```

        Save the modified launch details section to a separate file:

        ```bash theme={null}
        jq '.data."instance-details"."launch-details"' ic_details.json > launch_details_no_public_ip.json
        ```

        Then manually ensure in `launch_details_no_public_ip.json` that:

        ```json theme={null}
        "create-vnic-details": {
          ...
          "assign-public-ip": false
        }
        ```

        ### 4.2 Launch a new instance without public IP

        ```bash theme={null}
        oci compute instance launch \
          --from-json file://launch_details_no_public_ip.json \
          --output json > new_instance.json
        ```

        Note the new instance ID:

        ```bash theme={null}
        NEW_INSTANCE_ID=$(jq -r '.data.id' new_instance.json)
        echo "New instance without public IP: $NEW_INSTANCE_ID"
        ```

        Migrate applications/data (block volumes, config, etc.) as per your process, then:

        ```bash theme={null}
        oci compute instance terminate \
          --instance-id "$INSTANCE_ID" \
          --preserve-boot-volume false \
          --force
        ```

        Repeat for each non-compliant instance.

        ***

        ## 5. (Optional) Add Private Egress (NAT / Service Gateway)

        To keep instances private while letting them reach the internet or OCI services, use NAT Gateway / Service Gateway:

        ### 5.1 Create NAT Gateway (if not existing)

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

        oci network nat-gateway create \
          --compartment-id "$COMP_OCID" \
          --vcn-id "$VCN_ID" \
          --display-name "nat-gw-$VCN_ID" \
          --block-traffic false \
          --output json > nat_gw.json

        NAT_GW_ID=$(jq -r '.data.id' nat_gw.json)
        ```

        ### 5.2 Update route table for private subnets

        ```bash theme={null}
        ROUTE_TABLE_ID="<route_table_ocid>"

        oci network route-table update \
          --rt-id "$ROUTE_TABLE_ID" \
          --route-rules '[
            {
              "cidrBlock": "0.0.0.0/0",
              "networkEntityId": "'"$NAT_GW_ID"'"
            }
          ]' \
          --force
        ```

        ***

        ## 6. Use CLI for Continuous Monitoring

        You can periodically run a script like:

        ```bash theme={null}
        oci compute vnic-attachment list \
          --compartment-id "$COMP_OCID" \
          --all \
          --query 'data[]."vnic-id"' \
          --output json | jq -r '.[]' | while read -r VNIC_ID; do
            VNIC_JSON=$(oci network vnic get --vnic-id "$VNIC_ID" --output json)
            INSTANCE_ID=$(echo "$VNIC_JSON" | jq -r '.data."instance-id"')
            PUBLIC_IP=$(echo "$VNIC_JSON" | jq -r '.data["public-ip"]')
            if [ "$PUBLIC_IP" != "null" ]; then
              echo "NON-COMPLIANT: Instance $INSTANCE_ID, VNIC $VNIC_ID, Public IP $PUBLIC_IP"
            fi
        done
        ```

        You can wire this into your monitoring to alert on any new public IP assignments.

        ***

        If you tell me whether you prefer “rebuild instances” vs. “add secondary private-only VNICs and reconfigure services,” I can adapt the remediation steps to that exact pattern.
      </Accordion>

      <Accordion title="Using Python">
        Below is a concise, practical way to **detect** and **remediate** OCI compute instances that have a public IP attached, using Python and the OCI SDK.

        ***

        ## 1. High-level remediation flow

        1. List all compute instances in a compartment.
        2. For each instance, list its VNIC attachments.
        3. For each VNIC, check if it has a public IP.
        4. If a public IP exists, unassign it:
           * For ephemeral public IPs → “detach” by clearing its `privateIpId`.
           * For reserved public IPs → either:
             * Unassign (clear `privateIpId`) but keep the public IP object, or
             * Delete the public IP entirely (if your policy is “no public IPs at all”).

        > Note: You cannot “toggle” a flag on the instance; you must update or delete the public IP resource associated with the private IP.

        ***

        ## 2. Prerequisites

        1. Install OCI SDK:

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

        2. Configure `~/.oci/config`:

        ```ini theme={null}
        [DEFAULT]
        user=ocid1.user.oc1..aaaa...
        fingerprint=...
        key_file=/path/to/oci_api_key.pem
        tenancy=ocid1.tenancy.oc1..aaaa...
        region=us-ashburn-1
        ```

        3. Ensure the user has IAM permissions, for example (in policy language):

        ```text theme={null}
        Allow group net-admins to manage public-ips in tenancy
        Allow group net-admins to read instances in tenancy
        Allow group net-admins to read vnics in tenancy
        ```

        ***

        ## 3. Python script – detect and remediate public IPs

        This script:

        * Scans a target compartment (or tenancy root if you wish).
        * Logs instances/VNICs with public IPs.
        * Unassigns/deletes public IPs based on configuration flags.

        ```python theme={null}
        import oci

        # -------------------
        # CONFIGURATION
        # -------------------
        COMPARTMENT_ID = "ocid1.compartment.oc1..xxxx"   # target compartment
        PROFILE = "DEFAULT"                              # profile name in ~/.oci/config
        DRY_RUN = True                                   # True = only report, False = remediate
        DELETE_PUBLIC_IP = False                         # True = delete public IP objects, False = unassign only

        # -------------------
        # CLIENT SETUP
        # -------------------
        config = oci.config.from_file("~/.oci/config", PROFILE)
        compute_client = oci.core.ComputeClient(config)
        vn_client = oci.core.VirtualNetworkClient(config)

        # -------------------
        # HELPER FUNCTIONS
        # -------------------
        def list_all_instances(compartment_id):
            return oci.pagination.list_call_get_all_results(
                compute_client.list_instances,
                compartment_id=compartment_id,
                lifecycle_state="RUNNING"  # or remove filter if you want all
            ).data

        def list_vnic_attachments(compartment_id, instance_id):
            return oci.pagination.list_call_get_all_results(
                compute_client.list_vnic_attachments,
                compartment_id=compartment_id,
                instance_id=instance_id
            ).data

        def get_public_ip_for_vnic(vnic):
            """
            Returns public IP object if exists, else None.
            We resolve by the private IP address on the VNIC.
            """
            if not vnic.public_ip or not vnic.private_ip:
                # vnic.public_ip is often populated, but not guaranteed.
                # If no public_ip field, we still try via private_ip.
                pass

            try:
                response = vn_client.get_public_ip_by_ip_address(
                    oci.core.models.GetPublicIpByIpAddressDetails(
                        ip_address=vnic.public_ip or vnic.private_ip
                    )
                )
                return response.data
            except oci.exceptions.ServiceError as e:
                if e.status == 404:
                    return None
                raise

        def remediate_public_ip(public_ip):
            if DRY_RUN:
                print(f"[DRY-RUN] Would remediate public IP: {public_ip.ip_address} "
                      f"(OCID: {public_ip.id}, lifetime={public_ip.lifetime})")
                return

            if DELETE_PUBLIC_IP:
                # Delete the public IP object (reserved or ephemeral)
                print(f"Deleting public IP: {public_ip.ip_address} (OCID: {public_ip.id})")
                vn_client.delete_public_ip(public_ip.id)
            else:
                # Unassign from private IP (keep the object if it is reserved)
                print(f"Unassigning public IP {public_ip.ip_address} from private IP "
                      f"(public_ip_id={public_ip.id})")
                details = oci.core.models.UpdatePublicIpDetails(
                    private_ip_id=None
                )
                vn_client.update_public_ip(public_ip.id, details)

        # -------------------
        # MAIN LOGIC
        # -------------------
        def main():
            instances = list_all_instances(COMPARTMENT_ID)
            print(f"Found {len(instances)} running instances in compartment {COMPARTMENT_ID}")

            for inst in instances:
                print(f"\nInspecting instance: {inst.display_name} ({inst.id})")

                vnic_attachments = list_vnic_attachments(COMPARTMENT_ID, inst.id)
                if not vnic_attachments:
                    print("  No VNIC attachments.")
                    continue

                for va in vnic_attachments:
                    vnic = vn_client.get_vnic(va.vnic_id).data
                    print(f"  VNIC: {vnic.display_name or vnic.id} "
                          f"| private_ip={vnic.private_ip} | public_ip={vnic.public_ip}")

                    # Detect public IP
                    public_ip = get_public_ip_for_vnic(vnic)
                    if public_ip:
                        print(f"    -> Public IP detected: {public_ip.ip_address} "
                              f"(lifetime={public_ip.lifetime}, scope={public_ip.scope})")
                        remediate_public_ip(public_ip)
                    else:
                        print("    -> No public IP attached.")

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

        ***

        ## 4. Integrating with “OCI Networking Monitoring”

        For use in a monitoring/auto-remediation pipeline:

        1. **Detection**
           * Use OCI Events or Cloud Guard to raise an event when a public IP is assigned to a compute instance (or private IP) in your target compartments.

        2. **Invocation**
           * Create a **Functions** function or **OCI DevOps/CI pipeline** that:
             * Runs a Python handler similar to the above script.
             * Takes the compartment / instance ID from the event payload.
             * Executes with `DRY_RUN = False` in a controlled environment.

        3. **Least Privilege**
           * Create a dedicated dynamic group for the function instance principals.
           * Attach IAM policies allowing only:
             * `read instances`
             * `manage public-ips`
             * (optionally) `read vnics`\
               for the target compartments.

        This gives you continuous, Python-based remediation for “compute instances must not have public IPs” across OCI.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_core_instance" "APP_INSTANCE" {
          # Replace with your values
          availability_domain = "AVAILABILITY_DOMAIN_NAME"
          compartment_id      = "COMPARTMENT_OCID"
          shape               = "VM.SHAPE"
          display_name        = "APP_INSTANCE_NAME"

          source_details {
            source_type = "image"
            source_id   = "IMAGE_OCID"
          }

          create_vnic_details {
            subnet_id        = "PRIVATE_SUBNET_OCID" # Use a private subnet without internet gateway
            hostname_label   = "APP_HOSTNAME"
            assign_public_ip = false                 # KEY FIX: no public IP on primary VNIC
          }

          metadata = {
            ssh_authorized_keys = file("PATH_TO_SSH_PUBLIC_KEY")
          }
        }
        ```

        Changing `assign_public_ip` from `true` (or omitting it on a public subnet) to `false` on an existing `oci_core_instance` usually forces replacement of the instance and its primary VNIC, causing downtime; plan this as an outage.

        For verification, `terraform plan` should show a change on the instance where `assign_public_ip` is updated to `false` (or removed from state) and no public IP is associated with the instance’s VNIC in the planned configuration.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
