Categories
Cloud Cybersecurity Software Development

Kubernetes Pod Security Standards: Implementation and Best Practices

Kubernetes Pod Security Standards: Real-World Implementation, Pitfalls, and Best Practices

What Are Kubernetes Pod Security Standards?

Kubernetes Pod Security Standards (PSS) are a set of built-in, versioned security profiles that define how workloads can interact with the host and the cluster. These standards help administrators control what actions pods can perform, providing a practical baseline for enforcing security controls. PSS replaces the deprecated PodSecurityPolicy (PSP) mechanism as of Kubernetes v1.25, simplifying the approach to workload security.

There are three main Pod Security Standards, each offering a different level of restriction:

  • Privileged: No restrictions. Pods can do anything the node allows, including accessing the host’s resources and running as root. This profile is not recommended for most workloads, but may be required for some infrastructure-level components such as network plugins or storage daemons.
  • Baseline: Blocks known privilege escalation vectors but is flexible enough for most application workloads. This profile disallows privileged containers, restricts certain host features, but still allows some use of host networking and ports. It aims to balance security with application compatibility.
  • Restricted: Enforces the strictest controls. This profile blocks privileged containers, running as root, use of host network and namespaces, and mandates dropping most Linux capabilities. It is required for highly sensitive or multi-tenant clusters, as well as compliance-focused environments.

PodSecurityAdmission (PSA) is the controller that enforces these standards. It operates by reading labels on namespaces and applying the corresponding security controls to all pods within that namespace. Depending on the configuration, PSA can audit (log violations), warn (notify but allow), or enforce (block non-compliant pods).

# Example pod with strict security context for 'restricted'
apiVersion: v1
kind: Pod
metadata:
  name: compliance-app
spec:
  containers:
    - name: main
      image: registry.company.com/compliance:2.1
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        allowPrivilegeEscalation: false
        capabilities:
          drop:
            - ALL
        seccompProfile:
          type: RuntimeDefault

In this example, the pod will pass the restricted profile because it avoids running as root (runAsNonRoot: true), assigns a specific non-root user (runAsUser: 1001), prohibits privilege escalation (allowPrivilegeEscalation: false), drops all Linux capabilities, and applies the default seccomp profile for syscall filtering. These are all requirements for compliance with the restricted standard, and represent what production security teams should aim for unless a strong use case demands otherwise.

Key concept explanations:

  • Privilege escalation: The ability for a process running in a container to gain increased privileges, potentially compromising the host or cluster.
  • Linux capabilities: Fine-grained permissions that can be added or removed from a process, allowing more granular control over what the process can do.
  • Seccomp profile: A security mechanism for limiting the system calls that a container can make, reducing the attack surface.

Understanding these standards is essential before enforcement. Next, let's look at how to apply and manage these standards in your cluster.

Enforcing Pod Security Standards with PodSecurityAdmission

As of Kubernetes 1.25, PodSecurityAdmission (PSA) is the default method to apply Pod Security Standards. PSA is a built-in admission controller that evaluates pods at creation time, determining whether they meet the specified security requirements. Unlike previous mechanisms, you do not need to install extra webhook configurations or third-party controllers; enforcement is as simple as labeling your namespaces.

# Enforce 'restricted' profile for all pods in the 'prod-secure' namespace
kubectl label namespace prod-secure pod-security.kubernetes.io/enforce=restricted
kubectl label namespace prod-secure pod-security.kubernetes.io/enforce-version=latest

# Audit violations in the 'staging' namespace without blocking pods
kubectl label namespace staging pod-security.kubernetes.io/audit=restricted

# Warn users about violations in the 'dev' namespace, but allow pods
kubectl label namespace dev pod-security.kubernetes.io/warn=restricted

Namespace labels control the policy in three modes:

  • enforce: Violating pods are rejected and not admitted to the namespace.
  • warn: Pods are still accepted, but the user receives a warning message indicating a violation.
  • audit: Violations are only logged for auditing purposes; pods are not blocked or warned.

For example, you might begin by labeling your development namespaces with warn or audit to observe which workloads would be blocked, without disrupting your teams. Once all pods are compliant, you can switch to enforce mode for production.

Practical scenario: Suppose you have a legacy application in the staging namespace. By labeling this namespace with audit=restricted, you can see which pods would violate the restricted policy, review the audit logs, and plan the necessary changes before moving to enforcement.

For the latest Kubernetes documentation on PSA, see Pod Security Admission - Kubernetes.io.

Now that you know how enforcement works, let's examine how different pod specifications interact with these standards and what to expect in real-world usage.

Real-World Examples of Pod Security Standards

Below are practical manifests and their behavior under different Pod Security Standards. These are based on real scenarios you’ll face migrating from PSP, onboarding new teams, or dealing with legacy images.

Example 1: Legacy App with Privileged Container

apiVersion: v1
kind: Pod
metadata:
  name: legacy-priv
spec:
  containers:
  - name: sysadmin
    image: internal/legacy-admin:latest
    securityContext:
      privileged: true
      runAsUser: 0

What happens? In a namespace with enforce=baseline or enforce=restricted, this pod is rejected:

Error from server (Forbidden): pod violates pod security policy: privileged containers are not allowed

Explanation: The privileged: true setting grants the container full access to the host, and runAsUser: 0 means it runs as the root user. Both are explicitly forbidden by baseline and restricted standards. Only namespaces with enforce=privileged permit this configuration.

Practical guidance: If you must run legacy workloads that require privileged access, isolate them in dedicated namespaces and apply strict access controls. Avoid this model for third-party or untrusted workloads.

Example 2: Typical Microservice with Baseline Security

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: flaskapi
    image: registry.company.com/flaskapi:3.2
    ports:
      - containerPort: 8080
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
          - ALL

What happens? This pod will pass baseline enforcement because it disables privilege escalation and drops all extra Linux capabilities. However, restricted requires explicit non-root execution settings (runAsNonRoot: true and runAsUser), which are absent here. To pass restricted, you must add these fields.

Example improvement:

securityContext:
  allowPrivilegeEscalation: false
  runAsNonRoot: true
  runAsUser: 1000
  capabilities:
    drop:
      - ALL

This highlights why reviewing and updating manifests is essential when tightening security standards.

Example 3: Compliant Pod for Restricted Policy

apiVersion: v1
kind: Pod
metadata:
  name: secure-api
spec:
  containers:
  - name: api
    image: registry.company.com/secure-api:1.0
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
      seccompProfile:
        type: RuntimeDefault

What happens? This pod will run in any namespace, regardless of the enforced Pod Security Standard. It meets all requirements: runs as non-root, disables privilege escalation, uses a read-only root filesystem, drops all capabilities, and applies the default seccomp profile. This configuration should be your baseline for new applications.

Common Pitfalls in Production

  • Privileged initContainers: Even if your main containers are secure, a privileged initContainer will cause the pod to be rejected by baseline or restricted.
    Example: An initContainer that sets up environment files with privileged: true will block the entire pod.
  • Third-party images: Many off-the-shelf images run as root by default. You must override runAsNonRoot/runAsUser in the manifest or rebuild the image if it does not support non-root users.
    Example: Public images from Docker Hub often lack proper user settings.
  • HostPath usage: Mounting host directories is forbidden on restricted and discouraged on baseline. You’ll need to refactor storage patterns, such as using PersistentVolumeClaims instead.
  • Network and host namespace sharing: These are blocked by restricted. For workloads that require host networking, isolate them in their own namespace or re-architect to avoid this dependency.

These examples and pitfalls illustrate the impact of Pod Security Standards on real workloads. Understanding these nuances helps teams avoid common issues during migration or enforcement.

Trade-offs, Comparison, and Pitfalls

Choosing the right security profile involves understanding the trade-offs between security, compatibility, and developer effort. The following table summarizes the differences between profiles:

ProfileKey RestrictionsTypical Use CasesDeveloper Effort
PrivilegedNone. Full host access, all capabilities, root allowed.Node management, CNI/CNI plugins, storage daemons.None (but high security risk).
BaselineNo privileged containers, some host features allowed, root discouraged but not forbidden.Most web services, internal apps, legacy workloads.Low: Usually just minor manifest tweaks.
RestrictedNo privileged containers, no root, no hostPath, no host network or pid/ipc, most capabilities dropped, seccomp enforced.Zero-trust clusters, SaaS, fintech, regulated industries.Moderate to high: May require image and code changes, dev retraining.

Performance Impact: The main impact of enforcing stricter Pod Security Standards is on development agility and workload compatibility, rather than on runtime performance. For example, requiring non-root images or disabling host networking may necessitate significant changes in legacy applications. However, the PSA controller itself does not introduce direct CPU or memory overhead.

In addition to built-in PodSecurityAdmission, Kubernetes supports extensible admission controllers. For teams needing more granular or custom policies, alternatives like OPA Gatekeeper and Kyverno are available.

Alternatives: OPA Gatekeeper and Kyverno

OPA Gatekeeper and Kyverno are policy engines that extend Kubernetes admission control with custom rules. They allow you to write policies for scenarios not covered by PSS, such as enforcing specific annotations or restricting allowed image registries. However, adopting these tools introduces extra operational overhead and increases complexity.

Admission ControllerStrengthsWeaknesses
PodSecurityAdmission (PSA)Simple, built-in, low overhead, supports PSS profiles, no extra CRDs.Not customizable beyond the three profiles.
OPA GatekeeperHighly customizable policies, covers more than PSS.Requires management, can impact API server latency, learning curve.
KyvernoUser-friendly policy syntax, supports mutations, policy reports.Extra components to manage, more RBAC setup.

Teams should start with PSA for broad baseline enforcement and only introduce more advanced policy engines if use cases demand customization.

Best Practices for Rolling Out Pod Security Standards

  • Start with audit or warn before moving to enforce. This gives development teams time to adapt and prevents outages due to blocked pods.
    Example: Label non-production namespaces with warn=restricted and monitor compliance reports.
  • Document and share compliant pod templates with your teams to accelerate adoption and reduce errors.
    Example: Provide a YAML library of restricted-compliant manifests.
  • Automate checks in CI/CD pipelines (e.g., kubectl --dry-run or kube-linter) to catch violations before deployment.
    Example: Integrate policy checks in pull request workflows.
  • Review and update base images—use USER in Dockerfiles, drop capabilities, and set seccomp profiles.
    Example: Rebuild images so the application process does not require root.
  • Isolate legacy or privileged workloads in dedicated namespaces, with clear boundaries and access controls.
    Example: Segregate system daemons in a namespace with enforce=privileged and limit user access.

By following these practices, you can roll out Pod Security Standards smoothly and with minimal disruption.

Key Takeaways

Key Takeaways:

  • Pod Security Standards are essential for securing Kubernetes workloads and are enforced by the built-in PodSecurityAdmission controller as of v1.25.
  • Adopt restricted as your default for new production namespaces—it's the best way to avoid privilege escalation and supply chain risks.
  • Legacy workloads may need to stay on baseline or privileged until refactored. Use namespace isolation.
  • Always test enforcement in audit or warn mode before switching to enforce to prevent outages.
  • For specialized security needs, OPA Gatekeeper or Kyverno provide custom controls, but with added complexity.

Additional Resources