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

# Ensure Admission Control Plugin NamespaceLifecycle Is Set

### More Info:

Reject creating objects in a namespace that is undergoing termination.

### Risk Level

Low

### 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, back up the kube-apiserver manifest:
           ```bash theme={null}
           sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak
           ```

        2. On every control plane node, open the manifest for editing:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```

        3. In the `command:` section of the kube-apiserver container, locate the line starting with `- --disable-admission-plugins=`.
           * If it does **not** exist, go to step 4.
           * If it exists and contains `NamespaceLifecycle`, remove **only** `NamespaceLifecycle` from the comma-separated list, keeping any other plugins as-is.\
             Example before:
           ```yaml theme={null}
           - --disable-admission-plugins=NamespaceLifecycle,ServiceAccount
           ```
           Example after:
           ```yaml theme={null}
           - --disable-admission-plugins=ServiceAccount
           ```

        4. If there is no `--disable-admission-plugins` line, add nothing for this control; no additional flag is required as long as `NamespaceLifecycle` is not disabled. Save and exit the editor.\
           Note: Editing this static pod manifest will cause the kubelet to automatically restart the kube-apiserver pod.

        5. Wait 1–2 minutes for the kube-apiserver pod to be recreated, then on every control plane node verify that `NamespaceLifecycle` is not listed under `--disable-admission-plugins`:
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep | tr ' ' '\n' | grep -- '--disable-admission-plugins' || echo "No disable-admission-plugins flag present"
           ```
           Confirm that the printed value (if any) does **not** contain `NamespaceLifecycle`.
      </Accordion>

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

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Ensure NamespaceLifecycle admission plugin is NOT disabled
        # Scope: every control plane node
        #
        # Usage:
        #   1) Copy this script to each control plane node, e.g. /root/fix-namespace-lifecycle.sh
        #   2) Run as root: bash /root/fix-namespace-lifecycle.sh
        #   3) Safe to re-run; it is 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." >&2
          exit 1
        fi

        if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
          echo "ERROR: ${APISERVER_MANIFEST} not found on this node." >&2
          exit 1
        fi

        mkdir -p "${BACKUP_DIR}"

        backup_file="${BACKUP_DIR}/kube-apiserver.yaml.${TIMESTAMP}"
        cp "${APISERVER_MANIFEST}" "${backup_file}"
        echo "Backup created at ${backup_file}"

        # Function to clean a single line containing --disable-admission-plugins
        clean_disable_line() {
          local line="$1"

          # Remove NamespaceLifecycle from a comma-separated list while preserving others
          # Handle cases:
          #   NamespaceLifecycle
          #   NamespaceLifecycle,Other
          #   Other,NamespaceLifecycle
          #   Other,NamespaceLifecycle,Another
          # and variants with spaces.
          python3 - "$line" << 'PYEOF'
        import sys
        line = sys.argv[1]

        # Split around the flag
        import re
        m = re.search(r'(--disable-admission-plugins\s*=\s*)([^"\']+)', line)
        if not m:
            # Flag may be like: --disable-admission-plugins=NamespaceLifecycle,Other \ 
            # inside quotes or different formatting; fall back to simple replace
            cleaned = line.replace("NamespaceLifecycle,", "").replace(",NamespaceLifecycle", "").replace("NamespaceLifecycle", "")
            print(cleaned)
            sys.exit(0)

        prefix, value = m.groups()
        plugins = [p.strip() for p in value.split(',') if p.strip()]
        plugins = [p for p in plugins if p != "NamespaceLifecycle"]

        if plugins:
            new_value = ",".join(plugins)
            cleaned = line[:m.start()] + f"{prefix}{new_value}" + line[m.end():]
        else:
            # Remove entire flag if no plugins left
            cleaned = line[:m.start()] + line[m.end():]

        print(cleaned)
        PYEOF
        }

        tmp_file="$(mktemp)"
        changed=0

        while IFS= read -r line; do
          if echo "${line}" | grep -q -- "--disable-admission-plugins"; then
            if echo "${line}" | grep -q "NamespaceLifecycle"; then
              new_line="$(clean_disable_line "${line}")"
              if [[ "${new_line}" != "${line}" ]]; then
                changed=1
                echo "${new_line}" >> "${tmp_file}"
              else
                echo "${line}" >> "${tmp_file}"
              fi
            else
              echo "${line}" >> "${tmp_file}"
            fi
          else
            echo "${line}" >> "${tmp_file}"
          fi
        done < "${APISERVER_MANIFEST}"

        if [[ "${changed}" -eq 1 ]]; then
          mv "${tmp_file}" "${APISERVER_MANIFEST}"
          echo "Updated ${APISERVER_MANIFEST} to ensure NamespaceLifecycle is not disabled."
          echo "Note: Because this is a static pod manifest, kube-apiserver will restart automatically."
        else
          rm -f "${tmp_file}"
          echo "No changes needed; NamespaceLifecycle not found in --disable-admission-plugins."
        fi

        echo "Waiting for kube-apiserver process to be running..."
        # Simple wait loop to ensure API server is back (best-effort)
        for i in {1..30}; do
          if /bin/ps -ef | grep kube-apiserver | grep -v grep >/dev/null 2>&1; then
            break
          fi
          sleep 2
        done

        echo
        echo "Verification (on this control plane node):"
        /bin/ps -ef | grep kube-apiserver | grep -v grep | sed 's/^/  /'

        # Stronger verification: confirm NamespaceLifecycle is NOT in any disable-admission-plugins argument
        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 "VERIFICATION FAILED: NamespaceLifecycle still appears in --disable-admission-plugins." >&2
            exit 1
          fi
        fi

        echo
        echo "Verification passed: kube-apiserver is running and NamespaceLifecycle is not disabled."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/admin/admission-controllers/#namespacelifecycle](https://kubernetes.io/docs/admin/admission-controllers/#namespacelifecycle)
