Skip to main content

API Server Pod Specification File Permissions Should Be 600

More Info:

Verifies that the kube-apiserver pod manifest file has permissions of 600 or more restrictive. Restrictive permissions prevent unauthorized users from tampering with control plane configuration.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, check the current permissions of the API server manifest file:

    stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml
  2. If the permissions are more permissive than 600 (for example, 644 or 640), set them to 600:

    chmod 600 /etc/kubernetes/manifests/kube-apiserver.yaml
  3. Confirm the file owner is root (recommended). If not, set it:

    chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml
  4. Be aware: changing this static pod manifest file may trigger the kubelet to restart the kube-apiserver pod on this control plane node. Plan to do this one node at a time if running a multi-node control plane.

  5. Re-verify the permissions are now 600:

    stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml
Using kubectl

kubectl cannot modify file permissions on control plane nodes, so it cannot be used to fix /etc/kubernetes/manifests/kube-apiserver.yaml. This must be corrected directly on every control plane node’s filesystem; see the Manual Steps section for how to update the permissions and verify the fix.

Automation
#!/usr/bin/env bash
#
# Harden kube-apiserver pod manifest permissions on all control plane nodes.
# Run this script on each control plane node (e.g., via SSH or Ansible shell module).
#
# Requirements:
# - Run as root (or with sudo) on the control plane node.

set -euo pipefail

MANIFEST_PATH="/etc/kubernetes/manifests/kube-apiserver.yaml"
REQUIRED_MODE="600"

echo "==> Checking for kube-apiserver manifest at ${MANIFEST_PATH}"

if [ ! -e "${MANIFEST_PATH}" ]; then
echo "WARNING: ${MANIFEST_PATH} does not exist on this node. Skipping."
exit 0
fi

current_mode="$(stat -c '%a' "${MANIFEST_PATH}")"
echo "Current permissions: ${current_mode}"

# Normalize modes to 3 digits for comparison
pad_mode() {
local m="$1"
printf "%03d" "${m}"
}

current_mode_padded="$(pad_mode "${current_mode}")"
required_mode_padded="$(pad_mode "${REQUIRED_MODE}")"

# Convert octal modes to decimal to compare "restrictiveness"
octal_to_decimal() {
local o="$1"
echo "ibase=8; ${o}" | bc
}

current_dec="$(octal_to_decimal "${current_mode_padded}")"
required_dec="$(octal_to_decimal "${required_mode_padded}")"

# If current mode is numerically greater (less restrictive) than required, update it.
if [ "${current_dec}" -gt "${required_dec}" ]; then
echo "Permissions are too permissive; setting to ${REQUIRED_MODE}"
chmod "${REQUIRED_MODE}" "${MANIFEST_PATH}"
else
# Even if already restrictive (e.g., 400), we leave as is for idempotency.
echo "Permissions are already ${current_mode} which is as restrictive or more restrictive than ${REQUIRED_MODE}; no change needed."
fi

echo "==> Verifying final permissions:"
/bin/sh -c 'if test -e /etc/kubernetes/manifests/kube-apiserver.yaml; then stat -c permissions=%a /etc/kubernetes/manifests/kube-apiserver.yaml; fi'