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
Remediation
Manual Steps
-
On every worker node, SSH in and confirm how kubelet is configured and where its config file is:
ps -ef | grep kubeletIf you see a
--config=argument, note the full path (for example,/var/lib/kubelet/config.yamlor/etc/kubernetes/kubelet.conf). If there is no--configargument, you will use the systemd unit/args file instead. -
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.yamlUnder the
authentication:section, ensure it contains:authentication:anonymous:enabled: falseIf the
authenticationoranonymoussections are missing, add them as above, respecting YAML indentation. -
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.confIn the line defining kubelet arguments (often
KUBELET_ARGS=or insideExecStart=), ensure--anonymous-auth=falseis present. For example:Environment="KUBELET_ARGS=... --anonymous-auth=false ..."or in
ExecStart=:ExecStart=/usr/bin/kubelet ... --anonymous-auth=false ... -
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-reloadsudo systemctl restart kubelet.servicesudo systemctl status kubelet -l -
Verify on every worker node that the kubelet is running and the change is active:
/bin/ps -fC kubeletInspect the output:
- If using args, confirm
--anonymous-auth=falseappears. - If using a config file, confirm the
--config=argument points to the file you edited.
- If using args, confirm
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."