Skip to main content

Ensure That Anonymous Auth Is Not Enabled

More Info:

Anonymous authentication allows unauthenticated requests to the kubelet API. Disabling it ensures every request is authenticated, preventing anonymous access to node and workload data.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS EKS

Triage and Remediation

Remediation

Manual Steps
  1. On every worker node, SSH in and confirm how kubelet is configured and where its config file is:

    ps -ef | grep kubelet

    If you see a --config= argument, note the full path (for example, /var/lib/kubelet/config.yaml or /etc/kubernetes/kubelet.conf). If there is no --config argument, you will use the systemd unit/args file instead.

  2. If kubelet uses a config file (Method 1), edit it to disable anonymous auth (example uses /var/lib/kubelet/config.yaml; replace with the actual path you found):

    sudo vi /var/lib/kubelet/config.yaml

    Under the authentication: section, ensure it contains:

    authentication:
    anonymous:
    enabled: false

    If the authentication or anonymous sections are missing, add them as above, respecting YAML indentation.

  3. If kubelet instead uses executable arguments (Method 2), edit the systemd drop‑in on each worker node (EKS-optimized and similar systems):

    sudo vi /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf

    In the line defining kubelet arguments (often KUBELET_ARGS= or inside ExecStart=), ensure --anonymous-auth=false is present. For example:

    Environment="KUBELET_ARGS=... --anonymous-auth=false ..."

    or in ExecStart=:

    ExecStart=/usr/bin/kubelet ... --anonymous-auth=false ...
  4. On every worker node, reload systemd configuration and restart kubelet (this restarts the kubelet and may briefly impact node status/workload reporting):

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet.service
    sudo systemctl status kubelet -l
  5. Verify on every worker node that the kubelet is running and the change is active:

    /bin/ps -fC kubelet

    Inspect the output:

    • If using args, confirm --anonymous-auth=false appears.
    • If using a config file, confirm the --config= argument points to the file you edited.
Using kubectl

kubectl cannot change kubelet process flags or its on-node configuration file at /var/lib/kubelet/config.yaml, so this finding cannot be remediated via the Kubernetes API. Apply the fix directly on every worker node’s host OS as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Disable anonymous authentication on kubelet via config file on every worker node.
# Run this on each worker node over SSH (or via your automation tool of choice).
# Idempotent: safe to re-run.

set -euo pipefail

KUBELET_CONFIG="/var/lib/kubelet/config.yaml"
BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"

echo "[INFO] Checking for kubelet process..."
if ! /bin/ps -fC kubelet >/dev/null 2>&1; then
echo "[ERROR] kubelet process not found. Are you on a worker node?"
exit 1
fi

if [ ! -f "$KUBELET_CONFIG" ]; then
echo "[ERROR] Kubelet config file $KUBELET_CONFIG not found."
echo " This script only handles the config-file method."
echo " If your kubelet is configured via executable arguments, use:"
echo " sudo vi /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf"
echo " and add: --anonymous-auth=false , then restart kubelet."
exit 1
fi

echo "[INFO] Using kubelet config file: $KUBELET_CONFIG"

# Create a timestamped backup once per run
BACKUP_FILE="${KUBELET_CONFIG}.bak.${BACKUP_SUFFIX}"
cp "$KUBELET_CONFIG" "$BACKUP_FILE"
echo "[INFO] Backup created at $BACKUP_FILE"

# Ensure authentication and anonymous blocks exist and set enabled: false
# This edit is idempotent: repeated runs keep enabled: false.

TMP_FILE="$(mktemp)"
trap 'rm -f "$TMP_FILE"' EXIT

awk '
BEGIN {
in_auth = 0;
in_anon = 0;
}
/^authentication:/ {
in_auth = 1;
in_anon = 0;
}
in_auth && /^ *anonymous:/ {
in_anon = 1;
}
in_auth && /^ *[^ #]/ && !/^authentication:/ && !/^ *anonymous:/ {
# Leaving authentication block
in_auth = 0;
in_anon = 0;
}

{
print $0;
}

# If we just printed "authentication:" line and there is no anonymous block yet,
# we will add it later after scanning if needed (handled in END).
' "$KUBELET_CONFIG" > "$TMP_FILE"

# Now enforce/insert the anonymous.enabled: false block using a second pass
# to avoid complex single-pass logic.
python3 - "$TMP_FILE" << 'PYEOF'
import sys, io, re

path = sys.argv[1]
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()

out = []
i = 0
while i < len(lines):
line = lines[i]
# Detect "authentication:" at some indent
m_auth = re.match(r'^(\s*)authentication:\s*$', line)
if m_auth:
indent_auth = m_auth.group(1)
out.append(line)
i += 1

# Collect the block under authentication:
block = []
while i < len(lines):
l = lines[i]
# Next top-level or same/less indent non-empty key that is not part of this block
if re.match(r'^\S', l) and not l.startswith(indent_auth + " "):
break
# If indentation is less than auth indent and not blank/comment, we left the block
if (l.strip() and
len(l) - len(l.lstrip(" ")) < len(indent_auth)):
break
block.append(l)
i += 1

# Process block: ensure anonymous.enabled: false exists
block_text = "".join(block)
# If anonymous section already exists, rewrite/force enabled: false
if re.search(r'^\s*anonymous:\s*$', block_text, re.M):
new_block = []
j = 0
in_anon = False
anon_indent = ""
while j < len(block):
bl = block[j]
m_anon = re.match(r'^(\s*)anonymous:\s*$', bl)
if m_anon:
in_anon = True
anon_indent = m_anon.group(1)
new_block.append(bl)
j += 1
# Rewrite any enabled: line directly under this block
# and ensure one exists.
saw_enabled = False
sub = []
while j < len(block):
nl = block[j]
# Stop anon block when dedented or new sibling at same level
if (nl.strip() and
len(nl) - len(nl.lstrip(" ")) <= len(anon_indent) and
not re.match(r'^\s*#', nl)):
break
# Replace enabled line if found
if re.match(r'^\s*enabled:\s*', nl):
if not saw_enabled:
sub.append(f"{anon_indent} enabled: false\n")
saw_enabled = True
else:
sub.append(nl)
j += 1
if not saw_enabled:
sub.insert(0, f"{anon_indent} enabled: false\n")
new_block.extend(sub)
continue

new_block.append(bl)
j += 1
block = new_block
else:
# No anonymous: section; append one with enabled: false
# Place it at the end of the authentication block
if block and block[-1] and not block[-1].endswith("\n"):
block[-1] = block[-1] + "\n"
anon_indent = indent_auth + " "
block.append(f"{anon_indent}anonymous:\n")
block.append(f"{anon_indent} enabled: false\n")

out.extend(block)
continue

out.append(line)
i += 1

with open(path, "w", encoding="utf-8") as f:
f.writelines(out)
PYEOF

echo "[INFO] Updated $KUBELET_CONFIG to set authentication.anonymous.enabled: false"

echo "[INFO] Reloading systemd and restarting kubelet (this will restart the kubelet and may briefly impact node readiness)..."
if command -v systemctl >/dev/null 2>&1; then
sudo systemctl daemon-reload
sudo systemctl restart kubelet.service
sudo systemctl status kubelet.service -l --no-pager
else
echo "[WARN] systemctl not found. Restart kubelet using your OS service manager."
fi

echo "[INFO] Verifying kubelet process is running..."
/bin/ps -fC kubelet || {
echo "[ERROR] kubelet not running after restart."
exit 1
}

echo "[INFO] Verification: kubelet process details:"
/bin/ps -fC kubelet

echo "[INFO] Completed. Anonymous authentication is now disabled in $KUBELET_CONFIG."