Ensure Cluster Active Policy Control Mechanisms In Place
More Info:
Every Kubernetes cluster should have at least one policy control mechanism in place to enforce the other requirements in this section. This could be the in-built Pod Security Admission controller, or a third party policy control system.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Inventory namespaces and identify user-workload namespaces
- Run on: any machine with kubectl access
- Command:
kubectl get namespaces --show-labels
- Manually identify which namespaces contain user workloads (non-system, non-infra), e.g., by name, labels, or by listing workloads:
kubectl get pods -A
- Make a list of user-workload namespaces that must be covered by a policy control mechanism.
-
Check for Pod Security Admission (PSA) configuration on each user namespace
- Run on: any machine with kubectl access
- For each identified user namespace
<ns>, inspect PSA labels:kubectl get ns <ns> -o jsonpath='{.metadata.labels}' | jq - Verify at least the
pod-security.kubernetes.io/enforcelabel is set to a valid level (baselineorrestricted, and optionally version labels):pod-security.kubernetes.io/enforcepod-security.kubernetes.io/enforce-version(optional)
- If PSA labels are missing or set to
privileged, plan to either apply appropriate PSA labels or use an external policy system in the next steps.
-
Review for external policy control systems (e.g., Gatekeeper, Kyverno)
- Run on: any machine with kubectl access
- Check if common policy engines are installed:
kubectl get pods -A | egrep 'gatekeeper|kyverno|opa|policy'kubectl api-resources | egrep 'ConstraintTemplate|K8sRequired|ClusterPolicy'
- If you find such a system, list cluster-level policies to confirm it is actively enforcing:
# Gatekeeper exampleskubectl get k8srequiredlabels.constraints.gatekeeper.sh -A || truekubectl get k8spsp* -A || true# Kyverno exampleskubectl get clusterpolicies.kyverno.io || truekubectl get policies.kyverno.io -A || true
- Manually review these policies to confirm they apply to the user-workload namespaces and enforce security requirements (e.g., disallow privileged, host networking, etc.).
-
Decide on remediation approach per namespace (PSA vs. external policy)
- For each user-workload namespace not adequately covered by PSA or an external policy:
- Option A (PSA): choose an appropriate level (
baselinefor minimal protections,restrictedfor stricter isolation) based on application needs. - Option B (external policy): design or select policies that cover the same security concerns for that namespace.
- Option A (PSA): choose an appropriate level (
- Document the choice per namespace and any exceptions where stricter policies would break required workloads, including risk justification.
- For each user-workload namespace not adequately covered by PSA or an external policy:
-
Apply or adjust policy controls for uncovered namespaces
- Run on: any machine with kubectl access
- To enable or strengthen PSA on a namespace
<ns>:(Changekubectl label namespace <ns> \pod-security.kubernetes.io/enforce=baseline \pod-security.kubernetes.io/enforce-version=latest \--overwritebaselinetorestrictedif appropriate.) - For external policy engines, create or update policies so that the
namespaceSelector,matchrules, or similar constructs explicitly include the uncovered namespaces and enforce your required controls (use the engine’s standard manifests and tools). - If you intentionally exclude a namespace from these mechanisms (for example, an internal admin-only namespace), record this as a formal exception with the rationale and compensating controls.
-
Verify effective coverage across all user-workload namespaces
- Run on: any machine with kubectl access
- Re-check PSA labels and external policy objects:
kubectl get ns --show-labelskubectl get clusterpolicies.kyverno.io 2>/dev/null || truekubectl get constraints.constraints.gatekeeper.sh 2>/dev/null || true
- Optionally, attempt to create a clearly non-compliant test pod in a covered namespace and confirm it is rejected (adapted to your chosen mechanism), then delete any test resources.
- Confirm that every namespace with user workloads is now governed by at least one active policy control mechanism (PSA and/or external), and that this is captured in your cluster’s security documentation.
Using kubectl
# 1) List all namespaces and their Pod Security Admission labels
# Run on: any machine with kubectl access
kubectl get ns --show-labels
Review for each user workload namespace (anything other than kube-system, kube-public, kube-node-lease, and provider/system namespaces like gatekeeper-system, kyverno, etc.):
- If none of these labels are present, PSA is effectively off:
pod-security.kubernetes.io/enforcepod-security.kubernetes.io/auditpod-security.kubernetes.io/warn
- If
pod-security.kubernetes.io/enforceexists but is set to a very permissive level (e.g.privileged) for user workloads, this is likely a policy gap.
# 2) Show only namespaces that have Pod Security Admission labels
kubectl get ns -L pod-security.kubernetes.io/enforce \
-L pod-security.kubernetes.io/audit \
-L pod-security.kubernetes.io/warn
Problem indicators:
- User workload namespaces appear with all three columns as
<none>. - Critical namespaces where you expect restrictions (e.g.
production,staging) showenforce=privilegedwithout a compensating external policy mechanism.
# 3) Check for Gatekeeper (OPA) as an external policy mechanism
kubectl get ns | grep -E 'gatekeeper'
kubectl get pods -n gatekeeper-system
kubectl get constrainttemplates.constraints.gatekeeper.sh
kubectl get constraints --all-namespaces
Problem indicators:
gatekeeper-systemnamespace missing, or no Gatekeeper pods inRunning/Readystate.- No
ConstraintTemplateorConstraintresources defined, meaning Gatekeeper is present but not enforcing anything.
# 4) Check for Kyverno as an external policy mechanism
kubectl get ns | grep -i kyverno
kubectl get pods -n kyverno
kubectl get clusterpolicies.kyverno.io
kubectl get policies.kyverno.io --all-namespaces
Problem indicators:
kyvernonamespace missing, or Kyverno pods notRunning/Ready.- No
ClusterPolicy/Policyresources, or all are disabled (spec.validationFailureAction=Auditeverywhere when you expect enforcement).
# 5) Inspect example policies to understand scope and mode
# (replace with real names from previous outputs)
kubectl get clusterpolicies.kyverno.io <policy-name> -o yaml
kubectl get <constraint-kind> <constraint-name> -n <namespace> -o yaml
Problem indicators:
- Policies only in audit/warn mode where enforcement is required.
- Policies scoped only to a subset of namespaces, leaving others with user workloads completely unprotected.
# 6) Map user workload namespaces to active mechanisms (manual review aid)
# This just lists namespaces; you must compare with earlier outputs.
kubectl get ns
Human review needed:
- For each namespace that actually runs user pods, confirm at least one of:
- Pod Security Admission labels are set to an appropriate level (
baselineorrestricted) withenforceconfigured, or - An external policy system (Gatekeeper/Kyverno/other) is installed, healthy, and has policies that clearly apply to that namespace and relevant pod specs.
- Pod Security Admission labels are set to an appropriate level (
If any user workload namespace has neither PSA enforcement nor clearly-applied external policies, that namespace fails this control.
Automation
#!/usr/bin/env bash
# Purpose: Report cluster-wide policy control mechanisms (Pod Security Admission,
# Kyverno, OPA Gatekeeper, and admission webhooks) for review.
# Run on: any machine with kubectl access and appropriate RBAC.
# Requirements: kubectl, jq
set -euo pipefail
echo "=== Cluster Policy Control Mechanism Report ==="
echo "Timestamp: $(date -Iseconds)"
echo
# Helper: check if a resource exists (CRD, etc.)
kubectl_exists() {
local res="$1"
if kubectl api-resources --api-group="${res#*/}" 2>/dev/null | grep -q .; then
return 0
fi
return 1
}
echo "== 1. Pod Security Admission (built-in) =="
# Looks for pod-security.kubernetes.io labels on all namespaces
kubectl get ns -o json | jq -r '
.items[]
| {
name: .metadata.name,
enforce: .metadata.labels["pod-security.kubernetes.io/enforce"],
enforce_version: .metadata.labels["pod-security.kubernetes.io/enforce-version"],
warn: .metadata.labels["pod-security.kubernetes.io/warn"],
warn_version: .metadata.labels["pod-security.kubernetes.io/warn-version"],
audit: .metadata.labels["pod-security.kubernetes.io/audit"],
audit_version: .metadata.labels["pod-security.kubernetes.io/audit-version"]
}
| @tsv
' | awk -F'\t' '
BEGIN {
printf "NAMESPACE\tENFORCE\tENF_VER\tWARN\tWARN_VER\tAUDIT\tAUD_VER\n"
}
{
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
$1, ($2==""?"-":$2), ($3==""?"-":$3),
($4==""?"-":$4), ($5==""?"-":$5),
($6==""?"-":$6), ($7==""?"-":$7)
}
'
echo
echo "# Interpretation:"
echo "# - For each namespace that runs user workloads, you should normally see a"
echo "# non-empty ENFORCE level (e.g. baseline or restricted)."
echo "# - Namespaces with ENFORCE='-' AND no third-party policy engine (see below)"
echo "# are POTENTIAL PROBLEMS and must be reviewed."
echo
echo "== 2. External Policy Engines (Kyverno / Gatekeeper) presence =="
echo "-- Installed policy-related CRDs --"
kubectl get crds 2>/dev/null | grep -E 'kyverno|policies.kyverno|constraints.gatekeeper|constrainttemplates.gatekeeper' || \
echo "No Kyverno/Gatekeeper-related CRDs detected (this may be OK if you rely only on Pod Security Admission)."
echo
echo "-- Kyverno policies (if Kyverno is installed) --"
if kubectl get crd clusterpolicies.kyverno.io >/dev/null 2>&1; then
echo "ClusterPolicies:"
kubectl get clusterpolicies.kyverno.io -A || true
fi
if kubectl get crd policies.kyverno.io >/dev/null 2>&1; then
echo
echo "Namespaced Kyverno Policies:"
kubectl get policies.kyverno.io -A || true
fi
echo
echo "# Interpretation:"
echo "# - If Kyverno CRDs exist but 0 policies are listed, the engine is present"
echo "# but NOT enforcing anything: PROBLEM unless Pod Security Admission covers all user namespaces."
echo
echo "-- Gatekeeper constraints (if Gatekeeper is installed) --"
if kubectl get crd constrainttemplates.templates.gatekeeper.sh >/dev/null 2>&1; then
# List constraint kinds from Gatekeeper
echo "ConstraintTemplates:"
kubectl get constrainttemplates -A || true
echo
echo "Constraints by kind:"
# For each constraint CRD, list objects
kubectl get crd -o json | jq -r '
.items[]
| select(.spec.group=="constraints.gatekeeper.sh")
| .spec.names.kind
' | while read -r kind; do
echo " Constraints of kind ${kind}:"
kubectl get "${kind,,}.constraints.gatekeeper.sh" -A 2>/dev/null || echo " (none)"
echo
done
else
echo "No Gatekeeper ConstraintTemplate CRD detected (this may be OK if using PSA or another engine)."
fi
echo
echo "# Interpretation:"
echo "# - If Gatekeeper is installed but there are no constraints, it is NOT"
echo "# enforcing anything: PROBLEM unless Pod Security Admission covers all user namespaces."
echo
echo "== 3. Admission Webhooks overview (for other policy systems) =="
echo "-- ValidatingWebhookConfigurations --"
kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io -o wide || true
echo
echo "-- MutatingWebhookConfigurations --"
kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io -o wide || true
echo
echo "# Interpretation:"
echo "# - Identify any third-party policy systems (e.g. Falco-based admission,"
echo "# custom webhooks). Verify they are enabled and in use for user namespaces."
echo "# - Absence of any policy-related webhooks is NOT automatically a problem if"
echo "# PSA or another mechanism is clearly configured."
echo
echo "== 4. Workload namespaces without obvious policy labels =="
# Heuristic: user namespaces = all except kube-system, kube-public, kube-node-lease, default (you may need to adjust).
kubectl get ns -o json | jq -r '
.items[]
| select(.metadata.name != "kube-system"
and .metadata.name != "kube-public"
and .metadata.name != "kube-node-lease"
and .metadata.name != "default")
| {
name: .metadata.name,
enforce: .metadata.labels["pod-security.kubernetes.io/enforce"]
}
| @tsv
' | awk -F'\t' '
BEGIN {
printf "NAMESPACE\tPSA_ENFORCE_LABEL\n"
}
{
enforce = ($2==""?"-":$2)
printf "%s\t%s\n", $1, enforce
}
'
echo
echo "# Interpretation:"
echo "# - Each listed namespace is heuristically treated as a user workload namespace."
echo "# - If PSA_ENFORCE_LABEL='-' for a namespace, you MUST verify that:"
echo "# * Either a third-party policy control system is enforcing policies there,"
echo "# * Or you have explicitly decided to allow it (document the exception)."
echo "# - Any user namespace without PSA labels AND without clear coverage by an"
echo "# external policy engine is a PROBLEM relative to this control."
echo
echo "== 5. Summary guidance (manual review required) =="
echo "# Use the sections above to answer for every user workload namespace:"
echo "# 1) Is Pod Security Admission enforce-level configured?"
echo "# 2) If not, is there a Kyverno/Gatekeeper/other admission policy in place?"
echo "# If the answer to BOTH is 'no' for any user namespace, that namespace is"
echo "# non-compliant with: 'Ensure that either Pod Security Admission or an external"
echo "# policy control system is in place for every namespace which contains user workloads.'"