> ## 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 Authorization Mode Should Not Be AlwaysAllow

### More Info:

When kubelet authorization is set to AlwaysAllow, every authenticated request is authorized regardless of identity. Use Webhook authorization so kubelet defers to the API servers RBAC for explicit decisions.

### 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">
        Here’s how to remediate “Kubelet Authorization Mode = AlwaysAllow” on an OCI OKE cluster via the OCI Console.

        Because OKE is managed, you usually fix this at the **node pool** level. In many cases you **cannot change kubelet flags in-place**, so the safe pattern is:

        1. Create a new node pool with secure kubelet settings.
        2. Drain workloads from the old pool and delete it.

        Below are the steps.

        ***

        ### 1. Identify the affected node pool(s)

        1. Sign in to the **OCI Console**.
        2. In the left menu, go to **Developer Services → Kubernetes Clusters (OKE)**.
        3. Select the **compartment** and then click your **cluster**.
        4. Go to the **Node Pools** tab.
        5. For each node pool, note which ones were created with old/legacy settings (typically older pools are more likely using `AlwaysAllow`).

        > If you have OKE Terraform or scripts, you can also check there for kubelet extra args using `--authorization-mode=AlwaysAllow`.

        ***

        ### 2. Create a new node pool with secure kubelet authorization

        1. In the cluster details page, go to the **Node Pools** tab.
        2. Click **Create node pool**.
        3. Fill in the basics:
           * **Name**
           * **Kubernetes version** (match the existing cluster)
           * **Compartment**
           * **VCN / Subnets** (match existing topology)
        4. Under **Node configuration** / **Kubelet configuration** or **Security** section (names may vary slightly by OKE version/console UI):
           * Look for **Kubelet security** or **Kubelet authorization mode**.
           * Ensure **it is NOT set to “AlwaysAllow”**.
           * Select a secure option, typically:
             * `Webhook` (preferred in most modern setups), or
             * `RBAC` / `Webhook+RBAC` depending on the OKE UI.
        5. Keep any other settings (shape, boot volume, labels, taints, etc.) consistent with the old node pool, unless you intend to change them.
        6. Click **Create** and wait for the node pool status to become **Active** and all nodes become **Ready** in Kubernetes.

        ***

        ### 3. Move workloads from old node pool to new node pool

        From here, use `kubectl` or the OCI Cloud Shell (Console → **Developer Tools → Cloud Shell**):

        1. **Cordon** nodes in the *old* node pool so new pods don’t get scheduled there:

           ```bash theme={null}
           kubectl get nodes -l <label_that_identifies_old_pool>
           kubectl cordon <node-name-1> <node-name-2> ...
           ```

           * If you don’t have a label, you can list all nodes and identify them by node name or provider ID.

        2. **Drain** the nodes to move workloads:

           ```bash theme={null}
           kubectl drain <node-name-1> <node-name-2> ... \
             --ignore-daemonsets \
             --delete-emptydir-data
           ```

           * Ensure your workloads have **ReplicaSets/Deployments** or other controllers so pods are recreated on the new pool.

        3. Confirm pods are running on **new** nodes:

           ```bash theme={null}
           kubectl get pods -A -o wide
           ```

        ***

        ### 4. Delete the old node pool

        Back in the **OCI Console**:

        1. Go to **Developer Services → Kubernetes Clusters** → select your **cluster**.
        2. Open the **Node Pools** tab.
        3. Select the **old** node pool (the one assumed to have `AlwaysAllow`).
        4. Ensure that:
           * All pods have been drained or migrated.
           * Node pool is safe to remove.
        5. Click **Delete** and confirm.

        ***

        ### 5. (Optional) Enforce via IaC / templates

        If you manage OKE via Terraform/Resource Manager:

        * In the `oci_containerengine_node_pool` (or equivalent) resource:
          * Remove any kubelet `--authorization-mode=AlwaysAllow` from `node_config_details` → `kubelet_config` or `node_metadata/cloud-init`.
          * Explicitly set the correct kubelet mode if supported (e.g., Webhook).
        * Re-apply to ensure future node pools never use `AlwaysAllow`.

        ***

        If you can share your current OKE version and whether you manage node pools via the console only or also Terraform, I can give you the exact field names you’ll see in your UI.
      </Accordion>

      <Accordion title="Using CLI">
        For Oracle Container Engine for Kubernetes (OKE), you **cannot change the kubelet `--authorization-mode` from the OCI control plane or OCI CLI**. That flag is set on the worker node OS itself.

        What you can do depends on the type of worker node pool you use:

        ***

        ## 1. Managed OKE node pools (standard OKE worker nodes)

        On Oracle-managed node pools using Oracle platform images:

        * Kubelet is already configured with `authorization-mode=Webhook` by default on supported images.
        * You **cannot override kubelet flags via OCI CLI**; they are controlled by the node image and OKE.

        If a scanner is flagging `AlwaysAllow` on these nodes, it is usually:

        * A **false positive** from the scanner (e.g., assuming defaults, not actually inspecting kubelet flags), or
        * An **old, unsupported node image**.

        **Remediation path (OCI CLI)** is to replace or upgrade the node pool to a recent image:

        1. List node pools:
           ```bash theme={null}
           oci ce node-pool list \
             --compartment-id <compartment_ocid> \
             --cluster-id <cluster_ocid>
           ```

        2. Check the current image and node shape:
           ```bash theme={null}
           oci ce node-pool get --node-pool-id <node_pool_ocid>
           ```

        3. Create a new node pool with a current platform image (which has kubelet Webhook mode):
           ```bash theme={null}
           oci ce node-pool create \
             --compartment-id <compartment_ocid> \
             --cluster-id <cluster_ocid> \
             --name <new_nodepool_name> \
             --kubernetes-version <same_as_cluster_version> \
             --node-shape <shape> \
             --node-config-details file://node-config.json
           ```
           Where `node-config.json` contains subnet, availability domains, and uses a current **platform image**.

        4. Cordon & drain old nodes, then delete the old node pool:
           ```bash theme={null}
           kubectl cordon <old_nodes>
           kubectl drain <old_nodes> --ignore-daemonsets --delete-emptydir-data

           oci ce node-pool delete \
             --node-pool-id <old_node_pool_ocid> \
             --force
           ```

        This is the only practical way (via OCI) to “remediate” kubelet flags for managed node pools.

        ***

        ## 2. Custom / self-managed nodes attached to OKE

        If you created your own worker nodes (e.g., via kubeadm on OCI Compute instances and then joined them to an OKE cluster), **you must fix kubelet on the node OS**. OCI CLI cannot change that; you use SSH + OS tools.

        On each node:

        1. SSH in (OCI CLI to get IPs if needed):
           ```bash theme={null}
           oci compute instance list --compartment-id <compartment_ocid>
           # then ssh opc@<public_ip>
           ```

        2. Check kubelet arguments:
           ```bash theme={null}
           ps -p $(pidof kubelet) -o args=
           ```
           If you see `--authorization-mode=AlwaysAllow`, it must be removed/changed.

        3. If kubelet uses a config file (`/var/lib/kubelet/config.yaml`), ensure it has:
           ```yaml theme={null}
           authorization:
             mode: Webhook
           ```

        4. Remove or fix the `--authorization-mode` flag in the systemd drop-in, commonly:
           ```bash theme={null}
           sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
           ```
           Ensure the `KUBELET_KUBECONFIG_ARGS` / `KUBELET_CONFIG_ARGS` line **does not** set `--authorization-mode=AlwaysAllow`. If you must specify, set:
           ```text theme={null}
           --authorization-mode=Webhook
           ```

        5. Reload and restart:
           ```bash theme={null}
           sudo systemctl daemon-reload
           sudo systemctl restart kubelet
           sudo systemctl status kubelet
           ```

        6. Re-verify:
           ```bash theme={null}
           ps -p $(pidof kubelet) -o args=
           ```

        ***

        ### Key point

        * OCI CLI can manage **node pools and instances**, not **kubelet flags**.
        * For OKE-managed nodes: fix by upgrading/recreating node pools with up-to-date images.
        * For custom/self-managed nodes: fix kubelet on the host (SSH + systemd/config), then let OKE continue to use the node normally.
      </Accordion>

      <Accordion title="Using Python">
        Below is a practical way to remediate “Kubelet Authorization Mode Should Not Be AlwaysAllow” for OCI OKE using Python, assuming:

        * You manage the worker node OS (custom node pool or self-managed nodes).
        * Kubelet is started via systemd with a config file containing `--authorization-mode=AlwaysAllow`.

        If you are using fully managed OKE workers where Oracle controls kubelet flags, you **cannot** override `--authorization-mode` directly; you must instead:

        * Move to a supported OKE version/node image that no longer uses `AlwaysAllow`, or
        * Use custom worker nodes where you control kubelet startup.

        ***

        ## 1. What needs to change

        On each worker node:

        1. Find the kubelet systemd unit config (commonly one of):
           * `/etc/systemd/system/kubelet.service.d/10-kubeadm.conf`
           * `/etc/systemd/system/kubelet.service`
        2. Locate the line that has `--authorization-mode=AlwaysAllow`.
        3. Replace it with a more secure mode, e.g.:

        ```text theme={null}
        --authorization-mode=Webhook
        ```

        (or `--authorization-mode=Webhook,Node` in some environments, if supported)

        4. Restart kubelet:

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

        ***

        ## 2. High‑level Python approach

        1. Use the OCI Python SDK to:
           * List clusters
           * List node pools
           * Get node instances (instance OCIDs)
        2. SSH into each node (e.g., using `paramiko`) and:
           * Edit the kubelet unit/config file
           * Reload systemd
           * Restart kubelet

        Below is example code to illustrate the flow.

        ***

        ## 3. Example Python script (OCI SDK + Paramiko)

        Install dependencies:

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

        Python code:

        ```python theme={null}
        import oci
        import paramiko
        import io
        import re

        # -------------------------------------------------------------------
        # CONFIGURATION
        # -------------------------------------------------------------------
        COMPARTMENT_OCID = "<your_compartment_ocid>"
        CLUSTER_OCID = "<your_cluster_ocid>"        # optional filter
        SSH_USERNAME = "opc"                        # or ubuntu, etc.
        SSH_KEY_PATH = "/path/to/your/private_key"  # key with access to worker nodes
        KUBELET_UNIT_FILES = [
            "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf",
            "/etc/systemd/system/kubelet.service"
        ]
        NEW_AUTHZ_MODE = "Webhook"  # or "Webhook,Node" if supported
        # -------------------------------------------------------------------


        def get_oci_client(config_file="~/.oci/config", profile="DEFAULT"):
            config = oci.config.from_file(config_file, profile_name=profile)
            container_engine_client = oci.container_engine.ContainerEngineClient(config)
            compute_client = oci.core.ComputeClient(config)
            return container_engine_client, compute_client


        def list_node_instances(container_engine_client, compute_client, compartment_ocid, cluster_ocid=None):
            """Return list of (instance_ocid, public_ip) for worker nodes."""
            # 1) List node pools in compartment (optionally filter by cluster)
            node_pools = oci.pagination.list_call_get_all_results(
                container_engine_client.list_node_pools,
                compartment_id=compartment_ocid
            ).data

            if cluster_ocid:
                node_pools = [np for np in node_pools if np.cluster_id == cluster_ocid]

            node_instance_details = []

            # 2) For each node pool, list nodes
            for node_pool in node_pools:
                nodes = oci.pagination.list_call_get_all_results(
                    container_engine_client.list_node_pool_nodes,
                    node_pool_id=node_pool.id
                ).data

                for node in nodes:
                    # node is oci.container_engine.models.Node
                    # Has properties like node.id, node.node_name, node.public_ip, node.private_ip, etc.
                    if node.public_ip:
                        node_instance_details.append((node.id, node.public_ip))

            return node_instance_details


        def ssh_connect(hostname, username, key_path):
            key = paramiko.RSAKey.from_private_key_file(key_path)
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(hostname=hostname, username=username, pkey=key)
            return client


        def modify_kubelet_unit_over_ssh(ssh_client, unit_files, new_authz_mode):
            # Try each potential kubelet unit file until one is found
            for unit_file in unit_files:
                # Check if file exists
                stdin, stdout, stderr = ssh_client.exec_command(f"test -f {unit_file} && echo FOUND || echo MISSING")
                if stdout.read().decode().strip() != "FOUND":
                    continue

                # Read the file
                sftp = ssh_client.open_sftp()
                with sftp.open(unit_file, "r") as f:
                    content = f.read().decode()

                # Replace --authorization-mode=AlwaysAllow with new mode
                new_content, count = re.subn(
                    r'--authorization-mode=AlwaysAllow',
                    f'--authorization-mode={new_authz_mode}',
                    content
                )

                if count == 0:
                    # No AlwaysAllow found, nothing to change in this file
                    sftp.close()
                    continue

                # Write updated content
                with sftp.open(unit_file, "w") as f:
                    f.write(new_content)

                sftp.close()

                # Reload systemd and restart kubelet
                for cmd in [
                    "sudo systemctl daemon-reload",
                    "sudo systemctl restart kubelet"
                ]:
                    stdin, stdout, stderr = ssh_client.exec_command(cmd)
                    exit_status = stdout.channel.recv_exit_status()
                    if exit_status != 0:
                        raise RuntimeError(f"Command failed on {unit_file}: {cmd}\n{stderr.read().decode()}")

                # If we successfully modified this file, stop searching
                return True

            return False


        def main():
            container_engine_client, compute_client = get_oci_client()

            nodes = list_node_instances(
                container_engine_client=container_engine_client,
                compute_client=compute_client,
                compartment_ocid=COMPARTMENT_OCID,
                cluster_ocid=CLUSTER_OCID
            )

            print(f"Found {len(nodes)} worker nodes")

            for node_id, public_ip in nodes:
                print(f"Processing node {node_id} at {public_ip}")
                ssh_client = ssh_connect(public_ip, SSH_USERNAME, SSH_KEY_PATH)

                try:
                    changed = modify_kubelet_unit_over_ssh(
                        ssh_client,
                        KUBELET_UNIT_FILES,
                        NEW_AUTHZ_MODE
                    )
                    if changed:
                        print(f"Updated kubelet authorization mode on node {node_id}")
                    else:
                        print(f"No AlwaysAllow found in kubelet config on node {node_id}")
                finally:
                    ssh_client.close()


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

        ***

        ## 4. Operational notes

        * Run this during a maintenance window; restarting kubelet can briefly disrupt pods on that node.
        * Test on a single node first.
        * If kubelet is configured via a different mechanism (cloud‑init, custom scripts, or kubeadm config files), adapt the file path and replacement logic accordingly.
        * For managed node pools where Oracle controls the kubelet flags and you cannot SSH or edit systemd units, remediation is not possible via Python; you must change to a configuration/image/version that does not use `AlwaysAllow`.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # OCI does not currently expose kubelet authorization mode for OKE node pools
        # in the Terraform oci_containerengine_node_pool resource, so this setting
        # cannot be remediated or enforced via Terraform.

        # You must change this in the OCI Console or via an OCI API/CLI that supports
        # kubelet authorization mode on the node pool:
        # - Navigate to Container Clusters (OKE) → your Cluster → Node Pools
        # - Edit or recreate the node pool
        # - In advanced Kubelet settings, change authorization mode from AlwaysAllow
        #   to Webhook so that kubelet defers to API server RBAC.

        # Because this field is not in the Terraform provider schema, adding it to
        # Terraform would be ignored or cause an error; there is no valid argument
        # name to set here.

        # Verification with Terraform:
        # - `terraform plan` will show no diffs related to kubelet authorization mode,
        #   because that setting is not managed by the provider.
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
