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
Remediation
Manual Steps
-
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 -
On every control plane node, change the file owner and group to root:root:
sudo chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml -
(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 -
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/manifeststrigger static pod restarts.