Skip to content
Pod Security

Pod Security

Podplane runs Pods with secure defaults, and lets you relax them where a workload genuinely needs more access. Two built-in Kubernetes mechanisms work together:

  • Pod Security Admission enforces a Pod Security Standard level per namespace: privileged, baseline, or restricted.
  • If you deploy a Pod which has not set security settings, a mutating admission policy will fills in secure defaults, so ordinary Pods meet restricted without any configuration.

Defaults

Namespaces without a pod-security.kubernetes.io/enforce label, which includes every namespace you create, enforce baseline by default. Pods that do not also meet restricted are allowed, and the API server records them in its audit log.

baseline blocks the settings that give a Pod access to its node: hostPath volumes, privileged containers, host networking, PID and IPC namespaces, host ports, and added capabilities beyond a small safe set.

In every namespace except privileged ones and kube-system, Podplane also fills in these settings with secure defaults when a Pod omits them:

SettingValue
Pod seccompProfileRuntimeDefault
Container allowPrivilegeEscalationfalse
Container capabilitiesdrop: ["ALL"]
Container runAsNonRoottrue

Those settings are never changed if a Pod sets them explicitly. The web and worker deployment templates set these values by default themselves also.

Platform Namespaces

Each platform-* namespace declares its own Pod Security level. Most are restricted. These components need host access and run in privileged namespaces:

ComponentWhy
ciliumManages node networking: privileged containers, host networking, hostPath mounts, and added capabilities
corednsMounts the node’s systemd-resolved resolv.conf for upstream DNS
csi-aws-ebsAttaches and mounts volumes on the node
secrets-store-csi-driverMounts secrets into Pods on the node
secrets-store-csi-provider-*Mount the CSI driver’s provider socket directory
envoy-gatewayBinds host port 443 and, with node certificates, mounts them from the node
node-problem-detectorReads node logs, the journal, and the containerd socket

kube-system is exempt from Pod Security, because the VM-managed tunnel static Pod runs there with host networking.

Only admins can create kube-* and platform-* namespaces or change namespace labels, so other groups cannot use these levels. See RBAC.

Relaxing the Defaults

Images that run as root

This is the most common case. Many public images, and images built FROM language base images without a USER, run as root. With the defaults, their containers fail to start with container has runAsNonRoot and image will run as root.

Prefer a non-root image: add a numeric USER to the Dockerfile. If the image must run as root, opt out for that app:

podplane deploy web --name nginx --image nginx \
  --set podSecurityContext.runAsNonRoot=false

Some root images also need default Linux capabilities, for example to switch users or change file ownership. Restore them too:

podplane deploy web --name nginx --image nginx \
  --set podSecurityContext.runAsNonRoot=false \
  --set-json 'containerSecurityContext={"allowPrivilegeEscalation":false,"capabilities":{}}'

In your own manifests, set runAsNonRoot: false and capabilities: {} explicitly. Unlabelled namespaces allow this; restricted namespaces do not.

Workloads that need host access

Node agents such as monitoring exporters, log or security agents, storage drivers, and GPU device plugins usually need hostPath volumes, host networking, or privileged containers. Docker-in-Docker CI builds need privileged containers. baseline rejects these Pods.

An admin can run them in a dedicated namespace labelled privileged, which also disables the secure defaults there:

kubectl create namespace monitoring
kubectl label namespace monitoring pod-security.kubernetes.io/enforce=privileged

Keep such namespaces separate from application namespaces. Anyone who can create Pods in a privileged namespace can gain control of its nodes.

Stricter namespaces

Label a namespace restricted to reject Pods that opt out of the defaults:

kubectl label namespace payments pod-security.kubernetes.io/enforce=restricted

Platform components and cluster-wide defaults

Platform component levels and the secure defaults policy are set in the platform-components HelmRelease values, which Flux reconciles:

kubectl edit helmrelease platform-components -n platform-components
spec:
  values:
    platform:
      components:
        apps:
          zot-registry:
            podSecurity: baseline
        values:
          platform-rbac:
            platform:
              rbac:
                admissionPolicies:
                  podSecurityDefaults:
                    enabled: false

Disabling podSecurityDefaults restores Kubernetes defaults cluster-wide; the baseline level is still enforced.

Troubleshooting

When podplane deploy times out because of Pod Security, it explains which setting was rejected or which container cannot start, and how to fix it.

For other workloads, check the Deployment and Pods:

kubectl describe deployment <name>
kubectl get pods -l app.kubernetes.io/instance=<name>

A Pod Security rejection appears as a FailedCreate event and a ReplicaFailure condition containing violates PodSecurity. A root image shows a CreateContainerConfigError container status.