> ## 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 Access to Kubernetes Control Plane Should Be Restricted

### More Info:

The Kubernetes API endpoint should be reachable only from approved networks (VCN CIDRs, bastions, or specific NSG members). Open control-plane endpoints are a primary target for credential and token abuse.

### 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">
        Here’s how to restrict access to the OKE Kubernetes API (control plane) using the OCI Console.

        There are two main ways:

        1. Restrict a **public** endpoint with an IP allow-list
        2. Use a **private** endpoint (accessible only from your VCN)

        ***

        ## 1. Check current endpoint type and access

        1. Sign in to the **OCI Console**.
        2. Open the navigation menu → **Developer Services** → **Kubernetes Clusters (OKE)**.
        3. Select your **compartment**, then click the **cluster**.
        4. On the **Cluster details** page, under **Endpoint**, note:
           * **Endpoint Type**: Public or Private
           * For Public: check configured **Authorized IPs** (CIDR list)

        ***

        ## 2. If you must keep a Public endpoint – restrict by IP

        Use this if you still want internet access, but locked to specific IPs (e.g., office VPN, bastion).

        1. In the **Cluster details** page, click **Edit** on the **Endpoint Configuration** section (name may appear as “Kubernetes API Endpoint” or similar).
        2. Ensure **Endpoint Type** is **Public**.
        3. In **Authorized IPs** (or "API Endpoint CIDR allowlist"):
           * Remove `0.0.0.0/0` or any broad networks you don’t intend to allow.
           * Add only the required **source IPs/CIDRs**, e.g.:
             * Your corporate public IP: `203.0.113.10/32`
             * VPN egress subnet: `198.51.100.0/24`
        4. Save/Update the configuration.
        5. Test from:
           * An allowed IP: `kubectl get nodes` should work.
           * A disallowed IP: connection to the API should fail.

        ***

        ## 3. Prefer a Private endpoint – restrict to your VCN

        This is the most secure: the API is reachable only inside your VCN (via VCN, VPN, FastConnect, or bastion).

        ### 3.1 Convert/ensure cluster is using a private endpoint

        For new clusters, you can select **Private** API endpoint during creation.\
        For existing clusters, you may need to **update** or **recreate** depending on your OKE version and options available.

        1. In **Kubernetes Clusters (OKE)** → your **cluster** → **Cluster details**.
        2. Look for **Edit cluster** / **Edit endpoint configuration**.
        3. If the console allows:
           * Change **Endpoint Type** to **Private**.
           * Select the **VCN**, **subnet** where the endpoint will be hosted.
           * Confirm and save.
        4. If you cannot change endpoint type (option is disabled):
           * Plan to **create a new cluster** with a **Private** endpoint and migrate workloads.

        ### 3.2 Lock down network access to the private endpoint

        The private endpoint lives in a subnet; access is controlled by:

        * **Network Security Groups (NSGs)** attached to the OKE API endpoint
        * Or **Security Lists** on the subnet

        Steps (using NSGs is recommended):

        1. In the Console, go to **Networking** → **Virtual Cloud Networks**.
        2. Open the **VCN** used by your OKE cluster.
        3. Go to **Network Security Groups** and locate the NSG associated with the **Kubernetes API endpoint** (name often includes the cluster name and “cp”/“api”).
        4. Edit the **Ingress Rules**:
           * Remove any rule with source `0.0.0.0/0` or overly broad CIDRs.
           * Add rules that allow **TCP 6443** (Kubernetes API) only from:
             * Your admin/bastion subnet CIDR (e.g., `10.0.10.0/24`), or
             * A specific host subnet or NSG.
        5. If instead security lists are used:
           * In the **VCN**, open **Subnets** → endpoint subnet.
           * Edit the **Security List**:
             * Restrict inbound rule for port **6443** to only the required internal CIDRs.

        ***

        ## 4. Ensure access path for admins and automation

        After restricting:

        * If using **private endpoint**:
          * Use a **bastion host** inside the VCN, or
          * Use **OCI Bastion service**, or
          * Connect over **VPN / FastConnect** so your admin machine is in an allowed CIDR.
        * Update any CI/CD runners to come from the allowed CIDRs/subnets.

        ***

        ## 5. Verify restriction

        1. From an allowed network:
           * Download kubeconfig from the Console (cluster page → **Access Cluster** → **Local access** / **Cloud Shell**, etc.).
           * Run: `kubectl get namespaces` – should succeed.
        2. From a disallowed IP/subnet:
           * Kubernetes API calls should time out or be refused.

        ***

        If you share whether your current OKE endpoint is public or private, I can give a concrete, minimal rule set to apply for your case.
      </Accordion>

      <Accordion title="Using CLI">
        Below are two common remediations for “OCI OKE access to Kubernetes control plane should be restricted” using OCI CLI:

        * A) Restrict a **public** OKE endpoint to specific IPs
        * B) Switch to a **private** OKE endpoint (or ensure it’s private and protected via NSGs)

        Pick whichever matches your architecture.

        ***

        ## 0. Prerequisites

        Make sure:

        * `oci` CLI is installed and configured (`oci setup config` done).
        * You know your:
          * `cluster-id`
          * Compartment OCID
          * Subnet OCID for the control plane endpoint
          * NSG OCIDs (recommended for additional restriction)

        ***

        ## 1. Get current cluster configuration

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

        Look for:

        * `isPublicIpEnabled`
        * `subnetId`
        * `nsgIds`
        * `publicEndpointConfig.allowedPublicIpAddresses`

        If `isPublicIpEnabled = true` and allowedPublicIpAddresses is `null` or contains `0.0.0.0/0`, it’s effectively open.

        ***

        ## A) Restrict a **Public** OKE Control Plane Endpoint

        ### 1. Decide allowed IP CIDRs

        Example: only allow your corporate public IP and a jump host:

        ```text theme={null}
        203.0.113.10/32
        198.51.100.20/32
        ```

        ### 2. (Recommended) Create / identify NSGs

        Create an NSG for the OKE control plane subnet if you don’t have one:

        ```bash theme={null}
        oci network nsg create \
          --compartment-id <COMPARTMENT_OCID> \
          --vcn-id <VCN_OCID> \
          --display-name "oke-control-plane-nsg"
        ```

        Add rules to only allow your trusted sources to port 6443:

        ```bash theme={null}
        oci network nsg rule add \
          --network-security-group-id <NSG_OCID> \
          --security-rules '[
            {
              "direction": "INGRESS",
              "protocol": "6",
              "source": "203.0.113.10/32",
              "tcpOptions": { "destinationPortRange": { "min": 6443, "max": 6443 } }
            },
            {
              "direction": "INGRESS",
              "protocol": "6",
              "source": "198.51.100.20/32",
              "tcpOptions": { "destinationPortRange": { "min": 6443, "max": 6443 } }
            }
          ]'
        ```

        ### 3. Update the OKE cluster endpoint config (restrict IPs)

        ```bash theme={null}
        oci ce cluster update \
          --cluster-id <CLUSTER_OCID> \
          --force \
          --endpoint-config '{
            "isPublicIpEnabled": true,
            "subnetId": "<CONTROL_PLANE_SUBNET_OCID>",
            "nsgIds": ["<NSG_OCID>"],
            "publicEndpointConfig": {
              "allowedPublicIpAddresses": [
                "203.0.113.10/32",
                "198.51.100.20/32"
              ]
            }
          }'
        ```

        Wait for the work request to finish:

        ```bash theme={null}
        oci ce cluster get --cluster-id <CLUSTER_OCID> \
          --query 'data."lifecycle-state"' --raw-output
        ```

        Confirm:

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

        ***

        ## B) Use a **Private** OKE Control Plane Endpoint

        This limits access to VCN/internal networks and NSGs.

        ### 1. Ensure you have a private subnet for control plane

        * Subnet should be private (no internet gateway route).
        * Attach a suitable NSG.

        ### 2. Update cluster to use private endpoint

        ```bash theme={null}
        oci ce cluster update \
          --cluster-id <CLUSTER_OCID> \
          --force \
          --endpoint-config '{
            "isPublicIpEnabled": false,
            "subnetId": "<PRIVATE_CONTROL_PLANE_SUBNET_OCID>",
            "nsgIds": ["<NSG_OCID>"]
          }'
        ```

        Again, check state and config:

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

        Ensure:

        * `isPublicIpEnabled` is `false`
        * NSGs are in place and only allow required internal sources on port 6443.

        ***

        ## 3. Validate `kubectl` Access

        After any change, from your allowed location:

        ```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 PUBLIC_ENDPOINT   # or PRIVATE_ENDPOINT if private
        ```

        Then:

        ```bash theme={null}
        kubectl get ns
        ```

        If you need, tell me whether your cluster is currently public or private and I can give the exact JSON payload for your case.
      </Accordion>

      <Accordion title="Using Python">
        To restrict access to the Kubernetes control plane in OCI OKE, you generally want to:

        * Disable the public API endpoint for the cluster (make the control plane private), or
        * At minimum, ensure the API endpoint is only reachable from controlled private subnets/NSGs.

        Below is how to do this with Python using the OCI SDK.

        ***

        ## 1. Prerequisites

        * `oci` SDK installed:
          ```bash theme={null}
          pip install oci
          ```
        * A valid OCI config file (e.g. `~/.oci/config`) and a profile with permissions to update OKE clusters:
          * `OCI CLI`-style credentials, or instance principal/session auth if you prefer (code can be adapted).

        ***

        ## 2. High‑level steps

        1. Identify the OKE cluster OCID.
        2. Decide:
           * Which **private subnet** will host the control plane endpoint.
           * Which **Network Security Groups (NSGs)** will control traffic to that subnet.
        3. Update the OKE cluster’s endpoint configuration to:
           * Disable public IP access (`is_public_ip_enabled=False`).
           * Point to the correct private subnet and NSGs.

        ***

        ## 3. Python example: Make control plane private and restrict via NSGs

        ```python theme={null}
        import oci
        from oci.container_engine import ContainerEngineClient
        from oci.container_engine.models import UpdateClusterDetails, UpdateClusterEndpointConfigDetails

        # ------------------------------------------------------------------
        # CONFIGURATION
        # ------------------------------------------------------------------
        # Path to OCI config and profile name
        CONFIG_FILE = "~/.oci/config"
        PROFILE = "DEFAULT"

        # The OCID of your OKE cluster
        CLUSTER_ID = "ocid1.cluster.oc1...."

        # Private subnet OCID where the control plane endpoint should live
        PRIVATE_SUBNET_ID = "ocid1.subnet.oc1...."

        # One or more NSG OCIDs that strictly control who can reach the control plane
        # These NSGs should contain ONLY the required ingress rules (e.g., from bastion, CI/CD, etc.)
        CONTROL_PLANE_NSG_IDS = [
            "ocid1.networksecuritygroup.oc1...."
        ]

        # ------------------------------------------------------------------
        # MAIN
        # ------------------------------------------------------------------
        def main():
            # Load config
            config = oci.config.from_file(CONFIG_FILE, PROFILE)
            ce_client = ContainerEngineClient(config)

            # (Optional) show current cluster endpoint config
            current_cluster = ce_client.get_cluster(CLUSTER_ID).data
            print("Current endpoint config:", current_cluster.endpoint_config)

            # Build new endpoint configuration:
            # - Disable public IP for control plane (private endpoint only)
            # - Place endpoint in PRIVATE_SUBNET_ID
            # - Attach restrictive NSGs
            endpoint_config_update = UpdateClusterEndpointConfigDetails(
                is_public_ip_enabled=False,       # core restriction
                subnet_id=PRIVATE_SUBNET_ID,      # private subnet
                nsg_ids=CONTROL_PLANE_NSG_IDS     # controlled NSGs
            )

            update_details = UpdateClusterDetails(
                endpoint_config=endpoint_config_update
            )

            # Call update_cluster
            response = ce_client.update_cluster(
                cluster_id=CLUSTER_ID,
                update_cluster_details=update_details
            )

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

            # (Optional) wait for completion
            work_request_client = oci.work_requests.WorkRequestClient(config)
            oci.wait_until(
                work_request_client,
                work_request_client.get_work_request(work_request_id),
                'status',
                'SUCCEEDED'
            )
            print("Cluster endpoint update completed.")

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

        ***

        ## 4. NSG rules (conceptual)

        On the NSGs you pass in `CONTROL_PLANE_NSG_IDS`, ensure rules:

        * **Ingress**:
          * Allow TCP 6443 (Kubernetes API) and any other required ports
          * Source: only:
            * Bastion hosts
            * Admin subnets
            * CI/CD runners
            * On‑prem IPs via VPN/DRG
        * **No** `0.0.0.0/0` or broad public CIDRs.

        Example ingress rule concept (configured in OCI console or via Python SDK):

        * Source: `10.0.10.0/24` (bastion subnet)
        * Protocol: TCP
        * Port: 6443

        ***

        ## 5. Notes / edge cases

        * If the cluster was created as **public endpoint only** in some older setups, you might need to:
          * Ensure a private subnet exists with route to your workers/bastion; then
          * Apply the `UpdateClusterEndpointConfigDetails` as shown.
        * After making the endpoint private, your `kubectl` clients must reach that private subnet (via VPN, FastConnect, bastion, etc.).

        If you share how your cluster is currently configured (public vs private, subnets, etc.), I can tailor the Python snippet and NSG rules more precisely.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_containerengine_cluster" "OKE_CLUSTER" {
          # Substitute:
          # - OKE_CLUSTER with your cluster name
          # - COMPARTMENT_OCID with the target compartment OCID
          name           = "OKE_CLUSTER"
          compartment_id = "COMPARTMENT_OCID"
          kubernetes_version = "K8S_VERSION"
          vcn_id             = oci_core_vcn.OKE_VCN.id

          # Restrict the Kubernetes API endpoint to a private subnet and NSG
          endpoint_config {
            subnet_id            = oci_core_subnet.OKE_API_SUBNET.id
            is_public_ip_enabled = false
            nsg_ids              = [oci_core_network_security_group.OKE_API_NSG.id]
          }

          options {
            service_lb_subnet_ids = [oci_core_subnet.OKE_LB_SUBNET.id]
          }
        }

        # Subnet where the Kubernetes API (control plane endpoint) lives
        resource "oci_core_subnet" "OKE_API_SUBNET" {
          # Substitute:
          # - OKE_API_SUBNET with your subnet name
          # - OKE_VCN with your VCN resource name
          # - CIDR_FOR_API_SUBNET with the subnet CIDR
          compartment_id = "COMPARTMENT_OCID"
          vcn_id         = oci_core_vcn.OKE_VCN.id
          cidr_block     = "CIDR_FOR_API_SUBNET"

          display_name               = "OKE_API_SUBNET"
          prohibit_public_ip_on_vnic = true
          dns_label                  = "okeapi"

          # Attach the NSG that enforces allowed sources to the control-plane endpoint
          nsg_ids = [oci_core_network_security_group.OKE_API_NSG.id]
        }

        # NSG that protects the Kubernetes API endpoint
        resource "oci_core_network_security_group" "OKE_API_NSG" {
          # Substitute:
          # - OKE_API_NSG with your NSG name
          compartment_id = "COMPARTMENT_OCID"
          vcn_id         = oci_core_vcn.OKE_VCN.id
          display_name   = "OKE_API_NSG"
        }

        # Ingress rule: allow Kubernetes API (TCP 6443) only from approved network(s)
        resource "oci_core_network_security_group_security_rule" "OKE_API_INGRESS" {
          # Substitute:
          # - ALLOWED_CIDR with your bastion/office/VPN CIDR (e.g., "203.0.113.0/24")
          #   OR use source_type = "NETWORK_SECURITY_GROUP" and source = OTHER_NSG_OCID
          network_security_group_id = oci_core_network_security_group.OKE_API_NSG.id

          direction = "INGRESS"
          protocol  = "6" # TCP

          source      = "ALLOWED_CIDR"
          source_type = "CIDR_BLOCK"

          tcp_options {
            destination_port_range {
              min = 6443
              max = 6443
            }
          }
        }

        # (Example) Allow egress to worker nodes and required OCI services as needed
        resource "oci_core_network_security_group_security_rule" "OKE_API_EGRESS" {
          network_security_group_id = oci_core_network_security_group.OKE_API_NSG.id

          direction = "EGRESS"
          protocol  = "all"

          destination      = "0.0.0.0/0"
          destination_type = "CIDR_BLOCK"
        }
        ```

        Changing a public control-plane endpoint to private and attaching NSGs is an in-place update of the `oci_containerengine_cluster` endpoint configuration; worker nodes and node pools are not recreated, but expect a short control-plane reconfiguration period.

        Verification: `terraform plan` should show an in-place update to `oci_containerengine_cluster.OKE_CLUSTER` changing `endpoint_config.is_public_ip_enabled` from `true` to `false`, plus creation of the `oci_core_network_security_group` and `oci_core_network_security_group_security_rule` resources (and the API subnet if it is new).
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
