Kubernetes Pod Security Standards: Privileged, Baseline, and Restricted Explained

0


Kubernetes gives us a lot of flexibility when defining Pods. That flexibility is powerful, but it can also become dangerous when Pods are allowed to run with more access than they really need.

A Pod can be configured to run as privileged, use the host network namespace, mount host paths, add Linux capabilities, use host ports, disable default security profiles, or run as the root user. Some infrastructure components genuinely need elevated permissions, but most application workloads do not.

Pod Security Standards help solve this problem by giving us predefined security levels for Pods. Instead of inventing a pod security model from scratch, Kubernetes defines three standard profiles:

  • privileged
  • baseline
  • restricted

The goal is not to make every Pod restricted on day one. The practical goal is to apply the least permissive profile that still allows the workload to run correctly.

For many application namespaces, a good starting point is to enforce baseline, while using warn and audit with restricted. This blocks clearly risky configurations, but still gives developers feedback about what should be improved before moving to stricter enforcement.

What Are Pod Security Standards?

Pod Security Standards, often shortened to PSS, are Kubernetes-defined security profiles for Pods. They describe which Pod and container-level settings are allowed or disallowed.

They are not custom resources that you create with kubectl apply. They are predefined policy levels built into the Kubernetes security model.

ProfileRestriction levelTypical use case
privilegedLeast restrictiveTrusted system workloads that need host-level access.
baselineModerate restrictionGeneral-purpose application workloads that should avoid known privilege escalations.
restrictedMost restrictiveHardened workloads, sensitive applications, regulated environments, or namespaces used by lower-trust users.

The profiles move from very permissive to very restrictive. privileged allows almost everything. baseline blocks common privilege escalation paths while staying compatible with many normal containers. restricted follows stronger pod-hardening practices and may require changes to container images and manifests.

Why Pod Security Standards Matter

Without pod-level guardrails, a developer, CI/CD pipeline, Helm chart, or operator can accidentally deploy a Pod with too much power.

Examples of risky configurations include:

  • running a container as privileged
  • mounting the host filesystem with hostPath
  • using the host network namespace with hostNetwork: true
  • using the host process or IPC namespaces
  • adding powerful Linux capabilities
  • allowing privilege escalation
  • running as the root user
  • disabling or weakening seccomp or AppArmor profiles
  • using host ports unnecessarily

These settings do not automatically mean a cluster is compromised. The problem is blast radius. If an application is exploited, excessive Pod privileges can give an attacker more options to escape, escalate, inspect the node, access other workloads, or interfere with the cluster.

Pod Security Standards reduce this risk by making unsafe configurations visible or by blocking them during admission.

Pod Security Standards vs Pod Security Admission

There are two terms that are easy to mix up:

  • Pod Security Standards are the policy profiles: privilegedbaseline, and restricted.
  • Pod Security Admission is the built-in Kubernetes admission controller that evaluates Pods against those profiles.

When a request is sent to the Kubernetes API server, the Pod Security Admission controller can check whether the Pod matches the profile configured for its namespace.

Depending on the configured mode, Kubernetes can:

  • reject the Pod
  • allow the Pod but show a warning
  • allow the Pod but add audit information

This is why Pod Security Standards are useful operationally. You can introduce them gradually instead of immediately blocking every non-compliant workload.

What Happened to PodSecurityPolicy?

Before Pod Security Admission, Kubernetes had a feature called PodSecurityPolicy, usually shortened to PSP.

PodSecurityPolicy was powerful, but it was also difficult to use correctly. It required policy objects, RBAC bindings, and careful ordering. In many clusters it became confusing to understand which policy applied to which user or workload.

PodSecurityPolicy was deprecated in Kubernetes v1.21 and removed in Kubernetes v1.25. Pod Security Admission became the built-in replacement path for standard pod-level security enforcement.

That does not mean Pod Security Admission is a one-to-one replacement for everything PSP could do. It is intentionally simpler. It gives Kubernetes users a standard baseline, but it does not replace every custom policy use case.

For more advanced policy requirements, tools such as Kyverno, OPA Gatekeeper, Kubewarden, or Kubernetes Validating Admission Policy can be used alongside Pod Security Admission.

How Pod Security Admission Is Configured

Pod Security Admission is configured at the namespace level using labels.

The general label format is:

pod-security.kubernetes.io/<MODE>: <LEVEL>

The available modes are:

enforce
warn
audit

The available levels are:

privileged
baseline
restricted

For example, this namespace enforces the baseline profile:

apiVersion: v1
kind: Namespace
metadata:
  name: application
  labels:
    pod-security.kubernetes.io/enforce: baseline

This means that Pods violating the baseline profile will be rejected in this namespace.

Admission Modes: Enforce, Warn, and Audit

Pod Security Admission supports three different modes. Each mode answers a different operational question.

ModeWhat Kubernetes doesUseful for
enforceRejects Pods that violate the configured profile.Hard security boundaries.
warnAllows the Pod but returns a user-facing warning.Developer feedback and migration.
auditAllows the Pod but records an audit annotation.Security monitoring and compliance visibility.

These modes can be combined. This is one of the most useful parts of Pod Security Admission.

A common pattern is:

apiVersion: v1
kind: Namespace
metadata:
  name: app
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

This means:

  • Pods that violate baseline are blocked.
  • Pods that violate restricted still run, but developers see warnings.
  • Violations against restricted are also recorded in audit logs.

This is a good compromise for teams that want to improve security without breaking all workloads at once.

Version Pinning

Pod Security Standards can change between Kubernetes minor versions. For production clusters, it is often safer to pin the policy version explicitly.

Example:

apiVersion: v1
kind: Namespace
metadata:
  name: app
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.36
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.36
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.36

The version labels make the policy behavior more predictable during cluster upgrades.

For a local lab or learning cluster, using the default behavior or latest is usually acceptable. For production, pinning versions helps prevent unexpected policy changes after a Kubernetes upgrade.

The Three Pod Security Profiles

Now let’s go deeper into the three profiles.

Profile 1: Privileged

The privileged profile is intentionally open. It does not impose restrictions.

This profile is mainly for trusted system-level or infrastructure-level workloads. Examples may include:

  • some CNI components
  • some CSI storage components
  • low-level node agents
  • security or monitoring agents that need host access
  • cluster administration workloads

A privileged workload may need access to host namespaces, host devices, kernel-level interfaces, or node paths. That can be legitimate, but it must be treated as high risk.

Do not use privileged as the default profile for application namespaces.

A namespace that allows privileged workloads should have strong RBAC boundaries. Only trusted users, service accounts, and deployment automation should be able to create workloads there.

Example privileged namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: privileged
  labels:
    pod-security.kubernetes.io/enforce: privileged
    pod-security.kubernetes.io/warn: baseline
    pod-security.kubernetes.io/audit: baseline

This namespace allows privileged workloads, but still warns and audits when a Pod violates baseline. That gives visibility without blocking system-level workloads.

Profile 2: Baseline

The baseline profile is designed for broad compatibility with common application workloads while blocking known privilege escalation paths.

This is often the best starting point for regular application namespaces.

Baseline is useful because it blocks clearly dangerous settings, but it usually does not require every container image to be rebuilt immediately.

Baseline disallows or restricts several categories of risky behavior.

Control areaWhat baseline tries to prevent
Host namespacesPods should not share the host network, PID, or IPC namespace.
Privileged containersContainers should not run with privileged: true.
HostPath volumesPods should not mount arbitrary paths from the node filesystem.
Extra capabilitiesContainers should not add capabilities outside the allowed baseline set.
Host portsHost ports should be avoided or tightly controlled.
AppArmorWorkloads should not disable or override safe defaults in unsafe ways.
SELinuxCustom SELinux user and role settings are restricted.
/proc mount typeContainers should use the default masked /proc behavior.
SeccompContainers should not explicitly use Unconfined.
Unsafe sysctlsOnly safe sysctls are allowed.
Windows HostProcessHostProcess containers are not allowed under baseline.
Probe and lifecycle host fieldsSetting custom host fields in probes and lifecycle hooks is restricted in newer Kubernetes versions.

Baseline does not require every workload to run as non-root. It also does not require dropping all capabilities. Those stricter requirements belong to the restricted profile.

That makes baseline a realistic first enforcement level for many existing applications.

Profile 3: Restricted

The restricted profile is the strongest built-in profile.

It includes everything from baseline, and adds stricter controls that align with current pod-hardening practices.

Restricted is a good target for:

  • sensitive application namespaces
  • regulated workloads
  • production environments with strong security requirements
  • namespaces used by external teams or lower-trust users
  • workloads that should follow least privilege as strictly as possible

Restricted commonly requires the following patterns.

RequirementMeaning
allowPrivilegeEscalation: falseThe process should not gain more privileges through mechanisms such as setuid or setgid binaries.
runAsNonRoot: trueContainers must run as a non-root user.
runAsUser must not be 0Containers must not explicitly run as root.
seccompProfile.type: RuntimeDefault or LocalhostSeccomp must be explicitly configured.
capabilities.drop: ["ALL"]Linux capabilities must be dropped.
Only NET_BIND_SERVICE can be added backThe only capability that may be re-added is NET_BIND_SERVICE.
Restricted volume types onlyVolumes must be from the allowed set, such as configMapsecretemptyDirprojectedpersistentVolumeClaimephemeralcsi, or downwardAPI.

Restricted gives better security, but it can expose problems in existing images.

For example, a container image may assume that it runs as root and writes to directories owned by root. If you set runAsNonRoot: true, the Pod may pass admission but fail at runtime because the container process cannot write to the expected path.

This is not a Kubernetes bug. It means the image was not designed to run as a non-root user.

Why a Restricted Pod Can Still Fail at Runtime

Pod Security Admission validates the Pod specification. It does not fully test whether the application inside the container can actually run.

A Pod can pass the restricted policy but still fail because:

  • the image tries to run as root
  • the image needs to bind to port 80 instead of a higher port
  • the process writes to a root-owned directory
  • the application expects writable paths that are read-only
  • the entrypoint script changes file permissions at startup
  • the container expects capabilities that were dropped

This is why moving to restricted is both a Kubernetes task and an application packaging task.

In practice, you often need to adjust Dockerfiles, file ownership, application ports, and writable directories.

Workload Resources and Pod Templates

Most Pods are not created directly. They are created by workload controllers such as:

  • Deployments
  • StatefulSets
  • DaemonSets
  • Jobs
  • CronJobs
  • ReplicaSets

These workload resources contain a Pod template.

Pod Security Admission has an important nuance here:

  • warn and audit checks are applied to workload resources so violations can be caught early.
  • enforce is applied to the resulting Pod objects, not directly to the workload object itself.

This means you may see warnings when creating a Deployment, but the hard rejection happens when the controller tries to create the actual Pod.

That nuance is useful when troubleshooting. If a Deployment exists but no Pods are created, check the ReplicaSet events and namespace policy labels.

Useful commands:

kubectl describe deployment <deployment-name> -n <namespace>
kubectl describe rs -n <namespace>
kubectl get events -n <namespace> --sort-by=.lastTimestamp

Lab Overview

Let’s create a small lab that demonstrates the most important cases.

We will create three namespaces:

  • privileged
  • baseline
  • restricted

Then we will test different Pod manifests against them.

Suggested folder structure:

pod-security-standards/
├── namespaces/
│   ├── privileged.yaml
│   ├── baseline.yaml
│   └── restricted.yaml
├── pods/
│   ├── privileged-pod.yaml
│   ├── baseline-pod.yaml
│   ├── restricted-nginx-root.yaml
│   └── restricted-nginx-unprivileged.yaml
└── deployments/
    └── restricted-deployment.yaml

Create the Namespaces

Create namespaces/privileged.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: privileged
  labels:
    pod-security.kubernetes.io/enforce: privileged
    pod-security.kubernetes.io/warn: baseline
    pod-security.kubernetes.io/audit: baseline

Create namespaces/baseline.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: baseline
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

Create namespaces/restricted.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: restricted
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

Apply the namespaces:

kubectl apply -f namespaces/

Check the labels:

kubectl get namespaces --show-labels

Example 1: A Privileged Pod

Create pods/privileged-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-privileged
spec:
  containers:
    - name: nginx
      image: nginx:1.27.0
      ports:
        - containerPort: 80
      securityContext:
        privileged: true

Apply it to the privileged namespace:

kubectl apply -f pods/privileged-pod.yaml -n privileged

The Pod should be created, but you should see a warning because the namespace is configured to warn against baseline violations.

Expected behavior:

Warning: would violate PodSecurity "baseline:latest": privileged ...
pod/nginx-privileged created

Now try to apply the same Pod to the baseline namespace:

kubectl apply -f pods/privileged-pod.yaml -n baseline

Expected behavior:

Error from server (Forbidden): error when creating ... violates PodSecurity "baseline:latest" ...

This demonstrates the difference between warn and enforce.

In the privileged namespace, the Pod is allowed but warned. In the baseline namespace, the Pod is blocked.

Example 2: A Simple Baseline-Compatible Pod

Create pods/baseline-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-baseline
spec:
  containers:
    - name: nginx
      image: nginx:1.27.0
      ports:
        - containerPort: 80

Apply it to the baseline namespace:

kubectl apply -f pods/baseline-pod.yaml -n baseline

The Pod should be accepted by the baseline enforcement policy.

However, because the namespace warns against restricted, Kubernetes may show warnings about missing restricted-level settings, such as:

  • allowPrivilegeEscalation is not set to false
  • capabilities are not dropped
  • runAsNonRoot is not set
  • seccompProfile is not set

This is exactly why warn: restricted is useful. Developers can deploy the workload, but they also get immediate feedback about how to harden it.

Example 3: A Restricted Pod Using the Default NGINX Image

Now create a restricted-style Pod using the regular nginx image.

Create pods/restricted-nginx-root.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-restricted-root-image
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: nginx
      image: nginx:1.27.0
      ports:
        - containerPort: 80
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop:
            - ALL

Apply it to the baseline namespace:

kubectl apply -f pods/restricted-nginx-root.yaml -n baseline

This Pod may pass Pod Security Admission because the manifest contains the expected restricted fields.

But it can fail during container creation because the regular NGINX image commonly expects to run as root.

Check the Pod:

kubectl get pods -n baseline

Describe it:

kubectl describe pod nginx-restricted-root-image -n baseline

You may see an error similar to:

container has runAsNonRoot and image will run as root

This is a very important lesson. Pod Security Admission validates policy. It does not magically make an image compatible with non-root execution.

Example 4: A Restricted Pod Using an Unprivileged Image

Now use an image designed to run without root privileges.

Create pods/restricted-nginx-unprivileged.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-restricted
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 101
    runAsGroup: 101
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: nginx
      image: nginxinc/nginx-unprivileged:1.27.1
      ports:
        - containerPort: 8080
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop:
            - ALL

Apply it to the restricted namespace:

kubectl apply -f pods/restricted-nginx-unprivileged.yaml -n restricted

Check the Pod:

kubectl get pods -n restricted

This Pod should be much more compatible with the restricted profile because:

  • it runs as a non-root user
  • it drops all Linux capabilities
  • it disables privilege escalation
  • it explicitly uses the runtime default seccomp profile
  • it listens on port 8080 instead of privileged port 80

This is the kind of adjustment that is often needed when moving applications from baseline to restricted.

Example 5: Deployment Behavior

Create deployments/restricted-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-restricted
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-restricted
  template:
    metadata:
      labels:
        app: nginx-restricted
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 101
        runAsGroup: 101
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: nginx
          image: nginxinc/nginx-unprivileged:1.27.1
          ports:
            - containerPort: 8080
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL

Apply it:

kubectl apply -f deployments/restricted-deployment.yaml -n restricted

Check the Deployment and Pods:

kubectl get deployment -n restricted
kubectl get pods -n restricted

If the Deployment is created but Pods do not appear, check the ReplicaSet and events:

kubectl describe rs -n restricted
kubectl get events -n restricted --sort-by=.lastTimestamp

This helps distinguish between workload object creation and actual Pod admission.

Testing Namespace Label Changes Safely

When applying Pod Security labels to existing namespaces, do not immediately enforce strict policies without testing.

Use server-side dry run first:

kubectl label --dry-run=server --overwrite ns baseline \
  pod-security.kubernetes.io/enforce=restricted

Kubernetes will evaluate existing Pods and return warnings without actually changing the namespace.

For all namespaces, you can start with warnings and audit annotations:

kubectl label --overwrite ns --all \
  pod-security.kubernetes.io/warn=baseline \
  pod-security.kubernetes.io/audit=baseline

Then list namespaces that do not yet have an explicit enforce policy:

kubectl get namespaces --selector='!pod-security.kubernetes.io/enforce'

This gives you an inventory of namespaces that still need a decision.

Cluster-Wide Defaults

Namespace labels are the most common way to configure Pod Security Admission, but the admission controller can also be configured with cluster-wide defaults.

A kube-apiserver admission configuration can define default levels and exemptions.

Example:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: PodSecurity
    configuration:
      apiVersion: pod-security.admission.config.k8s.io/v1
      kind: PodSecurityConfiguration
      defaults:
        enforce: "baseline"
        enforce-version: "latest"
        audit: "restricted"
        audit-version: "latest"
        warn: "restricted"
        warn-version: "latest"
      exemptions:
        usernames: []
        runtimeClasses: []
        namespaces:
          - kube-system

This file must be provided to the API server using the appropriate admission-control configuration flag.

Managed Kubernetes providers may not expose this configuration directly. In that case, namespace labels are usually the practical method.

Exemptions

Pod Security Admission supports static exemptions.

Exemptions can be based on:

  • usernames
  • runtime class names
  • namespaces

Be careful with exemptions. They skip all Pod Security Admission behavior, including enforcewarn, and audit.

Avoid broad exemptions for controller service accounts. Most Pods are created indirectly by controllers. If you exempt a controller service account too broadly, you may accidentally exempt many workloads created by normal users.

A safer pattern is to isolate privileged workloads into dedicated namespaces and protect those namespaces with RBAC.

Protect Privileged Namespaces with RBAC

Pod Security Standards control which Pod specs are allowed in a namespace. They do not decide who can create workloads in that namespace.

RBAC is still required.

For example, you may have a namespace called privileged where privileged workloads are allowed, but only platform administrators should deploy there.

Example Role for managing Pods in the privileged namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: privileged
  name: privileged-workload-manager
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "pods/exec"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: ["apps"]
    resources: ["deployments", "daemonsets", "replicasets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Example RoleBinding:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: privileged
  name: platform-admins-privileged-workloads
subjects:
  - kind: Group
    name: platform-admins
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: privileged-workload-manager
  apiGroup: rbac.authorization.k8s.io

This is the important combination:

  • Pod Security Admission defines what is allowed in the namespace.
  • RBAC defines who can create or change workloads in the namespace.

You usually need both.

Practical Namespace Strategy

A practical production strategy could look like this.

Namespace typeRecommended policy
System namespacesUsually privileged, with tight RBAC and audit visibility.
General application namespacesenforce: baselinewarn: restrictedaudit: restricted.
New application namespacesStart with restricted when the platform supports it.
Sensitive workloadsenforce: restricted.
Third-party Helm chartsStart with warn and audit, then enforce after validation.
Legacy workloadsUse baseline first, fix warnings, then move toward restricted.

For many teams, the migration path is:

  1. Add warn and audit labels first.
  2. Collect violations.
  3. Fix the most common manifest and image issues.
  4. Enforce baseline.
  5. Keep warning and auditing against restricted.
  6. Move selected namespaces to restricted once workloads are compatible.

Common Violations and Fixes

ProblemTypical message or symptomFix
Privileged containerViolates baseline because privileged=true.Remove privileged: true, or move the workload to a tightly controlled privileged namespace.
HostPath volumeViolates baseline because hostPath is set.Use persistentVolumeClaimconfigMapsecretemptyDir, or another safer volume type.
Host networkViolates baseline because hostNetwork=true.Use a Service, Ingress, or other Kubernetes networking pattern.
Added capabilityCapability is not allowed by the profile.Remove the capability or justify a different namespace/profile.
Missing allowPrivilegeEscalation: falseWarning against restricted.Add allowPrivilegeEscalation: false to each container security context.
Missing capability dropWarning against restricted.Add capabilities.drop: ["ALL"].
Missing seccomp profileWarning or rejection under restricted.Set seccompProfile.type: RuntimeDefault at Pod or container level.
Root imageRuntime error with runAsNonRoot.Use a non-root image or update the Dockerfile to use a non-root USER.
Port 80 as non-rootApplication fails to bind to port 80.Listen on a high port such as 8080 and expose it through a Service.
Writable root-owned pathsContainer starts but crashes.Fix image ownership, use writable volumes, or configure the app to write to allowed paths.

Security Context Example for Restricted Workloads

A reusable baseline for restricted Linux workloads can look like this:

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    runAsGroup: 10001
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: example/app:1.0.0
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop:
            - ALL

This does not guarantee the application will run. The image must also be compatible with non-root execution.

A matching Dockerfile pattern might look like this:

FROM python:3.12-slim

RUN groupadd --gid 10001 app \
    && useradd --uid 10001 --gid 10001 --create-home app

WORKDIR /app
COPY --chown=10001:10001 . /app

USER 10001:10001

EXPOSE 8080
CMD ["python", "-m", "myapp"]

The Pod manifest and the image need to agree with each other.

What Pod Security Standards Do Not Cover

Pod Security Standards are useful, but they are not a complete Kubernetes security solution.

They do not replace:

  • RBAC
  • NetworkPolicies
  • image scanning
  • image signing and verification
  • runtime threat detection
  • secret management
  • node hardening
  • Kubernetes audit logging
  • admission policies for custom organization rules
  • resource quotas and limit ranges
  • supply-chain security

They also do not mutate workloads. For example, Pod Security Admission will not automatically add allowPrivilegeEscalation: false for you. It only evaluates the request and then allows, warns, audits, or rejects.

If you want automatic mutation or custom organization-specific rules, use an admission policy tool such as Kyverno, OPA Gatekeeper, Kubewarden, or native Kubernetes admission policy features where appropriate.

When to Use Kyverno, Gatekeeper, or Kubewarden

Pod Security Admission is excellent for standard pod security levels. It is not designed for every custom rule.

Use an additional policy engine when you need rules such as:

  • only images from approved registries are allowed
  • images must use immutable digests instead of mutable tags
  • Pods must define CPU and memory requests
  • Deployments must have certain labels
  • Services of type LoadBalancer are restricted
  • host ports are allowed only from a specific list
  • containers must not use the latest tag
  • security contexts should be automatically injected
  • namespace-specific exceptions need more flexibility

Pod Security Standards give you the standard security floor. A policy engine gives you more detailed organization-specific governance.

Recommended Rollout Plan

A safe rollout plan for existing clusters:

  1. Inventory namespaces.

    kubectl get namespaces --show-labels
    
  2. Start with warn and audit.

    kubectl label --overwrite ns --all \
      pod-security.kubernetes.io/warn=baseline \
      pod-security.kubernetes.io/audit=baseline
    
  3. Review warnings and audit logs.

    Focus on common problems: privileged containers, host paths, host namespaces, and unsafe capabilities.

  4. Enforce baseline for application namespaces.

    kubectl label --overwrite ns app \
      pod-security.kubernetes.io/enforce=baseline \
      pod-security.kubernetes.io/warn=restricted \
      pod-security.kubernetes.io/audit=restricted
    
  5. Fix restricted warnings.

    Update manifests and rebuild images where needed.

  6. Move compatible namespaces to restricted.

    kubectl label --overwrite ns sensitive-app \
      pod-security.kubernetes.io/enforce=restricted \
      pod-security.kubernetes.io/warn=restricted \
      pod-security.kubernetes.io/audit=restricted
    
  7. Protect privileged namespaces with RBAC.

    Do not allow every developer or pipeline to deploy workloads into namespaces that allow privileged Pods.

  8. Add complementary controls.

    Add NetworkPolicies, RBAC reviews, image scanning, runtime monitoring, and custom admission policies.

Cleanup Commands

Delete the lab Pods:

kubectl delete pod --all -n privileged
kubectl delete pod --all -n baseline
kubectl delete pod --all -n restricted

Delete the Deployment:

kubectl delete deployment --all -n restricted

Delete the namespaces:

kubectl delete namespace privileged baseline restricted

Key Takeaways

Pod Security Standards give Kubernetes a common language for pod-level security.

Use privileged only for trusted system workloads that truly require elevated access. Protect those namespaces with RBAC.

Use baseline as a realistic minimum for many general-purpose application namespaces. It blocks common privilege escalation paths without breaking as many existing workloads as restricted.

Use restricted for hardened workloads and as the long-term target for new applications. Expect to update container images, ports, writable paths, and security contexts.

Use warn and audit before strict enforcement. They are extremely useful for migration because they show what would break before you block workloads.

Do not treat Pod Security Standards as the only security mechanism. Combine them with RBAC, NetworkPolicies, image security, audit logging, and additional admission policies where needed.

The best practical default for many application namespaces is:

pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted

Then move workloads toward restricted whenever the application and image are ready.

References

  • Kubernetes: Pod Security Standards - kubernetes[.]io/docs/concepts/security/pod-security-standards/
  • Kubernetes: Pod Security Admission - kubernetes[.]io/docs/concepts/security/pod-security-admission/
  • Kubernetes: Enforce Pod Security Standards with Namespace Labels - kubernetes[.]io/docs/tasks/configure-pod-container/enforce-standards-namespace-labels/
  • Kubernetes: Enforce Pod Security Standards by Configuring the Built-in Admission Controller - kubernetes[.]io/docs/tasks/configure-pod-container/enforce-standards-admission-controller/
  • Kubernetes: Pod Security Policies - kubernetes[.]io/docs/concepts/security/pod-security-policy/
  • Kubernetes Blog: Pod Security Admission Controller in Stable - kubernetes[.]io/blog/2022/08/25/pod-security-admission-stable/
  • Kubernetes Enhancements: KEP-2579 Pod Security Admission Control - github[.]com/kubernetes/enhancements/blob/master/keps/sig-auth/2579-psp-replacement/README.md
  • Kyverno: Pod Security Standards - kyverno[.]io/docs/guides/pod-security/

Post a Comment

0 Comments

Post a Comment (0)

#buttons=(Ok, Go it!) #days=(20)

This site uses cookies from Google to deliver its services and analyze traffic. Your IP address and user-agent are shared with Google along with performance and security metrics to ensure quality of service, generate usage statistics, and to detect and address abuse. More Info
Ok, Go it!