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
- Remediation
Remediation
Using Console
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)
-
Sign in to the OCI Console
- Go to: Developer Services → Kubernetes Clusters (OKE).
-
Open your cluster
- Click your Compartment.
- Click your Cluster name.
-
Start creating a new node pool
- In the cluster details page, go to the Node Pools tab.
- Click Create node pool.
-
Fill in basic details
- Name, Kubernetes version, shape, image, subnet, etc., as per your requirements.
-
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:
--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:
{"kubeletExtraArgs": {"read-only-port": "0"}}```) - Key:
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):
#cloud-configruncmd:- 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.
- In the Advanced options or Node configuration section (exact label can vary by UI version), look for:
-
Create the node pool
- Click Create.
- Wait until the node pool status is Active and all nodes are Ready.
-
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.
- In your cluster (via
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:
- Create a new node pool following Section 1 with
--read-only-port=0. - Cordon & drain old nodes (using kubectl; console does not do this automatically for pods).
- 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:
- SSH to one of the worker nodes (if permitted by your security rules).
- Check kubelet process:
It should show:ps aux | grep kubelet | grep -v grep | grep read-only-port--read-only-port=0
- Alternatively, from a pod on that node, confirm that port 10255 is not reachable:
It should fail to connect.nc -zv <node-internal-ip> 10255
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.
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
readOnlyPortis 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:
- Ensure the cluster is on a supported/modern Kubernetes version
- 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
oci ce cluster list \
--compartment-id <compartment_ocid> \
--all
# Note the cluster OCID you want to remediate
2. Check available Kubernetes versions for OKE
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
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
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:
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:
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.
Using Python
To disable the kubelet read-only port in OCI OKE with Python, you must:
- Set the kubelet flag
--read-only-port=0via the node pool’skubeletConfig - 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
- Install OCI SDK:
pip install oci
- Make sure you have an OCI config file (
~/.oci/config) with a profile that has rights to manage OKE node pools:
[DEFAULT]
user=ocid1.user.oc1...
fingerprint=...
key_file=/path/to/oci_api_key.pem
tenancy=ocid1.tenancy.oc1...
region=eu-frankfurt-1
- You need:
compartment_id(OCID)cluster_id(OCID) – optional if you already know the node pool OCIDnode_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:
read-only-port: "0"
Python example:
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_poolintoupdate_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:
- Cordoning & draining (via kubectl; optional but strongly recommended before termination):
# 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
- Terminate nodes so they’re recreated via OCI SDK or Console.
With Python/SDK you can rotate nodes one by one (simple illustration):
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:
- SSH into a worker node (if allowed) and check kubelet process args:
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).
- From inside the cluster, confirm the read-only port isn’t listening (default would be 10255):
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:
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.
Using Terraform
# 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.