> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **SSH to each control plane node**
           ```bash theme={null}
           ssh root@<control-plane-node-ip>
           ```

        2. **Open the kube-scheduler static pod manifest for editing**
           ```bash theme={null}
           vi /etc/kubernetes/manifests/kube-scheduler.yaml
           ```

        3. **Set `--profiling=false` in the scheduler container args**\
           In the `containers:` → `- name: kube-scheduler` → `command:` or `args:` list, ensure there is an entry exactly like:
           ```yaml theme={null}
           - --profiling=false
           ```
           If 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 the `kube-scheduler` static pod on this node.

        4. **Wait for the kube-scheduler pod to restart and become Ready**\
           From any machine with kubectl access:
           ```bash theme={null}
           kubectl -n kube-system get pods -l component=kube-scheduler -w
           ```
           Wait until the scheduler pod shows `STATUS` as `Running` and `READY` as `1/1`, then Ctrl+C.

        5. **Verify the scheduler process is running with `--profiling=false`**\
           On each control plane node:
           ```bash theme={null}
           /bin/ps -ef | grep kube-scheduler | grep -v grep
           ```
           Confirm the output includes `--profiling=false` and does not include `--profiling=true`.
      </Accordion>

      <Accordion title="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.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/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
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
