> ## 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 Anonymous Requests to Kubelet Server Should Be Disabled

### More Info:

When anonymous authentication is enabled, requests not rejected by other authenticators are treated as anonymous and may reach kubelet APIs. Disabling anonymous auth is a hard requirement to prevent unauthenticated control-plane access on nodes.

### 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">
        To disable anonymous requests to the kubelet in OCI OKE using the OCI Console, you need to update your node pool configuration so kubelet runs with anonymous authentication turned off. In most cases this is done by updating (or recreating) the node pool with the correct kubelet config.

        Below are the steps in the OCI Console.

        ***

        ### 1. Confirm your OKE cluster and node pool

        1. Sign in to the OCI Console.
        2. Open the navigation menu → **Developer Services** → **Kubernetes Clusters (OKE)**.
        3. Select the **Compartment** where your cluster resides.
        4. Click your **cluster name**.
        5. In the cluster details page, go to the **Node Pools** tab.
        6. Identify the **node pool(s)** whose kubelet configuration you want to fix.

        ***

        ### 2. Check if node pool kubelet configuration is editable

        For many OKE versions, key kubelet security options are controlled via the **Kubelet configuration** section on the node pool.

        1. In the **Node Pools** tab, click the **node pool name**.
        2. Click **Edit** or **Update** (top-right of the node pool details page).
        3. Expand **Advanced Options** → **Kubelet configuration** (or similar wording).

        If the console exposes a checkbox or field related to anonymous authentication, use it (e.g. something like **Disable anonymous access** / **Anonymous auth**).\
        If not, you’ll need to specify a kubelet config block (JSON/YAML) through the console field provided.

        ***

        ### 3. Set kubelet to disallow anonymous auth

        Within the **Kubelet configuration** section, you must ensure that kubelet is started with `--anonymous-auth=false`. Depending on what your OKE version exposes:

        * If there is a **toggle/checkbox** for anonymous auth:
          * Turn **off / disable** anonymous authentication (or **enable “Disable anonymous access”**).
        * If there is a **raw kubelet config field**:
          * Add or ensure a flag or parameter equivalent to:
            * `anonymousAuth: false`\
              or
            * an extra-argument that sets `--anonymous-auth=false`.

        Save/update the node pool when done.

        > Note: Exact field names can differ slightly by OKE/Kubernetes version. Look for anything documented or labeled around “anonymous authentication”, “anonymous access”, or kubelet security options.

        ***

        ### 4. Apply changes to worker nodes

        Changing the node pool configuration alone does not always immediately reconfigure existing worker nodes. You typically need to **recreate** the worker nodes so they are started with the updated kubelet settings.

        You have two main options:

        #### Option A – Rolling replacement within the same node pool (if supported):

        1. After saving the updated node pool configuration, **scale the node pool up** by adding new nodes.
        2. Wait for the new nodes to become **Active** and join the cluster.
        3. **Cordon and drain** old nodes (from `kubectl`):
           ```bash theme={null}
           kubectl cordon <old-node-name>
           kubectl drain <old-node-name> --ignore-daemonsets --delete-emptydir-data
           ```
        4. In the OCI Console, **terminate** the old nodes from the node pool.
        5. Repeat for each old node until all nodes in the node pool are newly created with the updated kubelet config.

        #### Option B – Create a new node pool (cleaner approach):

        1. In the cluster page, go to **Node Pools** → **Create node pool**.
        2. Configure it identically (shape, images, labels, taints, etc.) but:
           * Under **Advanced Options → Kubelet configuration**, set anonymous auth to disabled as described above.
        3. Create the node pool and wait for nodes to become **Active**.
        4. Use `kubectl` to **cordon and drain** nodes from the old node pool, then:
           * In the console, **delete** the old node pool once workloads are safely running on the new pool.

        ***

        ### 5. Verify that anonymous auth is disabled

        After nodes with the new configuration are running:

        1. Get the node IPs:
           ```bash theme={null}
           kubectl get nodes -o wide
           ```
        2. From a pod (or a secure bastion) with network access to node IPs, verify kubelet no longer accepts anonymous requests (for example, an unauthenticated curl to kubelet’s read-only port or API should now fail).

        If available in your security tooling or benchmark scanner, re-run the **CIS / security check** that reported “Anonymous Requests to Kubelet Server Should Be Disabled” to confirm it passes.

        ***

        If you share the exact OKE and Kubernetes version you’re running, I can give the precise field name/syntax for the kubelet configuration in your console view.
      </Accordion>

      <Accordion title="Using CLI">
        To disable anonymous requests to the kubelet on OKE worker nodes, you need to change the kubelet configuration on each node so that:

        * `--anonymous-auth=false` is set (or)
        * `authentication: anonymous: enabled: false` is set in the kubelet config file

        OKE does not expose kubelet flags directly via the OCI CLI, so you use the OCI CLI to discover and SSH to nodes, then change the kubelet config on each node.

        Below are step‑by‑step commands and actions.

        ***

        ## 1. Get the node pool and node OCIDs

        ```bash theme={null}
        # Set variables
        COMPARTMENT_OCID="<your_compartment_ocid>"
        CLUSTER_OCID="<your_oke_cluster_ocid>"

        # List node pools for a cluster
        oci ce node-pool list \
          --compartment-id "$COMPARTMENT_OCID" \
          --cluster-id "$CLUSTER_OCID" \
          --all
        ```

        Note the `id` (`nodePoolId`) you want to remediate.

        ```bash theme={null}
        NODE_POOL_OCID="<your_node_pool_ocid>"

        # Get node pool details (to see instance OCIDs)
        oci ce node-pool get --node-pool-id "$NODE_POOL_OCID" \
          --query "data.nodes[].id" --raw-output
        ```

        This returns the compute instance OCIDs for the worker nodes.

        ***

        ## 2. Get each node’s public IP (via OCI CLI)

        For each worker instance OCID:

        ```bash theme={null}
        INSTANCE_OCID="<worker_instance_ocid>"

        # Get VNIC attachment
        VNIC_ID=$(oci compute vnic-attachment list \
          --compartment-id "$COMPARTMENT_OCID" \
          --instance-id "$INSTANCE_OCID" \
          --query "data[0].\"vnic-id\"" \
          --raw-output)

        # Get public IP
        oci network vnic get --vnic-id "$VNIC_ID" \
          --query "data.\"public-ip\"" \
          --raw-output
        ```

        Use that IP for SSH.

        ***

        ## 3. SSH to each node

        ```bash theme={null}
        ssh -i <path_to_private_key> opc@<node_public_ip>
        ```

        (Use `ubuntu` or other user if your image differs.)

        ***

        ## 4. Update kubelet config on the node

        On each node:

        1. Locate the kubelet config file (for OKE it is usually `/var/lib/kubelet/config.yaml`):

           ```bash theme={null}
           sudo cat /var/lib/kubelet/config.yaml | grep -A3 authentication
           ```

        2. Ensure anonymous auth is disabled. The `authentication` section should look like:

           ```yaml theme={null}
           authentication:
             anonymous:
               enabled: false
             webhook:
               enabled: true
               cacheTTL: 2m0s
           ```

           If `anonymous.enabled` is `true` or missing, edit the file:

           ```bash theme={null}
           sudo vi /var/lib/kubelet/config.yaml
           ```

           Add/modify:

           ```yaml theme={null}
           authentication:
             anonymous:
               enabled: false
           ```

           If the node is using flags instead of config.yaml, edit the kubelet systemd unit or environment file (often `/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf` or similar) and ensure:

           ```bash theme={null}
           --anonymous-auth=false
           ```

        3. Reload systemd and restart kubelet:

           ```bash theme={null}
           sudo systemctl daemon-reload
           sudo systemctl restart kubelet
           ```

        4. Verify kubelet is healthy:

           ```bash theme={null}
           sudo systemctl status kubelet
           ```

        Repeat steps 3–4 for each worker node in the node pool.

        ***

        ## 5. (Optional) Bake this into node boot/launch

        Because OKE re‑creates worker nodes (e.g., during scaling or upgrades), you should ensure new nodes get the same setting automatically. You can do this by:

        * Using a custom image where `/var/lib/kubelet/config.yaml` already has `anonymous.enabled: false`, or
        * Using cloud-init/bootstrapping scripts in the instance’s metadata.

        Using OCI CLI to update instance metadata for the node pool’s instance configuration (template):

        1. Identify the instance configuration used by the node pool:

           ```bash theme={null}
           oci ce node-pool get --node-pool-id "$NODE_POOL_OCID"
           ```

           Look for `nodeConfigDetails` → `placementConfigs` / instance details, then trace back to the instance configuration (if used) via Compute → Instance Configurations.

        2. Update that instance configuration’s metadata to include a cloud-init script that enforces the kubelet config on boot.

        Example (pseudo):

        ```bash theme={null}
        oci compute instance-configuration update \
          --instance-configuration-id <ocid> \
          --instance-details '{
            "launchDetails": {
              "metadata": {
                "user_data": "'"$(base64 -w0 kubelet-hardening.yaml)"'"
              }
            }
          }'
        ```

        Where `kubelet-hardening.yaml` is a cloud-init script that edits `/var/lib/kubelet/config.yaml` to set `authentication.anonymous.enabled: false` and restarts kubelet.

        ***

        ## 6. Validate from the cluster

        From a machine with `kubectl` access:

        ```bash theme={null}
        kubectl get nodes
        kubectl describe node <node_name> | grep -i kubelet
        ```

        Optionally, from within the node, query the kubelet config endpoint and ensure anonymous access fails (or needs auth).

        ***

        If you share your specific OKE node OS image and version (Oracle Linux vs Ubuntu, managed vs custom), I can give you the exact file paths and edit commands for that image.
      </Accordion>

      <Accordion title="Using Python">
        To disable anonymous requests to the kubelet for Oracle OKE using Python, you need to:

        1. Configure the kubelet on each node pool to run with `--anonymous-auth=false` (and preferably disable the read-only port).
        2. Do this via an **OKE Node Pool update** using the OCI Python SDK (which will roll the nodes).

        Below is a minimal, end‑to‑end example.

        ***

        ## 1. Prerequisites

        * Python 3.x
        * OCI Python SDK:
          ```bash theme={null}
          pip install oci
          ```
        * An OCI config file (`~/.oci/config`) with a profile that has:
          * Permission to `MANAGE` clusters and node pools in the target compartment.
        * Your OKE **cluster OCID** and/or **node pool OCIDs**.

        ***

        ## 2. High-level actions

        For each node pool in your OKE cluster:

        1. Get the node pool definition.
        2. Update its kubelet configuration to:
           * `anonymous-auth = false`
           * (Optionally) `read-only-port = 0` for extra hardening.
        3. Call `UpdateNodePool` via the OCI Container Engine client.
        4. Wait for the node pool to complete its rolling update.

        ***

        ## 3. Example Python script

        This example:

        * Lists node pools for a given cluster.
        * Updates each node pool’s kubelet config to disable anonymous auth.

        Adjust values in the `CONFIG` section.

        ```python theme={null}
        import oci
        from oci.container_engine import ContainerEngineClient
        from oci.container_engine.models import (
            UpdateNodePoolDetails,
            NodePoolNodeConfigDetails,
            KubeletConfig
        )

        # ---------------- CONFIG ----------------
        OCI_PROFILE = "DEFAULT"  # profile from ~/.oci/config
        CLUSTER_OCID = "<your_cluster_ocid>"  # e.g. ocid1.cluster.oc1...

        # If you already know a specific node pool OCID, you can skip cluster listing
        # and just put it in NODE_POOL_OCIDS and comment out the listing logic.
        NODE_POOL_OCIDS = []  # leave empty to fetch all node pools from the cluster
        COMPARTMENT_OCID = "<compartment_ocid>"  # only needed if discovering node pools
        # ----------------------------------------


        def get_container_engine_client():
            config = oci.config.from_file(profile_name=OCI_PROFILE)
            return ContainerEngineClient(config)


        def list_node_pools_for_cluster(client, cluster_id, compartment_id):
            node_pools = []
            list_resp = client.list_node_pools(
                compartment_id=compartment_id,
                cluster_id=cluster_id
            )
            node_pools.extend(list_resp.data)

            # Handle pagination if needed:
            while list_resp.has_next_page:
                list_resp = client.list_node_pools(
                    compartment_id=compartment_id,
                    cluster_id=cluster_id,
                    page=list_resp.next_page
                )
                node_pools.extend(list_resp.data)

            return node_pools


        def update_node_pool_kubelet_config(client, node_pool_id):
            # Get current node pool
            np = client.get_node_pool(node_pool_id).data

            # Build new kubelet config
            # Here we explicitly disable anonymous-auth
            # and optionally disable the read-only port.
            kubelet_config = KubeletConfig(
                anonymous_auth=False,
                # Optional hardening:
                # read_only_port_enabled=False
            )

            # Keep existing node config details but override kubelet_config
            current_node_config = np.node_config_details
            if current_node_config is None:
                current_node_config = NodePoolNodeConfigDetails()

            new_node_config = NodePoolNodeConfigDetails(
                size=current_node_config.size,
                nsg_ids=current_node_config.nsg_ids,
                subnet_ids=current_node_config.subnet_ids,
                # IMPORTANT: set the kubelet config here:
                kubelet_config=kubelet_config,
                # Preserve other fields if you’re using them:
                placement_configs=current_node_config.placement_configs,
                ssh_public_key=current_node_config.ssh_public_key,
                is_pv_encryption_in_transit_enabled=getattr(
                    current_node_config, "is_pv_encryption_in_transit_enabled", None
                )
            )

            update_details = UpdateNodePoolDetails(
                name=np.name,
                kubernetes_version=np.kubernetes_version,
                node_shape=np.node_shape,
                node_config_details=new_node_config,
                node_eviction_node_pool_settings=np.node_eviction_node_pool_settings,
                initial_node_labels=np.initial_node_labels
            )

            print(f"Updating node pool {node_pool_id} to disable kubelet anonymous-auth...")
            resp = client.update_node_pool(node_pool_id=node_pool_id, update_node_pool_details=update_details)

            work_request_id = resp.headers.get('opc-work-request-id')
            print(f"Submitted update. Work request: {work_request_id}")

            # Optionally, wait for completion
            if work_request_id:
                work_request_client = oci.container_engine.ContainerEngineClientCompositeOperations(client)
                print("Waiting for node pool update to complete...")
                work_request_client.wait_for_work_request(work_request_id)
                print(f"Node pool {node_pool_id} update completed.")

            return resp


        def main():
            client = get_container_engine_client()

            # Discover node pools if not explicitly provided
            node_pool_ids = NODE_POOL_OCIDS.copy()
            if not node_pool_ids:
                node_pools = list_node_pools_for_cluster(
                    client,
                    cluster_id=CLUSTER_OCID,
                    compartment_id=COMPARTMENT_OCID
                )
                node_pool_ids = [np.id for np in node_pools]

            if not node_pool_ids:
                print("No node pools found to update.")
                return

            for np_id in node_pool_ids:
                update_node_pool_kubelet_config(client, np_id)


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

        ***

        ## 4. Notes

        * Updating node pools this way typically triggers a **rolling replacement** of nodes; plan for impact.
        * Ensure your OKE version/API supports `kubeletConfig` on node pools; if it doesn’t, you must instead use a custom cloud-init or image that sets `--anonymous-auth=false` on kubelet before OKE manages the node.
        * After completion, validate on a node:
          ```bash theme={null}
          ps aux | grep kubelet | grep anonymous-auth
          # should show --anonymous-auth=false
          ```

        If you share your current OKE version and a sample `get_node_pool` output, I can tailor the exact fields to your environment.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        resource "oci_containerengine_node_pool" "OKE_NODEPOOL" {
          # Replace OKE_NODEPOOL with your node pool name
          # Set these to match your existing configuration
          cluster_id        = oci_containerengine_cluster.OKE_CLUSTER.id
          compartment_id    = var.COMPartment_OCID
          kubernetes_version = "YOUR_K8S_VERSION"
          name              = "YOUR_NODEPOOL_NAME"
          node_shape        = "YOUR_NODE_SHAPE"

          # ... other existing arguments like node_config_details, initial_node_labels, etc.

          kubelet_config {
            # Disable anonymous requests to the kubelet server
            is_anonymous_auth_enabled = false
          }
        }
        ```

        Changing `kubelet_config.is_anonymous_auth_enabled` does not force replacement of the node pool resource itself, but OKE will roll nodes to apply the new kubelet configuration (expect node recreation / disruption during the rollout).

        To verify, `terraform plan` should show an in-place update on `oci_containerengine_node_pool.OKE_NODEPOOL` with `kubelet_config.is_anonymous_auth_enabled` changing from `true` (or null) to `false`.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
