Skip to main content

Ensure Admission Control Plugin ServiceAccount Is Set

More Info:​

Automate service accounts management.

Risk Level​

Medium

Address​

Security

Compliance Standards​

  • CIS Kubernetes

Triage and Remediation​

Remediation​

Manual Steps
  1. On every control plane node, review existing ServiceAccounts to understand what is already in use (run on any machine with kubectl access):

    kubectl get serviceaccounts --all-namespaces -o wide

    If needed, create or adjust ServiceAccounts for your workloads according to your environment’s requirements:

    kubectl create serviceaccount <name> -n <namespace>
  2. On every control plane node, back up the existing kube-apiserver static pod manifest:

    sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.backup
  3. On every control plane node, open the kube-apiserver manifest for editing:

    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
  4. In the spec.containers[0].command section, locate the --disable-admission-plugins argument.

    • If it includes ServiceAccount, remove ServiceAccount from the comma-separated list, leaving the other plugins unchanged.
    • If --disable-admission-plugins is not present, no change is needed for this argument.
      Save and exit the editor.
      Note: Editing this file will cause the kubelet to restart the kube-apiserver static pod.
  5. Wait for the kube-apiserver pod to restart and become Ready (run on any machine with kubectl access):

    kubectl get pods -n kube-system -l component=kube-apiserver -o wide
  6. Verify on every control plane node that the kube-apiserver process is now running without ServiceAccount in --disable-admission-plugins:

    /bin/ps -ef | grep kube-apiserver | grep -v grep

    Inspect the output and confirm that either --disable-admission-plugins is absent or, if present, its value does not contain ServiceAccount.

Using kubectl

kubectl cannot modify the kube-apiserver static pod manifest or its process flags on the control plane node. To remediate this finding, you must edit /etc/kubernetes/manifests/kube-apiserver.yaml directly on every control plane node; see the Manual Steps section for detailed guidance.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure ServiceAccount admission plugin is NOT disabled
# Scope: Run on every control plane node
# Effect: Edits /etc/kubernetes/manifests/kube-apiserver.yaml
# Note: Changing this static pod manifest will restart the kube-apiserver.

set -euo pipefail

KUBE_APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"

if [ ! -f "$KUBE_APISERVER_MANIFEST" ]; then
echo "ERROR: $KUBE_APISERVER_MANIFEST not found on this node. Are you on a control plane node?"
exit 1
fi

if ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: python3 is required for safe YAML manipulation."
exit 1
fi

echo "Creating backup: ${KUBE_APISERVER_MANIFEST}.${BACKUP_SUFFIX}"
cp -p "$KUBE_APISERVER_MANIFEST" "${KUBE_APISERVER_MANIFEST}.${BACKUP_SUFFIX}"

python3 << 'PYEOF'
import sys, re, copy
from pathlib import Path

path = Path("/etc/kubernetes/manifests/kube-apiserver.yaml")
text = path.read_text()

# Very small YAML editor tailored for kube-apiserver command args line.
# Goal: ensure --disable-admission-plugins does NOT contain ServiceAccount.
lines = text.splitlines()
changed = False

def process_arg_list(arg_str: str) -> str:
"""
Given a comma-separated list (e.g. 'NamespaceLifecycle,ServiceAccount,NodeRestriction'),
remove 'ServiceAccount' token if present and deduplicate commas.
"""
parts = [p.strip() for p in arg_str.split(",") if p.strip()]
parts = [p for p in parts if p != "ServiceAccount"]
return ",".join(parts)

new_lines = []
for line in lines:
if "--disable-admission-plugins" in line:
orig_line = line

# Match patterns like:
# - --disable-admission-plugins=Something,ServiceAccount,Other
# - --disable-admission-plugins=ServiceAccount
# --disable-admission-plugins=ServiceAccount,Other
def repl(m):
pre = m.group(1)
val = m.group(2)
new_val = process_arg_list(val)
return f"{pre}{new_val}"

new_line = re.sub(
r"(--disable-admission-plugins=)([^ \"']+)",
repl,
line
)

if new_line != orig_line:
changed = True
line = new_line
new_lines.append(line)

if changed:
new_text = "\n".join(new_lines) + ("\n" if text.endswith("\n") else "")
path.write_text(new_text)

PYEOF

echo "Configuration updated. kube-apiserver static pod will be reloaded automatically."

# Verification: ensure kube-apiserver is running and ServiceAccount is NOT in --disable-admission-plugins
echo "Waiting briefly for kube-apiserver to stabilize..."
sleep 10

echo "Current kube-apiserver process flags:"
/bin/ps -ef | grep kube-apiserver | grep -v grep || {
echo "ERROR: kube-apiserver process not found. Check pod status."
exit 1
}

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 -q -- "--disable-admission-plugins=.*ServiceAccount"; then
echo "FAIL: ServiceAccount still present in --disable-admission-plugins."
exit 1
fi
fi

echo "PASS: ServiceAccount admission plugin is not disabled on this control plane node."

Additional Reading:​