Scheduler Should Disable Profiling
More Info:
Verifies that the scheduler --profiling argument is set to false so detailed profiling data is not exposed to potential attackers.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
SSH to each control plane node
ssh root@<control-plane-node-ip> -
Open the kube-scheduler static pod manifest for editing
vi /etc/kubernetes/manifests/kube-scheduler.yaml -
Set
--profiling=falsein the scheduler container args
In thecontainers:→- name: kube-scheduler→command:orargs:list, ensure there is an entry exactly like:- --profiling=falseIf a line
--profiling=true(or any--profiling=) exists, change it to--profiling=false. Save and exit the editor.
Operational impact: editing this file will cause the kubelet to restart thekube-schedulerstatic pod on this node. -
Wait for the kube-scheduler pod to restart and become Ready
From any machine with kubectl access:kubectl -n kube-system get pods -l component=kube-scheduler -wWait until the scheduler pod shows
STATUSasRunningandREADYas1/1, then Ctrl+C. -
Verify the scheduler process is running with
--profiling=false
On each control plane node:/bin/ps -ef | grep kube-scheduler | grep -v grepConfirm the output includes
--profiling=falseand does not include--profiling=true.
Using kubectl
kubectl cannot modify the kube-scheduler static pod manifest or its process flags on the node. To remediate this finding, you must edit /etc/kubernetes/manifests/kube-scheduler.yaml directly on every control plane node; see the Manual Steps section for exact host-level instructions.
Automation
#!/usr/bin/env bash
#
# Harden kube-scheduler profiling on all control plane nodes
# Scope: run on every control plane node (with root privileges)
#
# This script:
# - Ensures --profiling=false is set for kube-scheduler
# - Removes any conflicting --profiling flags
# - Relies on static pod manifest at /etc/kubernetes/manifests/kube-scheduler.yaml
# - Triggers kube-scheduler restart via manifest edit
# - Verifies result via process flags
#
set -euo pipefail
MANIFEST="/etc/kubernetes/manifests/kube-scheduler.yaml"
BACKUP_SUFFIX=".cis_sched_profiling.bak.$(date +%s)"
if [[ $EUID -ne 0 ]]; then
echo "ERROR: Run as root on each control plane node."
exit 1
fi
if [[ ! -f "$MANIFEST" ]]; then
echo "ERROR: $MANIFEST not found on this node."
exit 1
fi
echo "Backing up $MANIFEST to ${MANIFEST}${BACKUP_SUFFIX}"
cp -p "$MANIFEST" "${MANIFEST}${BACKUP_SUFFIX}"
# Idempotently enforce --profiling=false in the kube-scheduler manifest
# Strategy:
# 1. Remove any existing --profiling=... occurrences
# 2. Add --profiling=false as an argument in the container spec
TMP_MANIFEST="$(mktemp)"
cp "$MANIFEST" "$TMP_MANIFEST"
# Step 1: remove any existing --profiling=... arg lines
# Handles both:
# - --profiling=true
# - --profiling=false (to avoid duplicates)
# Applied only to 'args:' list items
sed -i '/^[[:space:]]*-[[:space:]]*--profiling=/d' "$TMP_MANIFEST"
# Step 2: ensure there is an args: section; then ensure --profiling=false is present
# This uses awk to inject the argument exactly once under the kube-scheduler container.
awk '
BEGIN { in_container=0; in_args=0; profiling_added=0 }
/name:[[:space:]]*kube-scheduler/ { in_container=1 }
in_container && /args:/ { in_args=1 }
in_container && in_args && /^[[:space:]]*-[[:space:]]*--/ && profiling_added==0 {
# We are in args list and see the first arg, inject profiling before it
indent = match($0, /-/) - 1
printf "%*s- --profiling=false\n", indent, ""
profiling_added=1
}
{ print }
in_container && in_args && !/^[[:space:]]*-/ && $0 !~ /args:/ { in_args=0 }
/image:/ && in_container && profiling_added==0 {
# Fallback: no args: block encountered; do nothing here
}
' "$TMP_MANIFEST" > "${TMP_MANIFEST}.awked"
mv "${TMP_MANIFEST}.awked" "$TMP_MANIFEST"
# If args: block does not exist at all, append one with --profiling=false
if ! grep -qE '^[[:space:]]*args:' "$TMP_MANIFEST"; then
# Append args under the kube-scheduler container definition
awk '
BEGIN { in_container=0; inserted=0 }
/name:[[:space:]]*kube-scheduler/ { in_container=1 }
{
print
if (in_container && /image:/ && inserted==0) {
# infer indentation from current line
match($0, /^[[:space:]]*/)
indent = RLENGTH + 2
printf "%*sargs:\n", indent-2, ""
printf "%*s- --profiling=false\n", indent, ""
inserted=1
}
}
' "$TMP_MANIFEST" > "${TMP_MANIFEST}.withargs"
mv "${TMP_MANIFEST}.withargs" "$TMP_MANIFEST"
fi
# Final safety: ensure at least one --profiling=false is present
if ! grep -q -- "--profiling=false" "$TMP_MANIFEST"; then
echo "ERROR: Failed to inject --profiling=false into $MANIFEST; restoring backup."
mv "${MANIFEST}${BACKUP_SUFFIX}" "$MANIFEST"
rm -f "$TMP_MANIFEST"
exit 1
fi
# Replace manifest (this will restart kube-scheduler static pod)
echo "Updating $MANIFEST (this will restart kube-scheduler)..."
cp "$TMP_MANIFEST" "$MANIFEST"
rm -f "$TMP_MANIFEST"
# Allow some time for kubelet to recreate the static pod
echo "Waiting for kube-scheduler to restart..."
sleep 20
# Verification: ensure kube-scheduler process runs with --profiling=false and without true
echo "Verifying kube-scheduler profiling flag..."
if ! /bin/ps -ef | grep kube-scheduler | grep -v grep >/dev/null 2>&1; then
echo "ERROR: kube-scheduler process not found after manifest change."
exit 1
fi
if /bin/ps -ef | grep kube-scheduler | grep -v grep | grep -q -- "--profiling=true"; then
echo "ERROR: kube-scheduler still running with --profiling=true."
exit 1
fi
if ! /bin/ps -ef | grep kube-scheduler | grep -v grep | grep -q -- "--profiling=false"; then
echo "ERROR: kube-scheduler not running with --profiling=false."
exit 1
fi
echo "SUCCESS: kube-scheduler is running with --profiling=false on this control plane node."
exit 0