GitHub EN PT

K8s by Example: Pod Security Standards

Pod Security Standards (PSS) replace the deprecated PodSecurityPolicy. They define three security levels: Privileged (unrestricted), Baseline (prevent known escalations), Restricted (hardened). Enforce via namespace labels without installing additional controllers.

namespace-baseline.yaml

Apply security standards via namespace labels. The pod-security.kubernetes.io/enforce label sets the required level. Pods violating the policy are rejected.

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

Three modes: enforce (reject), audit (log), warn (warn user). Use audit/warn to test before enforcing.

metadata:
  labels:
    # Reject violations
    pod-security.kubernetes.io/enforce: baseline
    # Log violations (for monitoring)
    pod-security.kubernetes.io/audit: restricted
    # Warn on kubectl apply
    pod-security.kubernetes.io/warn: restricted
security-levels.yaml

Privileged: No restrictions. Use for system components that need full access (CNI plugins, monitoring agents).

# Privileged: allows everything
# - hostNetwork, hostPID, hostIPC
# - privileged containers
# - any capabilities
# - any volumes
# - run as root

apiVersion: v1
kind: Namespace
metadata:
  name: kube-system
  labels:
    pod-security.kubernetes.io/enforce: privileged

Baseline: Prevents known privilege escalations. Reasonable default for most workloads. Blocks hostNetwork, privileged, and dangerous capabilities.

# Baseline blocks:
# - hostNetwork, hostPID, hostIPC
# - privileged: true
# - capabilities: SYS_ADMIN, NET_ADMIN, etc.
# - hostPath volumes
# - hostPorts

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

Restricted: Maximum security. Requires non-root, drops all capabilities, read-only root filesystem. Use for sensitive workloads.

# Restricted additionally requires:
# - runAsNonRoot: true
# - allowPrivilegeEscalation: false
# - capabilities.drop: ALL
# - seccompProfile: RuntimeDefault
# - runAsUser: non-zero

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
pod-baseline-compliant.yaml

A baseline-compliant pod. No hostNetwork, no privileged mode, no dangerous capabilities. Most applications work with baseline.

apiVersion: v1
kind: Pod
metadata:
  name: app
  namespace: staging       # baseline enforced
spec:
  containers:
    - name: app
      image: myapp:v1
      ports:
        - containerPort: 8080
      securityContext:
        allowPrivilegeEscalation: false
        # No privileged: true
        # No hostNetwork, hostPID, hostIPC
pod-restricted-compliant.yaml

A restricted-compliant pod. Runs as non-root, drops all capabilities, uses seccomp. This is the most secure configuration.

apiVersion: v1
kind: Pod
metadata:
  name: app
  namespace: production    # restricted enforced
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: myapp:v1
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL
      volumeMounts:
        - name: tmp
          mountPath: /tmp
  volumes:
    - name: tmp
      emptyDir: {}
terminal

When a pod violates the policy, it’s rejected with a clear error message explaining which fields are non-compliant.

$ kubectl apply -f privileged-pod.yaml
Error from server (Forbidden): pods "bad-pod" is forbidden:
  violates PodSecurity "baseline:latest":
  privileged (container "app" must not set
    securityContext.privileged=true)

Use —dry-run=server to test pods against namespace policies without creating them.

$ kubectl apply -f pod.yaml --dry-run=server
pod/app created (server dry run)

# Check warnings for restricted policy
$ kubectl apply -f pod.yaml
Warning: would violate PodSecurity "restricted:latest":
  runAsNonRoot must be true
pod/app created  # Baseline allowed, restricted warned
terminal

Audit which namespaces have security policies. Namespaces without labels default to privileged (no restrictions).

$ kubectl get ns --show-labels | grep pod-security
default      pod-security.kubernetes.io/warn=baseline
kube-system  pod-security.kubernetes.io/enforce=privileged
production   pod-security.kubernetes.io/enforce=restricted
staging      pod-security.kubernetes.io/enforce=baseline

Apply baseline to all new namespaces by default using an admission controller or GitOps policy.

# Add policy to existing namespace
$ kubectl label ns myapp \
    pod-security.kubernetes.io/enforce=baseline \
    pod-security.kubernetes.io/warn=restricted

# Check what would be blocked
$ kubectl get pods -n myapp -o yaml | \
    kubectl apply -f - --dry-run=server 2>&1 | \
    grep -i "forbidden\|warning"
exemptions.yaml

Some system components need exemptions. Configure the API server with —admission-control-config-file to exempt specific users, namespaces, or runtime classes.

# /etc/kubernetes/admission-control-config.yaml
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
        audit: restricted
        warn: restricted
      exemptions:
        usernames:
          - system:serviceaccount:kube-system:*
        namespaces:
          - kube-system
          - istio-system
        runtimeClasses:
          - gvisor

Index | Use arrow keys to navigate