> ## 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 OKE Kubernetes API Public Endpoint Should Be Disabled

### More Info:

Public Kubernetes API endpoints are continuously scanned and brute forced. Restrict the API server to private endpoints reachable only via VCN, bastions or Service Gateway to dramatically reduce attack surface.

### Risk Level

Critical

### Address

Compliance, Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are the steps to disable the public Kubernetes API endpoint for an OCI OKE cluster using the OCI Console, and move it to a private endpoint only.

        > Note: Some older / “Basic” clusters or early-generation clusters may not support changing endpoint type in-place. In that case, you must recreate the cluster with a private-only endpoint. I’ll include both paths.

        ***

        ## 1. Prerequisites

        Before disabling the public endpoint, make sure:

        1. You have **VCN connectivity** from your admin environment (bastion host / VPN / FastConnect / OCI Cloud Shell) to the private subnet where the API will reside.
        2. You have or will create a **private subnet** in your VCN for the Kubernetes API endpoint.
        3. You have required IAM permissions to **manage OKE clusters and networking**.

        ***

        ## 2. Check if Your Cluster Allows Editing the Endpoint

        1. In the OCI Console, go to:
           * **Main menu → Developer Services → Kubernetes Clusters (OKE)**.
        2. Select your **Compartment**.
        3. Click your **Cluster name**.
        4. On the **Cluster details** page:
           * Look for a button like **Edit Kubernetes API endpoint**, **Update cluster**, or similar under **More actions**.
           * If you see an option to change the **Kubernetes API endpoint access**, you can modify it in place.
           * If not, you will need to create a new cluster (see Section 4).

        Proceed with Section 3 if endpoint edit is available.

        ***

        ## 3. Change an Existing Cluster to Private Endpoint Only

        ### 3.1 Prepare a Private Subnet (if not existing)

        1. Go to **Networking → Virtual Cloud Networks**.
        2. Select the VCN used by your OKE cluster.
        3. Under **Subnets**, create a **new private subnet** (if you don’t already have one suitable):
           * **Subnet type**: Regional
           * **Private subnet**: Enabled (no public IPs)
           * Add appropriate **route table** and **security lists/NSGs** so your admins can reach it.

        ### 3.2 Edit the Cluster Endpoint

        1. Go to **Kubernetes Clusters (OKE)**.
        2. Select the **Compartment** and click your **Cluster**.
        3. On the **Cluster details** page, click:
           * **More actions → Edit cluster** or
           * **Edit Kubernetes API endpoint** (wording can differ slightly by region / console version).
        4. In the **Kubernetes API endpoint access** section:
           * Change to **Private endpoint only** (or uncheck “Public endpoint” if both are enabled).
           * Select the **VCN** and **Private Subnet** for the API endpoint.
           * (Optional) Attach **Network Security Groups** restricting which IPs / subnets can reach the endpoint.
        5. Click **Save changes / Update**.

        The cluster control plane will update. This can take several minutes. The public endpoint will be removed once the update completes.

        ### 3.3 Update Your kubeconfig / Access Method

        Once the endpoint is private-only:

        1. From a host that has network access to the private subnet (bastion, over VPN, etc.), run:
           * In OCI Console → Cluster details → **Access Cluster** → copy and run the `oci ce cluster create-kubeconfig` command.
        2. Confirm:
           * `kubectl get nodes` works from a network path that reaches the private subnet.
           * Access from the internet without VPN/bastion should no longer be possible.

        ***

        ## 4. If You Cannot Edit the Endpoint (Recreate Cluster as Private-Only)

        If the cluster does not allow editing the endpoint:

        ### 4.1 Create a New OKE Cluster with Private-Only API Endpoint

        1. Go to **Kubernetes Clusters (OKE)** → **Create cluster**.
        2. Choose **Quick create** or **Custom create** (Custom recommended for control).
        3. In the **Kubernetes API endpoint access** section:
           * Select **Private endpoint** (or deselect public so only private is enabled).
           * Choose the **VCN** and appropriate **private subnet** for the API endpoint.
        4. Complete other cluster settings (version, node pools, shapes, etc.).
        5. Create the cluster and wait until its status is **Active**.

        ### 4.2 Migrate Workloads

        1. Export current workloads from the old cluster (e.g., `kubectl get all -A -o yaml` or your manifests/Helm charts).
        2. Configure kubeconfig for the new cluster (via **Access Cluster** in cluster details).
        3. Apply manifests / reinstall Helm charts on the new cluster.
        4. Test workloads, ingress, services, and networking.
        5. Decommission the old cluster:
           * Drain and delete node pools.
           * Delete the old OKE cluster.

        ***

        ## 5. Validate That the Public Endpoint Is Disabled

        1. On **Cluster details**, confirm:
           * API Endpoint shows only a **private IP / private FQDN**.
           * No public endpoint is listed.
        2. From the public internet (without VPN/bastion), verify:
           * `kubectl` cannot reach the cluster.
           * Any previous public FQDN or IP is no longer responsive.

        This fully remediates the issue: the Kubernetes API for OCI OKE is no longer exposed via a public endpoint and is accessible only over private network paths.
      </Accordion>

      <Accordion title="Using CLI">
        To disable the public Kubernetes API endpoint for an existing OKE cluster using OCI CLI, you update the cluster’s `endpoint-config` and set `isPublicIpEnabled` to `false`.

        **Prereqs**

        * OCI CLI installed and configured (`oci setup config`)
        * OCID of the OKE cluster
        * OCID of the subnet where the private endpoint will live (usually a private subnet in the same VCN)

        ***

        ### 1. Get the current cluster configuration (optional but recommended)

        ```bash theme={null}
        oci ce cluster get \
          --cluster-id <cluster_ocid> \
          --query "data.{id:id,name:name,endpointConfig:endpoint-config}" \
          --output table
        ```

        Note the current `endpoint-config` values (especially `subnetId` and any `nsgIds`).

        ***

        ### 2. Build the new `endpoint-config` JSON

        You want the same subnet and NSGs, but with `isPublicIpEnabled` set to `false`.

        Example (adjust OCIDs and NSGs as needed):

        ```bash theme={null}
        cat > endpoint-config.json << 'EOF'
        {
          "isPublicIpEnabled": false,
          "subnetId": "ocid1.subnet.oc1..exampleuniqueID",
          "nsgIds": [
            "ocid1.networksecuritygroup.oc1..exampleNSG1",
            "ocid1.networksecuritygroup.oc1..exampleNSG2"
          ]
        }
        EOF
        ```

        If you are not using NSGs, omit the `nsgIds` array:

        ```json theme={null}
        {
          "isPublicIpEnabled": false,
          "subnetId": "ocid1.subnet.oc1..exampleuniqueID"
        }
        ```

        ***

        ### 3. Update the cluster to disable the public endpoint

        ```bash theme={null}
        oci ce cluster update \
          --cluster-id <cluster_ocid> \
          --endpoint-config file://endpoint-config.json \
          --force \
          --wait-for-state ACTIVE
        ```

        This updates the control plane endpoint to **private only** in the specified subnet.

        ***

        ### 4. Verify the change

        ```bash theme={null}
        oci ce cluster get \
          --cluster-id <cluster_ocid> \
          --query "data.endpoint-config" \
          --output json
        ```

        Ensure:

        ```json theme={null}
        "isPublicIpEnabled": false
        ```

        ***

        ### 5. Update your access method

        Once public access is disabled, you must:

        * Access the API from within the VCN (e.g., bastion host, VPN, FastConnect, VCN peering), and
        * Regenerate or update your kubeconfig as needed:

        ```bash theme={null}
        oci ce cluster create-kubeconfig \
          --cluster-id <cluster_ocid> \
          --file $HOME/.kube/config \
          --region <region> \
          --token-version 2.0.0 \
          --kube-endpoint PRIVATE_ENDPOINT \
          --overwrite
        ```
      </Accordion>

      <Accordion title="Using Python">
        To remediate “OCI OKE Kubernetes API Public Endpoint Should Be Disabled” using Python, you need to update the OKE cluster’s endpoint configuration so that `isPublicIpEnabled` is set to `False`.

        Below are the minimal practical steps and a working Python example using the OCI Python SDK.

        ***

        ### 1. Prerequisites

        1. Install OCI Python SDK (if not already):
           ```bash theme={null}
           pip install oci
           ```

        2. Ensure you have:
           * A working OCI CLI/config file (typically at `~/.oci/config`) with a profile that has permissions to update OKE clusters.
           * The **OCID of the OKE cluster** you want to remediate.
           * A **private subnet OCID** for the OKE endpoint (if your cluster is not already using a suitable subnet).
             * This subnet must:
               * Be in the same VCN/region as the cluster.
               * Have appropriate route tables and security lists/NSGs to allow access from your admin nodes/jump hosts.

        ***

        ### 2. Core Python Script to Disable Public Endpoint

        ```python theme={null}
        import oci

        # -----------------------------
        # CONFIGURATION
        # -----------------------------
        # Use the default profile or specify another, e.g., profile_name="MYPROFILE"
        config = oci.config.from_file("~/.oci/config", "DEFAULT")

        # Replace these with your values
        CLUSTER_OCID = "<your_oke_cluster_ocid>"
        PRIVATE_SUBNET_OCID = "<your_private_subnet_ocid>"   # Subnet for the private endpoint

        # -----------------------------
        # CLIENT
        # -----------------------------
        container_engine_client = oci.container_engine.ContainerEngineClient(config)

        # -----------------------------
        # FETCH EXISTING CLUSTER
        # -----------------------------
        cluster = container_engine_client.get_cluster(CLUSTER_OCID).data

        # -----------------------------
        # BUILD UPDATED ENDPOINT CONFIG
        # -----------------------------
        # We reuse existing endpointConfig, just override the relevant fields.
        # If endpoint_config is None, we create a new one.
        endpoint_config = cluster.endpoint_config

        if endpoint_config is None:
            # Create a minimal endpoint config if it doesn't exist
            endpoint_config = oci.container_engine.models.UpdateClusterEndpointConfigDetails(
                subnet_id=PRIVATE_SUBNET_OCID,
                is_public_ip_enabled=False
            )
        else:
            # Update existing endpoint config
            # Ensure we are pointing to a private subnet you control:
            endpoint_config.subnet_id = PRIVATE_SUBNET_OCID
            endpoint_config.is_public_ip_enabled = False

        # -----------------------------
        # PREPARE UPDATE DETAILS
        # -----------------------------
        update_cluster_details = oci.container_engine.models.UpdateClusterDetails(
            endpoint_config=endpoint_config
        )

        # -----------------------------
        # UPDATE CLUSTER
        # -----------------------------
        update_response = container_engine_client.update_cluster(
            cluster_id=CLUSTER_OCID,
            update_cluster_details=update_cluster_details
        )

        work_request_id = update_response.headers.get("opc-work-request-id")
        print(f"Update initiated. Work request OCID: {work_request_id}")

        # OPTIONAL: Wait for the work request to finish
        work_request_client = oci.work_requests.WorkRequestClient(config)

        def wait_for_work_request(wr_id):
            while True:
                wr = work_request_client.get_work_request(wr_id).data
                if wr.time_finished:
                    print("Work request completed with status:", wr.status)
                    break

        wait_for_work_request(work_request_id)
        ```

        ***

        ### 3. High-Level Step-by-Step

        1. **Identify the OKE cluster**: Get its OCID from the console or CLI.
        2. **Select/prepare a private subnet** in the same VCN/region for the Kubernetes API endpoint.
        3. **Configure OCI Python SDK**:
           * Ensure `~/.oci/config` exists and the profile has `ContainerEngine` permissions (e.g., `MANAGE CLUSTER`).
        4. **Run the Python script**:
           * It:
             * Fetches the cluster.
             * Sets `endpoint_config.subnet_id` to your private subnet.
             * Sets `endpoint_config.is_public_ip_enabled = False`.
             * Calls `update_cluster`.
        5. **Verify**:
           * In the OCI Console → Developer Services → Kubernetes Clusters → your cluster:
             * Confirm the API endpoint is now private-only (no public IP).

        This change effectively disables the public Kubernetes API endpoint and forces access via the private network.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_containerengine_cluster" "oke_cluster" {
          # EXISTING ARGUMENTS
          compartment_id     = OCI_COMPARTMENT_OCID          # e.g. "ocid1.compartment.oc1..aaaa..."
          kubernetes_version = "K8S_VERSION"                 # e.g. "v1.30.1"
          name               = "OKE_CLUSTER_NAME"
          vcn_id             = OKE_VCN_OCID                  # e.g. "ocid1.vcn.oc1..aaaa..."

          # This block is what remediates the finding:
          endpoint_config {
            is_public_ip_enabled = false

            # These must be private subnets that have network access to your control plane
            subnet_id = OKE_ENDPOINT_SUBNET_OCID             # private subnet OCID
          }

          # OPTIONAL: if you're using endpoint subnets via network_config instead of endpoint_config
          # ensure they're private and reachable only within the VCN
          # network_config {
          #   pods_cidr       = "PODS_CIDR"
          #   services_cidr   = "SERVICES_CIDR"
          # }
        }
        ```

        This change switches the Kubernetes API endpoint to use only a private IP; Terraform will update the cluster endpoint configuration in place (no resource replacement, but expect API control-plane unavailability during the update).

        To verify, `terraform plan` should show `endpoint_config.is_public_ip_enabled` changing from `true` to `false` on `oci_containerengine_cluster.oke_cluster` and no `-/+` replacement indicator for the resource.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
