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โ
- Remediation
Remediationโ
Manual Steps
-
On every control plane node, back up the kube-apiserver manifest:
sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak -
On every control plane node, open the manifest for editing:
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml -
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 onlyNamespaceLifecyclefrom the comma-separated list, keeping any other plugins as-is.
Example before:
- --disable-admission-plugins=NamespaceLifecycle,ServiceAccountExample after:
- --disable-admission-plugins=ServiceAccount -
If there is no
--disable-admission-pluginsline, add nothing for this control; no additional flag is required as long asNamespaceLifecycleis not disabled. Save and exit the editor.
Note: Editing this static pod manifest will cause the kubelet to automatically restart the kube-apiserver pod. -
Wait 1โ2 minutes for the kube-apiserver pod to be recreated, then on every control plane node verify that
NamespaceLifecycleis not listed under--disable-admission-plugins:/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.
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.
Automation
#!/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."