> ## 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.

# API Server Should Enable The NamespaceLifecycle Admission Plugin

### More Info:

Verifies that the NamespaceLifecycle admission plugin is not disabled so objects cannot be created in non-existent or terminating namespaces.

### 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. On every control plane node, open the API server static pod manifest for editing (this will restart the API server when saved):

        ```bash theme={null}
        sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
        ```

        2. In the `spec.containers[0].command` list, locate any existing `--disable-admission-plugins=` argument. Edit it to remove `NamespaceLifecycle` from the comma-separated list of plugins, ensuring the argument either:

        * does not exist at all, or
        * exists but does not contain `NamespaceLifecycle` (e.g. change\
          `--disable-admission-plugins=NamespaceLifecycle,SomeOtherPlugin`\
          to\
          `--disable-admission-plugins=SomeOtherPlugin`).

        3. If `NamespaceLifecycle` was the only plugin listed, remove the entire `--disable-admission-plugins=NamespaceLifecycle` flag line from the `command` list.

        4. Save the file and exit the editor. The kubelet will automatically detect the manifest change and restart the kube-apiserver static pod.

        5. Wait for the API server pod to become ready:

        ```bash theme={null}
        # On any machine with kubectl access
        kubectl get pods -n kube-system -l component=kube-apiserver -o wide
        ```

        Confirm the pod is in `Running` status and `READY` is `1/1`.

        6. Verify that the `NamespaceLifecycle` plugin is not disabled:

        ```bash theme={null}
        # On every control plane node
        /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--disable-admission-plugins'
        ```

        Confirm that the output either shows no `--disable-admission-plugins` flag, or that its value does not contain `NamespaceLifecycle`.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to change API server process flags or edit the static pod manifest at `/etc/kubernetes/manifests/kube-apiserver.yaml` on the control plane nodes. To remediate this finding, make the change directly on each control plane node as described in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Fix: Ensure the NamespaceLifecycle admission plugin is NOT disabled
        # Scope: every control plane node
        # Target file: /etc/kubernetes/manifests/kube-apiserver.yaml
        #
        # Usage:
        #   - Run on each control plane node as root.
        #   - Safe to re-run; idempotent.

        set -euo pipefail

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

        if [[ $EUID -ne 0 ]]; then
          echo "ERROR: 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

        mkdir -p "$BACKUP_DIR"
        cp -p "$APISERVER_MANIFEST" "$BACKUP_DIR/kube-apiserver.yaml.$TIMESTAMP"

        # Function: remove NamespaceLifecycle from an existing --disable-admission-plugins arg
        # Handles comma-separated lists and possible surrounding spaces.
        clean_disable_plugins_value() {
          local value="$1"
          local cleaned

          # Add leading/trailing commas to simplify removal
          cleaned=",$value,"

          # Remove 'NamespaceLifecycle' (with or without surrounding spaces) from list
          cleaned="$(printf '%s' "$cleaned" \
            | sed -E 's/,\s*NamespaceLifecycle\s*,/,/g')"

          # Remove possible duplicate commas
          cleaned="$(printf '%s' "$cleaned" \
            | sed -E 's/,+/,/g')"

          # Trim leading/trailing commas
          cleaned="$(printf '%s' "$cleaned" \
            | sed -E 's/^,+//; s/,+$//')"

          # Trim spaces around commas
          cleaned="$(printf '%s' "$cleaned" \
            | sed -E 's/\s*,\s*/,/g')"

          printf '%s' "$cleaned"
        }

        # Work on a temporary file and then move into place atomically
        TMP_FILE="$(mktemp)"
        cp "$APISERVER_MANIFEST" "$TMP_FILE"

        # Detect if there is any --disable-admission-plugins arg present
        if grep -q -- '--disable-admission-plugins' "$TMP_FILE"; then
          # Process each line containing --disable-admission-plugins
          # Supports both:
          #   - --disable-admission-plugins=Foo,Bar
          #   - --disable-admission-plugins Foo,Bar
          # We only modify NamespaceLifecycle; other plugins remain untouched.
          mapfile -t lines < <(grep -n -- '--disable-admission-plugins' "$TMP_FILE" | cut -d: -f1)

          for lineno in "${lines[@]}"; do
            line="$(sed -n "${lineno}p" "$TMP_FILE")"

            if printf '%s' "$line" | grep -qE -- '--disable-admission-plugins='; then
              # Form: --disable-admission-plugins=Foo,Bar
              before="$(printf '%s' "$line" | sed -E 's/(.*--disable-admission-plugins=)([^"]*)(.*)/\1/')"
              value="$(printf '%s' "$line" | sed -E 's/.*--disable-admission-plugins=([^"]*).*/\1/')"
              after="$(printf '%s' "$line" | sed -E 's/.*--disable-admission-plugins=[^"]*(.*)/\1/')"

              new_value="$(clean_disable_plugins_value "$value")"

              if [[ -z "$new_value" ]]; then
                # Entire list would be empty; remove the flag altogether
                # Remove ' --disable-admission-plugins=...' from the line
                newline="$(printf '%s' "$line" \
                  | sed -E 's/[[:space:]]*--disable-admission-plugins=[^"[:space:]]*//')"
              else
                newline="${before}${new_value}${after}"
              fi
            else
              # Form: --disable-admission-plugins Foo,Bar
              # Split on the first occurrence of the flag
              prefix="$(printf '%s' "$line" | sed -E 's/(.*)--disable-admission-plugins(.*)/\1/')"
              rest="$(printf '%s' "$line" | sed -E 's/.*--disable-admission-plugins(.*)/\1/')"

              # Extract the value (first token after the flag)
              value="$(printf '%s' "$rest" | sed -E 's/^[[:space:]]+([^[:space:]]+).*/\1/')"
              suffix="$(printf '%s' "$rest" | sed -E 's/^[[:space:]]+[^[:space:]]+(.*)/\1/')"

              new_value="$(clean_disable_plugins_value "$value")"

              if [[ -z "$new_value" ]]; then
                # Remove the flag entirely
                newline="$(printf '%s' "$prefix$rest" \
                  | sed -E 's/[[:space:]]*--disable-admission-plugins[[:space:]]+[^[:space:]]+//')"
              else
                newline="${prefix}--disable-admission-plugins ${new_value}${suffix}"
              fi
            fi

            # Replace line in temp file
            sed -i "${lineno}s~.*~${newline}~" "$TMP_FILE"
          done
        else
          # No --disable-admission-plugins flag: nothing to change; the plugin is enabled by default.
          echo "INFO: --disable-admission-plugins not present; NamespaceLifecycle is not disabled."
        fi

        # Replace the manifest only if it changed
        if cmp -s "$APISERVER_MANIFEST" "$TMP_FILE"; then
          echo "INFO: No changes required in $APISERVER_MANIFEST"
          rm -f "$TMP_FILE"
        else
          mv "$TMP_FILE" "$APISERVER_MANIFEST"
          echo "INFO: Updated $APISERVER_MANIFEST (kube-apiserver static pod will restart automatically)."
        fi

        # Verification: ensure NamespaceLifecycle is NOT listed under --disable-admission-plugins
        echo "INFO: Waiting for kube-apiserver process to be running..."
        sleep 10

        echo "INFO: Verifying that NamespaceLifecycle is not disabled..."
        if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- '--disable-admission-plugins'; then
          if /bin/ps -ef | grep kube-apiserver | grep -v grep \
               | grep -- '--disable-admission-plugins' \
               | grep -q 'NamespaceLifecycle'; then
            echo "ERROR: NamespaceLifecycle is still present in --disable-admission-plugins." >&2
            exit 1
          fi
          echo "SUCCESS: kube-apiserver is running and NamespaceLifecycle is NOT disabled."
        else
          echo "SUCCESS: kube-apiserver is running with no --disable-admission-plugins flag; NamespaceLifecycle is enabled by default."
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
