Skip to main content

API Server Should Disable Profiling

More Info:

Verifies that the API server --profiling argument is set to false. Profiling exposes detailed system and program data that could aid an attacker.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, open the API server static pod manifest for editing:

    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
  2. In the command: or args: list for kube-apiserver, locate any existing --profiling flag and change it to:

    - --profiling=false

    If no --profiling flag is present, add this line under the other arguments, for example:

    spec:
    containers:
    - name: kube-apiserver
    command:
    - kube-apiserver
    - --profiling=false
    ...
  3. Save and exit the editor. The kubelet will automatically detect the manifest change and restart the kube-apiserver static pod; expect a brief API server disruption during this restart.

  4. After 30–60 seconds, verify on the same control plane node that the kube-apiserver process is now running with --profiling=false:

    ps -ef | grep kube-apiserver | grep -v grep
  5. In the command output, confirm that --profiling=false appears and that there is no --profiling=true flag. Repeat steps 1–4 on every control plane node.

Using kubectl

kubectl cannot modify the kube-apiserver static pod manifest or its process flags, so this finding cannot be fixed through the Kubernetes API. The change must be made directly on every control plane node in /etc/kubernetes/manifests/kube-apiserver.yaml; see the Manual Steps section for the exact procedure.

Automation
#!/usr/bin/env bash
#
# Remediate CISKubernetes 1.2.15:
# Ensure that the kube-apiserver --profiling argument is set to false.
#
# Run this script on every control plane node as root.
# It is safe to re-run; it will only adjust the --profiling flag as needed.
#
# Operational impact:
# - Editing /etc/kubernetes/manifests/kube-apiserver.yaml will trigger
# the kube-apiserver static pod to restart on this node.

set -euo pipefail

APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d%H%M%S)"

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

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

echo "Creating backup of ${APISERVER_MANIFEST} in ${BACKUP_DIR}..."
mkdir -p "$BACKUP_DIR"
cp -p "$APISERVER_MANIFEST" "${BACKUP_DIR}/kube-apiserver.yaml"

echo "Ensuring --profiling=false is configured..."

# Normalize existing --profiling flags:
# 1) Convert any explicit --profiling=true to --profiling=false
# 2) Remove any duplicate occurrences after the first corrected one

TMP_FILE="$(mktemp)"
trap 'rm -f "${TMP_FILE}"' EXIT

# Step 1: Replace any explicit --profiling=true with --profiling=false
# and keep existing --profiling=false as-is.
# We do not add the flag yet; that happens in Step 2.
sed -E \
-e 's/(--profiling)=[Tt][Rr][Uu][Ee]/\1=false/g' \
"$APISERVER_MANIFEST" > "${TMP_FILE}.step1"

# Step 2: Ensure exactly one --profiling=false exists in the container args list.
# Approach:
# - Remove all existing --profiling=... entries
# - Add a single "--profiling=false" as the last item in the args: list
# (or create args: section with it if missing)
awk '
BEGIN {
in_container = 0
in_args = 0
}
# Detect containers: section and container start
/containers:/ { print; next }
/- name: kube-apiserver/ {
in_container = 1
print
next
}
in_container == 1 && /- name:/ && $0 !~ /kube-apiserver/ {
# another container; end of kube-apiserver container
if (in_args == 1) {
# If we were in args, ensure we have added profiling flag
if (profiling_added == 0) {
print " - \"--profiling=false\""
}
in_args = 0
}
in_container = 0
profiling_added = 0
print
next
}
in_container == 1 && /args:/ {
in_args = 1
profiling_added = 0
print
next
}
in_container == 1 && in_args == 1 {
# Within args of kube-apiserver
if ($0 ~ /--profiling=/) {
# Skip all existing profiling flags
next
}
# Detect end of args list (next non-indented key or end of container)
if ($1 !~ /^-/ && $1 !~ /^ *-/ && $1 !~ /^ *\"--/ && $1 !~ /^ *\'--/ && $1 !~ /^ *- /) {
# We reached a new section; add profiling flag before this line
if (profiling_added == 0) {
print " - \"--profiling=false\""
profiling_added = 1
}
in_args = 0
print
next
}
# Normal args line; just print
print
next
}
{
print
}
END {
# No special END handling; if args: existed it was handled above
}
' "${TMP_FILE}.step1" > "${TMP_FILE}.step2"

# Step 3: If kube-apiserver container has no args: section at all,
# add one with --profiling=false
awk '
BEGIN {
in_container = 0
has_args = 0
}
/- name: kube-apiserver/ {
in_container = 1
has_args = 0
print
next
}
in_container == 1 && /args:/ {
has_args = 1
print
next
}
in_container == 1 && /- name:/ && $0 !~ /kube-apiserver/ {
# another container; end of kube-apiserver container
if (has_args == 0) {
print " args:"
print " - \"--profiling=false\""
}
in_container = 0
has_args = 0
print
next
}
{
print
}
END {
# If file ended while still in kube-apiserver container with no args
if (in_container == 1 && has_args == 0) {
print " args:"
print " - \"--profiling=false\""
}
}
' "${TMP_FILE}.step2" > "${TMP_FILE}"

# Move final result into place
cp "${TMP_FILE}" "$APISERVER_MANIFEST"

echo "Updated ${APISERVER_MANIFEST} with --profiling=false."
echo "kubelet will restart the kube-apiserver static pod automatically."

# Wait for kube-apiserver process to restart and reflect new flags
echo "Waiting for kube-apiserver to be running with --profiling=false..."
RETRY=30
SLEEP_SECONDS=5
SUCCESS=0

for i in $(seq 1 "$RETRY"); do
if /bin/ps -ef | grep kube-apiserver | grep -v grep >/dev/null 2>&1; then
if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- "--profiling=false" >/dev/null 2>&1; then
# Ensure no process still has --profiling=true
if ! /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- "--profiling=true" >/dev/null 2>&1; then
SUCCESS=1
break
fi
fi
fi
sleep "$SLEEP_SECONDS"
done

echo
echo "Verification output (ps -ef | grep kube-apiserver | grep -v grep):"
# Final verification command as per audit guidance
/bin/ps -ef | grep kube-apiserver | grep -v grep || true

if [[ "$SUCCESS" -eq 1 ]]; then
echo
echo "Result: kube-apiserver is running with --profiling=false and no --profiling=true flags detected."
exit 0
else
echo
echo "WARNING: kube-apiserver process does not yet show --profiling=false as expected."
echo "Inspect the manifest and kubelet status on this control plane node."
exit 1
fi