This article shows how to spin up a local Kubernetes cluster with cluster-level PodSecurity admission set to baseline. You get strong workload guardrails from day one. No manual policy patches later.
TL;DR
- PodSecurity admission enforces security context rules on pods at create time.
- Kind can enable the PodSecurity feature gate and apply a cluster-level baseline policy during cluster creation.
- Script example uses
kind create cluster --config
with featureGates and a patch to setenforce
version to latest. - Baseline policy blocks privileged pods, enforces readOnlyRootFilesystem and dropCapabilities.
- You can adjust policy scope via
PodSecurityConfiguration
CR andPodSecurityAdmissionConfiguration
. - Use local kind clusters to test compliance, integrate with CI/CD pipelines, or prototype secure deployments.
Overview of PodSecurity Baseline
Kubernetes v1.23 introduced the built-in PodSecurity admission controller. It replaces the old PSP (PodSecurityPolicy) mechanism and provides three profiles: privileged
, baseline
, and restricted
. The baseline profile bans known risky behaviors but remains user-friendly for most apps. It does not require admins to write custom policies.
Admins apply these profiles either at namespace level via labels or cluster level via the PodSecurityAdmissionConfiguration
API. The cluster-level route ensures every namespace, existing or new, inherits the baseline policy without manual labeling.
Kind cluster with PodSecurity Baseline
Kind (Kubernetes IN Docker) offers quick local clusters. You can pass a config file to kind create cluster
that enables the PodSecurity feature gate, disables default CNI for flexibility, and launches control-plane plus worker nodes. After cluster creation, you patch the cluster policy resource to enforce the baseline version “latest.”
Shell Script Breakdown
#!/usr/bin/env bash
# Create kind cluster with PodSecurity admission at cluster level.
# Ensure kind exists
if ! command -v kind &> /dev/null; then
echo "kind not found. Install kind first.";
exit 1;
fi
# Variables
default_version="v0.17.0"
cluster_name="kind-podsec-baseline"
kind_image="kindest/node:${default_version}"
cat <<EOF | kind create cluster \
--name "${cluster_name}" \
--image "${kind_image}" \
--config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
PodSecurity: true
networking:
disableDefaultCNI: true
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
echo "Applying cluster-level baseline policy"
kubectl patch \
podsecurityconfigurationsettings baseline \
--type=merge \
-p '{"enforce":{"version":"latest"}}'
echo "Cluster ${cluster_name} ready with baseline PodSecurity"
Understanding PodSecurity Admission Modes
PodSecurity admission offers three modes per policy: enforce
, warn
, and audit
.
- enforce: Denies non-compliant pod creations.
- warn: Allows pods but prints warnings.
- audit: Logs policy violations to the audit backend.
The script patches only the enforce
field, but you can add warn
or audit
to test impact gradually.
PodSecurityConfiguration Lifecycle
Cluster-level policies live as PodSecurityConfigurationSettings
objects in the pod-security.kubernetes.io
API group. Each profile—privileged, baseline, restricted—has default settings you can override via JSON patch. After updating, the admission controller reads the latest configuration in real time. No restart required.
Customizing PodSecurity Baseline Policies
You may need to tweak specific rules. For instance, allow the SYS_TIME
capability or permit hostPath
mounts to /data. Create a custom PodSecurityConfiguration
CR:
apiVersion: pod-security.k8s.io/v1
kind: PodSecurityConfiguration
metadata:
name: custom-baseline
spec:
enforce:
profile: baseline
version: "latest"
exemptions:
capabilities:
- SYS_TIME
hostPaths:
- pathPrefix: "/data"
Then set that CR in your PodSecurityAdmissionConfiguration
:
apiVersion: apiserver.config.k8s.io/v1
kind: PodSecurityAdmissionConfiguration
profiles:
- level: "privileged"
enforcement:
level: "enforce"
- level: "baseline"
configuration:
apiVersion: pod-security.k8s.io/v1
kind: PodSecurityConfiguration
name: custom-baseline
Access Controls and Security Contexts
The baseline profile enforces key securityContext fields:
runAsNonRoot
must be true.readOnlyRootFilesystem
must be true unless writable volume used.capabilities
drop all by default, add only required ones.allowPrivilegeEscalation
must be false.- No host networking, host PID/IPC.
Pods violating these fail admission. The policy does not cover RBAC roles or NetworkPolicies.
Use-case: Local CI/CD
Integrate this kind setup in CI pipelines. Each PR test spins up an isolated cluster with baseline policies. That tests your Helm charts or Kustomize overlays for policy violations before merging.
You can combine PSA with event-driven operators. For example, an admission webhook triggers custom security scans when a new pod passes baseline. Events flow from the API server to webhook, then to your scanning service via a Kafka topic for audit storage.
Limitations and Best Practices
Baseline covers most apps but not all. Legacy apps using hostPath or privileged mode will break. Audit with warn
first. Keep featureGates up to date when upgrading Kubernetes or kind.
References
Suggested Reading
PostHashID: 75263dfb936b0e2e1c460ac37b9c745564315c222b90e100ff345cde02b89a4f