Introduction
In the ever-evolving landscape of cloud-native security, safeguarding your Kubernetes clusters goes beyond just hardening the control plane or securing network access. A critical, often overlooked, layer of defense lies within the very workloads running on your cluster: the Pods themselves. Unrestricted Pod configurations can introduce severe vulnerabilities, allowing attackers to escalate privileges, access sensitive data, or compromise the underlying host. This is precisely where Kubernetes Pod Security Standards (PSS) come into play.
The Pod Security Standards provide a set of predefined security policies ranging from highly permissive to highly restrictive, helping you enforce best practices for how Pods are allowed to run. These standards replace the deprecated Pod Security Policies (PSPs) and integrate directly into the Kubernetes API server, offering a more streamlined and robust enforcement mechanism. By understanding and implementing PSS, you can significantly reduce the attack surface of your applications, ensuring that even if an attacker gains access to a Pod, their potential for lateral movement and damage is severely limited.
This comprehensive guide will walk you through the intricacies of Kubernetes Pod Security Standards. We’ll explore the different PSS profiles, demonstrate how to configure them using Pod Security Admission, and provide practical examples to secure your workloads effectively. Whether you’re a security engineer looking to harden your cluster or a developer aiming to deploy secure applications, this guide will equip you with the knowledge to implement PSS and build a more resilient Kubernetes environment.
TL;DR: Kubernetes Pod Security Standards (PSS) Implementation
Kubernetes Pod Security Standards (PSS) define three security profiles (Privileged, Baseline, Restricted) to enforce best practices for Pod configurations, replacing deprecated Pod Security Policies (PSPs). PSS are enforced via the Pod Security Admission controller, which is built into Kubernetes and configured using namespace labels. This guide covers how to enable PSS, apply different profiles to namespaces, and manage exceptions.
Key Commands:
- Enable PSS (Kubeadm/Managed K8s): Usually enabled by default in recent K8s versions (1.23+). For older, ensure
PodSecurityadmission controller is active. - Label a Namespace for PSS Enforcement:
kubectl label namespace <namespace-name> pod-security.kubernetes.io/enforce=<profile> \ pod-security.kubernetes.io/warn=<profile> \ pod-security.kubernetes.io/audit=<profile> - Example: Enforce ‘Restricted’ profile on ‘production’ namespace:
kubectl label namespace production pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/audit=restricted - Check Namespace Labels:
kubectl get namespace <namespace-name> -o yaml | grep pod-security.kubernetes.io - Test a Restricted Pod:
kubectl run nginx --image=nginx -n restricted-test
Prerequisites
Before diving into the implementation of Pod Security Standards, ensure you have the following:
- A Kubernetes Cluster: Version 1.23 or newer is recommended, as Pod Security Admission is GA in 1.25. While PSS can be used with older versions (1.22+), the admission controller might need explicit enabling. A local cluster (Minikube, Kind, Docker Desktop) or a managed cloud Kubernetes service (EKS, GKE, AKS) will work.
kubectlCommand-Line Tool: Configured to connect to your Kubernetes cluster. Refer to the official Kubernetes documentation for installation instructions.- Basic Understanding of Kubernetes Concepts: Familiarity with Pods, Namespaces, Deployments, and ReplicaSets is essential.
- Administrative Access: You’ll need permissions to create and modify namespaces and deploy applications to test the policies.
Step-by-Step Guide: Implementing Pod Security Standards
The Pod Security Standards define three policy levels:
- Privileged: Unrestricted policy, allowing known escalations. This policy is typically used for cluster-critical or system-level workloads that require elevated privileges.
- Baseline: Minimally restrictive policy that prevents known privilege escalations. This is a good starting point for most applications, balancing security with operational flexibility.
- Restricted: Heavily restricted policy, following current Pod hardening best practices. This is the most secure profile and should be applied to sensitive applications or those with strict compliance requirements.
These policies are enforced through the Pod Security Admission controller, which is a built-in admission controller in Kubernetes. Enforcement is configured by applying specific labels to namespaces.
Step 1: Verify Pod Security Admission Controller Status
For Kubernetes versions 1.23 and later, the Pod Security Admission controller is typically enabled by default. For versions 1.25 and later, it’s GA and fully integrated. If you are running an older cluster (1.22), you might need to ensure it’s explicitly enabled.
To check if the Pod Security Admission controller is enabled, you can inspect your API server’s configuration. However, a simpler way is to try applying a PSS label to a namespace and see if it takes effect. If you encounter issues, consult your Kubernetes distribution’s documentation or the Kubernetes Admission Controllers documentation.
For most modern clusters, you can assume it’s enabled. Let’s create a test namespace first.
kubectl create namespace pss-test
Verify:
kubectl get namespace pss-test
Expected Output:
NAME STATUS AGE
pss-test Active Xs
Step 2: Understanding PSS Enforcement Modes
Pod Security Admission uses namespace labels to define the desired security profile and enforcement behavior. There are three modes for each profile:
enforce: Pods that violate the policy will be rejected. This is the strictest mode.warn: Pods that violate the policy will be admitted, but a warning will be displayed to the user. This is useful for testing and auditing before full enforcement.audit: Pods that violate the policy will be admitted, but an audit event will be recorded in the API server’s audit logs. This is useful for monitoring and compliance.
You can apply different levels for each mode. For example, you could warn on restricted but enforce on baseline. The labels follow the pattern: pod-security.kubernetes.io/<MODE>=<LEVEL>.
Step 3: Applying the ‘Baseline’ Profile to a Namespace
The ‘Baseline’ profile is a great starting point for most applications. It prevents known privilege escalations but is less restrictive than ‘Restricted’. Let’s label our pss-test namespace to enforce the ‘Baseline’ profile. We’ll also add ‘warn’ and ‘audit’ for good measure, all at the ‘baseline’ level.
kubectl label namespace pss-test pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/warn=baseline \
pod-security.kubernetes.io/audit=baseline
Verify: Check the labels on the namespace.
kubectl get namespace pss-test -o yaml | grep pod-security.kubernetes.io
Expected Output:
pod-security.kubernetes.io/audit: baseline
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: baseline
pod-security.kubernetes.io/warn-version: latest
Now, let’s try to deploy a Pod that violates the ‘Baseline’ profile. A common violation of the baseline policy is running a container as root user with a non-zero UID, or requesting hostPath volumes. We’ll try to run a privileged container, which is explicitly forbidden by ‘Baseline’.
# privileged-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: privileged-app
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true # This violates the Baseline policy
kubectl apply -f privileged-pod.yaml -n pss-test
Expected Output (Rejection):
Error from server (Forbidden): error when creating "privileged-pod.yaml": pods "privileged-app" is forbidden: violates PodSecurity "baseline:latest": privileged (containers "nginx" must not set securityContext.privileged=true)
The Pod was rejected, confirming that the ‘Baseline’ policy is being enforced. This is a crucial step in preventing common attack vectors. For more on securing your network, consider exploring our guide on Kubernetes Network Policies: Complete Security Hardening Guide.
Step 4: Applying the ‘Restricted’ Profile to a Namespace
The ‘Restricted’ profile is the most secure and is ideal for highly sensitive workloads. It requires Pods to run as non-root users, disallows hostPath volumes, and enforces many other security best practices. Let’s create a new namespace for this and apply the ‘Restricted’ profile.
kubectl create namespace restricted-apps
kubectl label namespace restricted-apps pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
Verify:
kubectl get namespace restricted-apps -o yaml | grep pod-security.kubernetes.io
Expected Output:
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
Now, let’s try to deploy a simple Nginx Pod without any specific security context. This Pod would normally run as root (UID 0) inside the container, which violates the ‘Restricted’ policy.
# simple-nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: simple-nginx
spec:
containers:
- name: nginx
image: nginx
kubectl apply -f simple-nginx-pod.yaml -n restricted-apps
Expected Output (Rejection):
Error from server (Forbidden): error when creating "simple-nginx-pod.yaml": pods "simple-nginx" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (containers "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (containers "nginx" must drop all capabilities), runAsNonRoot != true (containers "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (containers "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
As you can see, the ‘Restricted’ policy is much stricter. It requires specific security contexts to be set. Let’s fix the Nginx Pod to comply with the ‘Restricted’ profile.
# restricted-nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: restricted-nginx
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000 # Example non-root user
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# Image should ideally provide a non-root user, e.g., using USER directive in Dockerfile
# For nginx, it typically runs as nginx user (UID 101 or similar)
# We'll override it here for demonstration, but a better practice is to build images with non-root by default.
runAsUser: 1000
kubectl apply -f restricted-nginx-pod.yaml -n restricted-apps
Expected Output (Success):
pod/restricted-nginx created
Verify:
kubectl get pod restricted-nginx -n restricted-apps
Expected Output:
NAME READY STATUS RESTARTS AGE
restricted-nginx 1/1 Running 0 Xs
This demonstrates how to successfully deploy a Pod under the ‘Restricted’ profile by adhering to its strict security requirements. When deploying sensitive workloads, you might also consider advanced security measures like those discussed in Securing Container Supply Chains with Sigstore and Kyverno.
Step 5: Using Different Enforcement Levels (Warn/Audit)
It’s often impractical to immediately enforce ‘Restricted’ on an existing namespace with many applications. A common strategy is to start with ‘warn’ or ‘audit’ modes to identify violations without blocking deployments.
Let’s modify our pss-test namespace to warn on ‘restricted’ but still enforce ‘baseline’.
kubectl label namespace pss-test pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted --overwrite
Verify:
kubectl get namespace pss-test -o yaml | grep pod-security.kubernetes.io
Expected Output:
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
Now, let’s try to deploy our previously rejected simple-nginx-pod.yaml (which violates ‘Restricted’ but not ‘Baseline’) into the pss-test namespace.
kubectl apply -f simple-nginx-pod.yaml -n pss-test
Expected Output (Success with Warning):
Warning: pods "simple-nginx" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (containers "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (containers "nginx" must drop all capabilities), runAsNonRoot != true (containers "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (containers "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/simple-nginx created
The Pod was created, but a warning was issued, indicating that it violates the ‘Restricted’ policy. This allows you to identify non-compliant Pods without disrupting existing deployments. You can then work on bringing these Pods into compliance before switching the enforce mode to ‘restricted’.
Step 6: Exemptions for Specific Workloads
Sometimes, specific applications (e.g., certain monitoring agents, custom storage drivers, or even GPU-enabled LLM workloads) genuinely require higher privileges and cannot comply with ‘Baseline’ or ‘Restricted’ profiles. PSS allows for exemptions at the Pod, RuntimeClass, or User/ServiceAccount level.
However, direct Pod or RuntimeClass exemptions are typically configured at the API server level (e.g., in a PodSecurityConfiguration file), which requires cluster administrative access and often a restart of the API server. For most users, exempting a specific ServiceAccount is the most practical approach.
Let’s say you have an application in the restricted-apps namespace that absolutely needs to run with hostPath volumes. Instead of weakening the entire namespace’s policy, you can exempt its ServiceAccount.
First, create a ServiceAccount and a Pod that uses it, violating the ‘Restricted’ policy with a hostPath.
kubectl create serviceaccount privileged-sa -n restricted-apps
# hostpath-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpath-app
spec:
serviceAccountName: privileged-sa
containers:
- name: my-container
image: busybox
command: ["sh", "-c", "echo Hello from hostPath; sleep 3600"]
volumeMounts:
- name: host-path-vol
mountPath: /var/log/host
volumes:
- name: host-path-vol
hostPath: # This violates the Restricted policy
path: /var/log
type: DirectoryOrCreate
kubectl apply -f hostpath-pod.yaml -n restricted-apps
Expected Output (Rejection):
Error from server (Forbidden): error when creating "hostpath-pod.yaml": pods "hostpath-app" is forbidden: violates PodSecurity "restricted:latest": hostPath volumes (volumes "host-path-vol" must not use hostPath)
Now, to exempt the privileged-sa ServiceAccount from the ‘Restricted’ policy, you would typically configure this in the PodSecurityConfiguration file for the API server. This file is specified via the --admission-control-config-file flag on the kube-apiserver.
# pod-security-config.yaml (example for API server configuration)
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: restricted
enforceVersion: latest
audit: restricted
auditVersion: latest
warn: restricted
warnVersion: latest
exemptions:
namespaces: []
runtimeClasses: []
serviceAccounts:
- names: ["privileged-sa"]
namespaces: ["restricted-apps"]
Note: Applying this configuration requires access to the Kubernetes control plane and restarting the kube-apiserver. This is usually beyond the scope of a typical user and is an administrator’s task. For managed Kubernetes services, consult your cloud provider’s documentation on how to configure Pod Security Admission exemptions.
Once the API server is updated with this configuration, the hostpath-app Pod using privileged-sa in the restricted-apps namespace would be allowed, even though it violates the ‘Restricted’ policy. It’s crucial to use exemptions sparingly and only when absolutely necessary, documenting the reasons thoroughly. For broader security context, also consider how robust network configurations, like those provided by Cilium WireGuard Encryption, complement PSS.
Production Considerations
Implementing Pod Security Standards in a production environment requires careful planning and execution to avoid disrupting critical workloads.
- Phased Rollout: Never apply
enforce: restricteddirectly to a production namespace without prior testing. Start withwarn: restrictedandaudit: restrictedto identify violations. Monitor your logs and application behavior for warnings. - Application Compatibility: Review your existing applications. Many legacy or poorly configured applications might violate even the ‘Baseline’ profile. Identify these, prioritize remediation, or plan for specific exemptions (with extreme caution).
- Image Hardening: Encourage or mandate the use of hardened container images that run as non-root users, drop unnecessary capabilities, and have minimal attack surface. Tools like Distroless images are excellent for this.
- CI/CD Integration: Integrate PSS checks into your CI/CD pipeline. Use tools like Kyverno or OPA/Gatekeeper to validate Pod manifests against PSS profiles before deployment, providing developers with early feedback. This is also covered in our Sigstore and Kyverno Security guide.
- Monitoring and Alerting: Set up monitoring and alerting for Pod Security Admission audit events. This helps detect attempts to deploy non-compliant Pods, even if they are eventually blocked. eBPF Observability with Hubble can provide deep insights into runtime behavior that complements PSS.
- Exemption Management: If exemptions are necessary, keep them minimal, time-bound, and well-documented. Regularly review and challenge exemptions to see if the underlying application can be refactored to comply.
- Cluster-wide Defaults: Consider setting a cluster-wide default PSS policy in the
PodSecurityConfigurationfile. For example, defaulting to ‘Baseline’ or ‘Restricted’ for all new namespaces ensures a minimum security posture across the cluster. - Version Skew: Be aware of Kubernetes version changes. PSS is GA in 1.25, and behavior might slightly differ in older versions. Always refer to the official Kubernetes PSS documentation for your specific version.
- Service Mesh Integration: While PSS secures the Pod, a service mesh like Istio Ambient Mesh can provide additional layers of security like mTLS, fine-grained authorization, and traffic encryption for Pod-to-Pod communication.
Troubleshooting
Here are common issues you might encounter when implementing Pod Security Standards and their solutions:
-
Pod Rejected with “violates PodSecurity” Error
Issue: You tried to create a Pod, and it was rejected with an error message similar to “violates PodSecurity ‘restricted:latest’: runAsNonRoot != true”.
Solution: The error message is usually very descriptive. It tells you exactly which part of the Pod’s security context violates the applied PSS profile. Review the Pod’s YAML and adjust its
securityContextto comply. For example, if it says “runAsNonRoot != true”, addrunAsNonRoot: trueand arunAsUserto your Pod or container’s security context.# Example fix for runAsNonRoot apiVersion: v1 kind: Pod metadata: name: my-app spec: securityContext: runAsNonRoot: true runAsUser: 1000 # Must be a non-root user containers: - name: my-container image: my-image # ... other settings -
Namespace Labels Not Taking Effect
Issue: You’ve applied PSS labels to a namespace, but Pods that should be rejected are still being admitted (or vice versa).
Solution:
- Check Labels: Double-check that the labels are correctly applied to the namespace using
kubectl get namespace <name> -o yaml. - Admission Controller Enabled: Ensure the Pod Security Admission controller is enabled on your API server. For K8s 1.23+, it should be by default. For older versions, check your
kube-apiservermanifest for the--enable-admission-plugins=...PodSecurity...flag. - Kubernetes Version: PSS behavior might vary slightly across Kubernetes versions. Ensure you’re referencing documentation for your specific version.
- Check Labels: Double-check that the labels are correctly applied to the namespace using
-
“Warning: pods … is forbidden” but Pod is Created
Issue: You see a warning about PSS violations, but the Pod is still created successfully.
Solution: This is expected behavior when the namespace is labeled with
warn=<profile>but notenforce=<profile>. The Pod Security Admission controller is warning you about non-compliance without blocking the deployment. This is useful for auditing and preparing for full enforcement. To block the Pod, change the label toenforce=<profile>.kubectl label namespace <namespace-name> pod-security.kubernetes.io/enforce=restricted --overwrite -
Existing Pods Not Affected by New Policy
Issue: You’ve applied a strict PSS policy to a namespace, but existing running Pods in that namespace are not being terminated or rejected.
Solution: Pod Security Admission only applies to new Pod creation or updates. It does not affect already running Pods. To enforce the new policy on existing deployments, you need to recreate them (e.g., by updating an annotation, scaling down and up, or rolling out a new version). Managed solutions like Karpenter Cost Optimization can help manage node lifecycles, but Pod restarts are still needed for policy re-evaluation.
-
Exemptions Not Working
Issue: You’ve tried to configure an exemption for a ServiceAccount or RuntimeClass, but Pods using it are still being rejected.
Solution: Exemptions are configured at the API server level via the
PodSecurityConfigurationfile.- API Server Configuration: Ensure your
kube-apiserveris configured with the--admission-control-config-fileflag pointing to yourPodSecurityConfiguration. - API Server Restart: Changes to the
PodSecurityConfigurationoften require a restart of thekube-apiserverto take effect. - Correct Exemption Syntax: Double-check the YAML syntax for your exemptions (
serviceAccounts,namespaces,runtimeClasses) in the configuration file. - Managed Kubernetes: For EKS, GKE, AKS, etc., exemption configuration might be handled differently (e.g., via cluster-level settings or specific add-ons). Consult your cloud provider’s documentation.
- API Server Configuration: Ensure your
FAQ Section
-
What are the Kubernetes Pod Security Standards (PSS) and why were they introduced?
The Kubernetes Pod Security Standards (PSS) are built-in, evolving security policies for Pods, replacing the deprecated Pod Security Policies (PSPs). They were introduced to provide a more user-friendly and maintainable way to enforce Pod security best practices directly within the Kubernetes API server. PSS defines three profiles (Privileged, Baseline, Restricted) to help users secure their workloads against common privilege escalation attacks and other vulnerabilities.
-
What’s the difference between ‘Baseline’ and ‘Restricted’ profiles?
The ‘Baseline’ profile is a moderately restrictive policy that aims to prevent known privilege escalations. It disallows actions like running privileged containers, using host namespaces, or escalating privileges. The ‘Restricted’ profile is the most secure, heavily constrained policy, following current Pod hardening best practices. It further requires Pods to run as non-root, drop all capabilities, use a read-only root filesystem, and enforce specific seccomp profiles, among other things. ‘Baseline’ is a good starting point, while ‘Restricted’ is for highly sensitive workloads.
-
How do I apply PSS to a specific namespace?
You apply PSS to a namespace by labeling it with
pod-security.kubernetes.io/enforce=<profile>,pod-security.kubernetes.io/warn=<profile>, and/orpod-security.kubernetes.io/audit=<profile>. For example, to enforce the ‘Restricted’ profile:kubectl label namespace my-app-ns pod-security.kubernetes.io/enforce=restricted -
Can I exempt certain Pods or ServiceAccounts from PSS?
Yes, you can configure exemptions for specific namespaces, RuntimeClasses, or ServiceAccounts. These exemptions are defined in the
PodSecurityConfigurationresource, which is passed to thekube-apiservervia the--admission-control-config-fileflag. This is typically an administrative task and may require a restart of the API server. Exemptions should be used sparingly and with a clear rationale. -
What happens to existing Pods when I apply a new PSS policy to their namespace?
Existing, running Pods are not affected by a new PSS policy applied to their namespace. The Pod Security Admission controller only evaluates Pods at creation or update time. To enforce the new policy on existing workloads, you need to trigger a recreation or update of those Pods (e.g., by performing a rolling update on their Deployment).
Cleanup Commands
To clean up the resources created during this guide, execute the following commands:
kubectl delete namespace pss-test
kubectl delete namespace restricted-apps
kubectl delete -f privileged-pod.yaml -n pss-test # If it was created with warn/audit
kubectl delete -f simple-nginx-pod.yaml -n pss-test # If it was created with warn/audit
kubectl delete -f restricted-nginx-pod.yaml -n restricted-apps
kubectl delete -f hostpath-pod.yaml -n restricted-apps # If it was created via exemption
Verify:
kubectl get namespace pss-test restricted-apps
Expected Output:
Error from server (NotFound): namespaces "pss-test" not found
Error from server (NotFound): namespaces "restricted-apps" not found
Next Steps / Further Reading
Congratulations! You’ve successfully navigated the implementation of Kubernetes Pod Security Standards. To further enhance your cluster’s security posture and deepen your understanding, consider these topics:
- Kubernetes Security Best Practices: Explore the broader landscape of Kubernetes security, including network policies, RBAC, image scanning, and supply chain security. The official Kubernetes Security documentation is an excellent resource.
- Admission Controllers: Dive deeper into other Kubernetes admission controllers like ValidatingAdmissionWebhook and MutatingAdmissionWebhook, which can be used to implement custom policies or modify resources on the fly.
- Policy Engines: Learn about powerful policy engines like Open Policy Agent (OPA) with Gatekeeper or Kyverno. These tools offer much more flexibility and control over policy enforcement than PSS alone, allowing you to define highly specific rules for almost any Kubernetes resource. Our guide on
Was this article helpful?Thanks for your feedback.
