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
- Remediation
Remediation
Using Console
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:
- Create a new node pool with secure kubelet settings.
- Drain workloads from the old pool and delete it.
Below are the steps.
1. Identify the affected node pool(s)
- Sign in to the OCI Console.
- In the left menu, go to Developer Services → Kubernetes Clusters (OKE).
- Select the compartment and then click your cluster.
- Go to the Node Pools tab.
- 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
- In the cluster details page, go to the Node Pools tab.
- Click Create node pool.
- Fill in the basics:
- Name
- Kubernetes version (match the existing cluster)
- Compartment
- VCN / Subnets (match existing topology)
- 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), orRBAC/Webhook+RBACdepending on the OKE UI.
- Keep any other settings (shape, boot volume, labels, taints, etc.) consistent with the old node pool, unless you intend to change them.
- 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):
-
Cordon nodes in the old node pool so new pods don’t get scheduled there:
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.
-
Drain the nodes to move workloads:
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.
-
Confirm pods are running on new nodes:
kubectl get pods -A -o wide
4. Delete the old node pool
Back in the OCI Console:
- Go to Developer Services → Kubernetes Clusters → select your cluster.
- Open the Node Pools tab.
- Select the old node pool (the one assumed to have
AlwaysAllow). - Ensure that:
- All pods have been drained or migrated.
- Node pool is safe to remove.
- 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=AlwaysAllowfromnode_config_details→kubelet_configornode_metadata/cloud-init. - Explicitly set the correct kubelet mode if supported (e.g., Webhook).
- Remove any kubelet
- 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.
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=Webhookby 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:
-
List node pools:
oci ce node-pool list \--compartment-id <compartment_ocid> \--cluster-id <cluster_ocid> -
Check the current image and node shape:
oci ce node-pool get --node-pool-id <node_pool_ocid> -
Create a new node pool with a current platform image (which has kubelet Webhook mode):
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.jsonWhere
node-config.jsoncontains subnet, availability domains, and uses a current platform image. -
Cordon & drain old nodes, then delete the old node pool:
kubectl cordon <old_nodes>kubectl drain <old_nodes> --ignore-daemonsets --delete-emptydir-dataoci 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:
-
SSH in (OCI CLI to get IPs if needed):
oci compute instance list --compartment-id <compartment_ocid># then ssh opc@<public_ip> -
Check kubelet arguments:
ps -p $(pidof kubelet) -o args=If you see
--authorization-mode=AlwaysAllow, it must be removed/changed. -
If kubelet uses a config file (
/var/lib/kubelet/config.yaml), ensure it has:authorization:mode: Webhook -
Remove or fix the
--authorization-modeflag in the systemd drop-in, commonly:sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.confEnsure the
KUBELET_KUBECONFIG_ARGS/KUBELET_CONFIG_ARGSline does not set--authorization-mode=AlwaysAllow. If you must specify, set:--authorization-mode=Webhook -
Reload and restart:
sudo systemctl daemon-reloadsudo systemctl restart kubeletsudo systemctl status kubelet -
Re-verify:
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.
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:
- Find the kubelet systemd unit config (commonly one of):
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf/etc/systemd/system/kubelet.service
- Locate the line that has
--authorization-mode=AlwaysAllow. - Replace it with a more secure mode, e.g.:
--authorization-mode=Webhook
(or --authorization-mode=Webhook,Node in some environments, if supported)
- Restart kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
2. High‑level Python approach
- Use the OCI Python SDK to:
- List clusters
- List node pools
- Get node instances (instance OCIDs)
- 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:
pip install oci paramiko
Python code:
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.
Using Terraform
# 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.