Skip to main content

Kubelet Anonymous Auth Argument Set To False

More Info:

Anonymous authentication allows unauthenticated requests to reach the kubelet API. Setting --anonymous-auth to false ensures every request must be authenticated.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, confirm the current kube-apiserver arguments:

    ps -ef | grep kube-apiserver | grep -v grep
  2. On every control plane node, back up the existing manifest:

    sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak
  3. Edit the kube-apiserver static pod manifest to set anonymous auth to false:

    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml

    In the containers:- name: kube-apiservercommand: list, ensure there is a line exactly like:

    - --anonymous-auth=false

    If a --anonymous-auth= flag already exists with a different value, change it to false. Save and exit.
    Note: updating this file will cause the kube-apiserver static pod to restart automatically.

  4. (If flags are passed via args: instead of command:) ensure the flag is present or corrected under args::

    args:
    - kube-apiserver
    - --anonymous-auth=false
  5. Wait for the kube-apiserver pod to restart and become Ready (from any machine with kubectl access):

    kubectl get pods -n kube-system -l component=kube-apiserver -w
  6. Verify on every control plane node that the process now includes --anonymous-auth=false:

    ps -ef | grep kube-apiserver | grep -v grep | grep -- '--anonymous-auth=false'
Using kubectl

kubectl cannot modify kube-apiserver process flags or static pod manifests, so it cannot be used to set --anonymous-auth=false for this control-plane component. To address this finding, you must edit /etc/kubernetes/manifests/kube-apiserver.yaml directly on every control plane node; follow the steps in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Purpose: Ensure kube-apiserver is started with --anonymous-auth=false
# Scope: Run on every control plane node (with root privileges)
#
# Idempotent: safe to re-run; only updates when needed.

set -euo pipefail

APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d)"
FLAG="--anonymous-auth="
DESIRED_VALUE="false"

echo "[INFO] Ensuring ${FLAG}${DESIRED_VALUE} on this control plane node"

if [[ $EUID -ne 0 ]]; then
echo "[ERROR] This script must be run as root." >&2
exit 1
fi

if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
echo "[ERROR] kube-apiserver manifest not found at ${APISERVER_MANIFEST}" >&2
exit 1
fi

mkdir -p "${BACKUP_DIR}"

# Backup manifest once per day (idempotent by path+date)
BACKUP_FILE="${BACKUP_DIR}/kube-apiserver.yaml"
if [[ ! -f "${BACKUP_FILE}" ]]; then
cp "${APISERVER_MANIFEST}" "${BACKUP_FILE}"
echo "[INFO] Backup created at ${BACKUP_FILE}"
else
echo "[INFO] Backup already exists at ${BACKUP_FILE}"
fi

TMP_FILE="$(mktemp)"

# Normalize file with a trailing newline to make sed safer
# 1. If --anonymous-auth is present, force it to false.
# 2. If not present, add it as a separate arg line under 'command:' for kube-apiserver.
if grep -qE '\s--anonymous-auth=' "${APISERVER_MANIFEST}"; then
echo "[INFO] Existing --anonymous-auth flag found; updating to ${DESIRED_VALUE} if needed"
sed -E "s/(^(\s*-\s*|\s*)--anonymous-auth=)(true|false)/\1${DESIRED_VALUE}/" \
"${APISERVER_MANIFEST}" > "${TMP_FILE}"
else
echo "[INFO] No --anonymous-auth flag found; adding --anonymous-auth=${DESIRED_VALUE}"

# Add to the first 'kube-apiserver' container command list
# This assumes the standard static pod manifest structure.
# - name: kube-apiserver
# command:
# - kube-apiserver
# - ...
awk -v flag="--anonymous-auth=${DESIRED_VALUE}" '
/- name: kube-apiserver/ { in_apiserver=1 }
in_apiserver && /command:/ { in_command=1 }
in_command && /^\s*-/ && !added && !/- kube-apiserver/ {
print " - " flag
added=1
}
{ print }
' "${APISERVER_MANIFEST}" > "${TMP_FILE}"

# Fallback: if the above logic failed to insert the flag (e.g. nonstandard layout),
# append it at the end of the kube-apiserver command list.
if ! grep -q "${FLAG}${DESIRED_VALUE}" "${TMP_FILE}"; then
echo "[INFO] Fallback: appending flag near existing kube-apiserver args"
sed -E '
/- kube-apiserver/ {
p=1
}
p && /^\s*-/ && !seen && !/- kube-apiserver/ {
print " - --anonymous-auth='${DESIRED_VALUE}'"
seen=1
}
{ print }
' "${APISERVER_MANIFEST}" > "${TMP_FILE}.2" || true

if grep -q "${FLAG}${DESIRED_VALUE}" "${TMP_FILE}.2" 2>/dev/null; then
mv "${TMP_FILE}.2" "${TMP_FILE}"
else
rm -f "${TMP_FILE}.2" 2>/dev/null || true
fi
fi
fi

# If no change, exit cleanly
if cmp -s "${APISERVER_MANIFEST}" "${TMP_FILE}"; then
echo "[INFO] No changes required; manifest already enforces ${FLAG}${DESIRED_VALUE}"
rm -f "${TMP_FILE}"
else
echo "[INFO] Updating ${APISERVER_MANIFEST} (this will restart kube-apiserver static pod)"
mv "${TMP_FILE}" "${APISERVER_MANIFEST}"
fi

# Wait for kube-apiserver to restart and stabilize
echo "[INFO] Waiting for kube-apiserver process with ${FLAG}${DESIRED_VALUE} ..."
RETRIES=30
SLEEP_SECONDS=5
SUCCESS=0

for i in $(seq 1 "${RETRIES}"); do
if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q "${FLAG}${DESIRED_VALUE}"; then
SUCCESS=1
break
fi
echo "[INFO] Attempt ${i}/${RETRIES}: kube-apiserver not yet running with ${FLAG}${DESIRED_VALUE}, retrying in ${SLEEP_SECONDS}s..."
sleep "${SLEEP_SECONDS}"
done

echo "[INFO] Verification (matches audit command):"
/bin/ps -ef | grep kube-apiserver | grep -v grep || true

if [[ "${SUCCESS}" -ne 1 ]]; then
echo "[ERROR] kube-apiserver did not come up with ${FLAG}${DESIRED_VALUE} within timeout." >&2
exit 2
fi

echo "[INFO] kube-apiserver is running with ${FLAG}${DESIRED_VALUE} on this control plane node."
echo "[INFO] Repeat this script on each remaining control plane node."