> ## 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 Kubelet Read-Only Port Should Be Disabled

### More Info:

The kubelet read-only port (10255) exposes node and pod information without authentication. It must be disabled (set to 0) so attackers on the node network cannot enumerate workloads or fingerprint the cluster.

### Risk Level

High

### Address

Compliance, Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        To disable the kubelet read-only port in OCI OKE you must configure the node pools so that kubelet is started with `--read-only-port=0`. This is done via node pool configuration in the OCI Console; it cannot be toggled at the cluster level.

        Below are the steps (console-only) for both **new** and **existing** node pools.

        ***

        ## 1. For a new node pool (recommended approach)

        1. **Sign in to the OCI Console**
           * Go to: Developer Services → **Kubernetes Clusters (OKE)**.

        2. **Open your cluster**
           * Click your **Compartment**.
           * Click your **Cluster** name.

        3. **Start creating a new node pool**
           * In the cluster details page, go to the **Node Pools** tab.
           * Click **Create node pool**.

        4. **Fill in basic details**
           * Name, Kubernetes version, shape, image, subnet, etc., as per your requirements.

        5. **Configure kubelet to disable read-only port**

           * In the **Advanced options** or **Node configuration** section (exact label can vary by UI version), look for:
             * **Kubelet configuration**, **Kubelet arguments**, or **Custom cloud-init / user data**.
           * You need to ensure kubelet runs with:
             ```text theme={null}
             --read-only-port=0
             ```
           * How you set this depends on what your UI offers:

           **If your console has “Kubelet arguments” / “Kubelet configuration” fields:**

           * Add an extra argument:
             * Key: `read-only-port`
             * Value: `0`
               (or equivalent format in a JSON/YAML field like:
             ````json theme={null}
             {
               "kubeletExtraArgs": {
                 "read-only-port": "0"
               }
             }
             ```)
             ````

           **If there is no explicit kubelet field but a “Custom cloud-init / User data” field:**

           * Add a cloud-init script that edits the kubelet service before it starts. Example (Ubuntu-style):
             ```bash theme={null}
             #cloud-config
             runcmd:
               - sed -i 's#KUBELET_EXTRA_ARGS="\(.*\)"#KUBELET_EXTRA_ARGS="--read-only-port=0 \1"#' /etc/default/kubelet || true
               - systemctl daemon-reload
               - systemctl restart kubelet
             ```
           * Adapt path/file if your image uses a different kubelet config mechanism.

        6. **Create the node pool**
           * Click **Create**.
           * Wait until the node pool status is **Active** and all nodes are **Ready**.

        7. **Move workloads to the new node pool**
           * In your cluster (via `kubectl`), cordon and drain nodes from the old node pool, then delete the old node pool from the **Node Pools** tab once all workloads have been rescheduled.
           * This is the safe way to roll to kubelets with read-only-port disabled.

        ***

        ## 2. For an existing node pool (no direct toggle)

        OKE does not provide an in-place checkbox to change kubelet flags for an existing node pool via the Console. To remediate an existing pool from the console only, the usual pattern is:

        1. **Create a new node pool** following **Section 1** with `--read-only-port=0`.
        2. **Cordon & drain old nodes** (using kubectl; console does not do this automatically for pods).
        3. **Delete the old node pool** in the OKE Console when no longer used.

        ***

        ## 3. Verify kubelet read-only port is disabled

        After the new nodes are up and workloads have moved:

        1. SSH to one of the worker nodes (if permitted by your security rules).
        2. Check kubelet process:
           ```bash theme={null}
           ps aux | grep kubelet | grep -v grep | grep read-only-port
           ```
           It should show:
           ```text theme={null}
           --read-only-port=0
           ```
        3. Alternatively, from a pod on that node, confirm that port 10255 is not reachable:
           ```bash theme={null}
           nc -zv <node-internal-ip> 10255
           ```
           It should fail to connect.

        ***

        If you share what options you see under **Create node pool → Advanced options** in your console, I can give you the exact field names and JSON/YAML structure to paste.
      </Accordion>

      <Accordion title="Using CLI">
        For Oracle Container Engine for Kubernetes (OKE) you generally **don’t disable the kubelet read-only port yourself**; it’s controlled by the OKE control plane:

        * For all current/modern OKE Kubernetes versions, the kubelet `readOnlyPort` is **already disabled by default and cannot be enabled**.
        * If a security scanner is flagging this on an OKE-managed node pool, it’s usually a **false positive** or based on an old OKE version.

        So the only “remediation” that’s actually under your control via OCI CLI is:

        1. **Ensure the cluster is on a supported/modern Kubernetes version**
        2. **Ensure all node pools are upgraded to that version**

        Below are the minimal OCI CLI steps to do that.

        ***

        ### 1. List your clusters and get the OCID

        ```bash theme={null}
        oci ce cluster list \
          --compartment-id <compartment_ocid> \
          --all

        # Note the cluster OCID you want to remediate
        ```

        ***

        ### 2. Check available Kubernetes versions for OKE

        ```bash theme={null}
        oci ce cluster-option get \
          --cluster-option-id all \
          --compartment-id <compartment_ocid> \
          | jq '.kubernetesVersions'
        ```

        Pick a current supported version (for example: `v1.29.x` or similar).

        ***

        ### 3. Upgrade the cluster control plane

        ```bash theme={null}
        TARGET_VERSION="<target_k8s_version>"

        oci ce cluster update \
          --cluster-id <cluster_ocid> \
          --kubernetes-version "$TARGET_VERSION" \
          --force \
          --wait-for-state SUCCEEDED
        ```

        Wait until the update completes.

        ***

        ### 4. List node pools for this cluster

        ```bash theme={null}
        oci ce node-pool list \
          --compartment-id <compartment_ocid> \
          --cluster-id <cluster_ocid> \
          --all
        ```

        Note each node pool OCID.

        ***

        ### 5. Upgrade each node pool to the same version

        For each node pool:

        ```bash theme={null}
        NODE_POOL_ID="<node_pool_ocid>"

        oci ce node-pool update \
          --node-pool-id "$NODE_POOL_ID" \
          --kubernetes-version "$TARGET_VERSION" \
          --force \
          --wait-for-state SUCCEEDED
        ```

        OKE will roll your worker nodes to the new version, using the OKE-managed kubelet configuration (with the read-only port disabled).

        ***

        ### 6. (Optional) Verify from a node

        If you have SSH access to a worker node, you can confirm the port is not listening:

        ```bash theme={null}
        sudo netstat -tulnp | grep 10255 || echo "kubelet read-only port not listening"
        ```

        ***

        **Key point:**\
        On managed OKE node pools you cannot set or unset `readOnlyPort` via OCI CLI; compliance is achieved by running on a current OKE version, where the kubelet read-only port is disabled by design.
      </Accordion>

      <Accordion title="Using Python">
        To disable the kubelet read-only port in OCI OKE with Python, you must:

        * Set the kubelet flag `--read-only-port=0` via the node pool’s `kubeletConfig`
        * Then roll/replace the nodes in that pool so the new config takes effect

        Below are step‑by‑step instructions and example Python code using the OCI Python SDK.

        ***

        ## 1. Prerequisites

        1. Install OCI SDK:

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

        2. Make sure you have an OCI config file (`~/.oci/config`) with a profile that has rights to manage OKE node pools:

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

        3. You need:
           * `compartment_id` (OCID)
           * `cluster_id` (OCID) – optional if you already know the node pool OCID
           * `node_pool_id` (OCID) of the node pool to modify

        ***

        ## 2. Update node pool kubelet config (`read-only-port=0`)

        OKE lets you set kubelet flags via `kubelet_config` (a key/value map) on the node pool. You want to ensure:

        ```yaml theme={null}
        read-only-port: "0"
        ```

        Python example:

        ```python theme={null}
        import oci

        # ---- Setup client ----
        config = oci.config.from_file("~/.oci/config", "DEFAULT")
        ce_client = oci.container_engine.ContainerEngineClient(config)

        node_pool_id = "ocid1.nodepool.oc1..."  # your node pool OCID

        # ---- Get current node pool ----
        node_pool = ce_client.get_node_pool(node_pool_id).data

        # Existing kubelet config is a dict[str,str] or None
        current_kubelet_config = dict(node_pool.node_config_details.kubelet_config or {})

        # ---- Set read-only-port=0 ----
        current_kubelet_config["read-only-port"] = "0"

        # ---- Build update details ----
        update_details = oci.container_engine.models.UpdateNodePoolDetails(
            node_config_details=oci.container_engine.models.NodePoolNodeConfigDetails(
                size=node_pool.node_config_details.size,
                placement_configs=node_pool.node_config_details.placement_configs,
                kubelet_config=current_kubelet_config
            ),
            name=node_pool.name,
            kubernetes_version=node_pool.kubernetes_version,
            node_shape=node_pool.node_shape,
            node_source_details=node_pool.node_source_details
        )

        # ---- Call update_node_pool ----
        update_response = ce_client.update_node_pool(
            node_pool_id=node_pool_id,
            update_node_pool_details=update_details
        )

        print("Update work request:", update_response.headers.get("opc-work-request-id"))
        ```

        Notes:

        * You must preserve all required existing fields (size, placement\_configs, node\_source\_details, etc.) when constructing `UpdateNodePoolDetails`, otherwise you may unintentionally change them.
        * If your SDK version has additional required attributes, mirror them from `node_pool` into `update_details`.

        ***

        ## 3. Roll / recycle the nodes in the pool

        The new kubelet arguments are applied when new nodes are created. For existing nodes, you must replace them (cordon/drain then terminate) so OKE recreates nodes with the new config.

        Minimal approach:

        1. **Cordoning & draining** (via kubectl; optional but strongly recommended before termination):

        ```bash theme={null}
        # Get nodes in this node pool (assuming label contains pool ID or name)
        kubectl get nodes --show-labels | grep <node-pool-identifier>

        # For each node:
        kubectl cordon <node-name>
        kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
        ```

        2. **Terminate nodes so they’re recreated** via OCI SDK or Console.

        With Python/SDK you can rotate nodes one by one (simple illustration):

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

        compute_client = oci.core.ComputeClient(config)

        # Example: list instances with the node pool OCID tag/metadata
        # This depends on how your environment labels/compartmentalizes instances.
        # You might filter by compartment and matching display_name prefix, or tags.

        compartment_id = "ocid1.compartment.oc1..."  # same as cluster/node pool compartment

        instances = oci.pagination.list_call_get_all_results(
            compute_client.list_instances,
            compartment_id=compartment_id
        ).data

        for inst in instances:
            # Very environment-specific filter: adjust to your naming/tagging
            if node_pool_id in (inst.display_name or ""):
                print("Terminating:", inst.display_name, inst.id)
                compute_client.terminate_instance(inst.id, preserve_boot_volume=False)
        ```

        OKE will recreate terminated worker nodes according to the node pool definition, now with `--read-only-port=0` in kubelet arguments.

        ***

        ## 4. Verify the kubelet read-only port is disabled

        Once new nodes are ready:

        1. SSH into a worker node (if allowed) and check kubelet process args:

        ```bash theme={null}
        ps aux | grep kubelet | grep read-only-port
        ```

        You should see `--read-only-port=0` (or no `--read-only-port` at all, which defaults to disabled on newer versions).

        2. From inside the cluster, confirm the read-only port isn’t listening (default would be 10255):

        ```bash theme={null}
        kubectl run test-pod --rm -it --image=nicolaka/netshoot -- bash
        # Inside pod:
        nc -vz <node-internal-ip> 10255 || echo "No read-only port open"
        ```

        ***

        ## 5. Apply to all node pools programmatically (optional)

        You can loop over all node pools in a cluster and enforce this setting:

        ```python theme={null}
        compartment_id = "ocid1.compartment.oc1..."
        cluster_id = "ocid1.cluster.oc1..."

        node_pools = oci.pagination.list_call_get_all_results(
            ce_client.list_node_pools,
            compartment_id=compartment_id,
            cluster_id=cluster_id
        ).data

        for np in node_pools:
            np_details = ce_client.get_node_pool(np.id).data
            kubelet_cfg = dict(np_details.node_config_details.kubelet_config or {})
            if kubelet_cfg.get("read-only-port") == "0":
                continue  # already compliant

            kubelet_cfg["read-only-port"] = "0"

            update_details = oci.container_engine.models.UpdateNodePoolDetails(
                node_config_details=oci.container_engine.models.NodePoolNodeConfigDetails(
                    size=np_details.node_config_details.size,
                    placement_configs=np_details.node_config_details.placement_configs,
                    kubelet_config=kubelet_cfg
                ),
                name=np_details.name,
                kubernetes_version=np_details.kubernetes_version,
                node_shape=np_details.node_shape,
                node_source_details=np_details.node_source_details
            )

            resp = ce_client.update_node_pool(np.id, update_details)
            print(f"Updating node pool {np.name} ({np.id}), work request: {resp.headers.get('opc-work-request-id')}")
        ```

        Then roll nodes as in step 3.

        ***

        If you share your current SDK version and how your node pools are structured (e.g., managed vs. custom node images), I can adjust the exact model fields and filters for your environment.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # There is currently no Terraform argument on oci_containerengine_node_pool
        # (or related OKE resources in the oci provider) that controls the kubelet
        # read-only port (10255), so this finding cannot be remediated via Terraform.

        # You must change this setting outside Terraform:
        # 1. In the OCI Console or via OCI CLI/REST, configure the OKE node pool/cluster
        #    so the kubelet is started with the read-only port disabled (set to 0).
        # 2. This typically requires updating the nodepool/cluster configuration and
        #    rolling or recreating nodes so the kubelet starts with the new flags.
        ```

        This setting is not exposed by the `oci_containerengine_node_pool` resource or any other current OKE-related Terraform resource, so Terraform cannot directly set the kubelet read-only port to 0; follow Oracle’s OKE documentation for the exact Console/API steps.

        Verification: `terraform plan` will show **no changes** related to kubelet or port 10255, since the provider does not manage that field.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
