Skip to main content

API Server Pod Specification File Ownership Should Be

More Info:

Verifies that the kube-apiserver pod manifest file is owned by root:root. Correct ownership ensures only privileged users can modify control plane configuration.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, confirm the kube-apiserver manifest file exists and note its current ownership:

    sudo ls -l /etc/kubernetes/manifests/kube-apiserver.yaml
  2. On every control plane node, change the file owner and group to root:root:

    sudo chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml
  3. (Optional) Confirm file permissions are still appropriate (read-only change, no restart impact):

    sudo stat -c 'File: %n Owner: %U Group: %G Mode: %a' /etc/kubernetes/manifests/kube-apiserver.yaml
  4. Verify the fix on every control plane node using the audit command:

    /bin/sh -c 'if test -e /etc/kubernetes/manifests/kube-apiserver.yaml; then stat -c %U:%G /etc/kubernetes/manifests/kube-apiserver.yaml; fi'

    The output must be:

    root:root
Using kubectl

kubectl cannot change file ownership on control plane nodes, so it cannot be used to remediate this finding on /etc/kubernetes/manifests/kube-apiserver.yaml. To fix this, you must adjust file ownership directly on every control plane node; see the Manual Steps section for the exact commands.

Automation
#!/usr/bin/env bash
#
# Fix ownership of kube-apiserver manifest to root:root on all control plane nodes.
# Usage:
# 1) Copy this script to each control plane node and run with sudo:
# sudo bash fix-kube-apiserver-ownership.sh
# 2) Or run remotely via SSH from an admin host.

set -euo pipefail

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

echo "=== [$(hostname)] Ensuring ownership of ${MANIFEST_PATH} is root:root ==="

# Ensure script is run as root
if [[ "$(id -u)" -ne 0 ]]; then
echo "ERROR: This script must be run as root (use sudo)." >&2
exit 1
fi

# Check if file exists
if [[ ! -e "${MANIFEST_PATH}" ]]; then
echo "NOTICE: ${MANIFEST_PATH} does not exist on this node. Nothing to do."
exit 0
fi

# Show current ownership
current_owner_group="$(stat -c '%U:%G' "${MANIFEST_PATH}")"
echo "Current ownership: ${current_owner_group}"

# Apply fix only if needed (idempotent)
if [[ "${current_owner_group}" != "root:root" ]]; then
echo "Changing ownership to root:root..."
chown root:root "${MANIFEST_PATH}"
else
echo "Ownership already set to root:root. No change needed."
fi

# Verification
echo "Verifying ownership..."
verified_owner_group="$(stat -c '%U:%G' "${MANIFEST_PATH}")"
echo "Verified ownership: ${verified_owner_group}"

if [[ "${verified_owner_group}" != "root:root" ]]; then
echo "ERROR: Failed to set ownership to root:root on ${MANIFEST_PATH}" >&2
exit 2
fi

echo "SUCCESS: ${MANIFEST_PATH} is owned by root:root on node $(hostname)."

Operational notes:

  • Run on: every control plane node.
  • Changing ownership of this manifest does not restart the kube-apiserver by itself; only content changes to files under /etc/kubernetes/manifests trigger static pod restarts.