The platform sets no default Pod Security level. Every namespace runs at `privileged` (no restrictions) until you label it, so roll out baseline or restricted audit-first, without breaking running workloads. ## Prerequisites - `kubectl` access to your workload cluster - A namespace with workloads deployed that you want to harden ## Levels and modes Pod Security Standards (PSS) is a built-in Kubernetes feature that restricts what pods can do inside a namespace. **Levels** go from least to most strict: - `privileged`: no restrictions. This is the default. - `baseline`: blocks well-known dangerous settings such as privileged containers and host namespace access. - `restricted`: hardened. Pods must run as non-root, containers must drop all Linux capabilities (fine-grained kernel privileges) with only `NET_BIND_SERVICE` allowed back, and must use a seccomp (secure computing mode) profile set to `RuntimeDefault`. Both `baseline` and `restricted` forbid `appArmorProfile: Unconfined`, so once a namespace is at `baseline` or above, a pod cannot opt out of the default [AppArmor profile](/docs/hetzner/apalla/security/use-apparmor). **Modes** control what happens when a pod violates the level: - `enforce`: reject the pod. - `audit`: allow the pod and record the violation in the API audit log. - `warn`: allow the pod and return a warning to whoever submitted it. You can set enforce, audit, and warn at once on the same namespace. Aim for `restricted` on your application namespaces. Use `baseline` when a workload genuinely needs a setting that `restricted` forbids. ## Why baseline is the minimum The node hardening does not reach privileged pods. The runtime's default [AppArmor profile](/docs/hetzner/apalla/security/use-apparmor) confines non-privileged containers only; a privileged container has full host access on its node, so there is nothing left for a profile to protect. And every namespace starts at `privileged`, so in an unlabeled namespace anyone who can create a pod can set `privileged: true` and bypass that hardening. Labeling the namespace at `baseline` or above rejects privileged containers and host namespace access at admission. That label is what makes the node hardening bind to every workload in the namespace. A privileged pod is still limited. It gets host-level access on the node it runs on, not root on every node in the cluster. On that node, the [sealed OS](/docs/hetzner/apalla/security/node-and-os-security) blocks changes to the base system and tamper detection flags changes to the writable area, which limits what an attacker can quietly persist. ## Roll out a stricter level Audit first, enforce later Label the namespace to warn and audit at your target level, but do not enforce yet. This shows you what would be rejected without blocking anything currently running: ```console $ kubectl label namespace team-a \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/audit=restricted ``` Redeploy your workloads, or trigger pod restarts, and read the warnings. Fix the workloads that violate the level. The most common fix is adding a `securityContext` block to the pod spec: ```yaml securityContext: runAsNonRoot: true allowPrivilegeEscalation: false capabilities: {drop: ["ALL"]} seccompProfile: {type: RuntimeDefault} ``` See [Set security contexts](/docs/hetzner/apalla/security/set-security-contexts) for what each field does and how to apply it. Enforce Once warnings are clean, add `enforce`: ```console $ kubectl label namespace team-a \ pod-security.kubernetes.io/enforce=restricted \ --overwrite ``` From this point, a pod that violates `restricted` is rejected at admission before it starts. Verify ```console # Show the labels on the namespace: $ kubectl get namespace team-a -o jsonpath='{.metadata.labels}' | jq . # A privileged pod should now be rejected: $ kubectl -n team-a run bad --image=busybox --privileged --restart=Never -- sleep 1 # Expected: Error from server (Forbidden): ... ``` ## What to know **Platform namespaces must stay at `privileged`.** Platform components need host access to work, so those namespaces are the residual you accept. What bounds it is [RBAC](/docs/hetzner/apalla/security/manage-access-and-tenancy): who can create pods in a platform namespace. Apply PSS only to your own namespaces. **PSS is coarse and namespace-scoped.** For rules that must apply across namespaces (for example, "images must come from this registry" or "every pod needs a cost label"), write a native ValidatingAdmissionPolicy: a Kubernetes policy that runs inside the API server and can reject a resource outright. See [Control admission](/docs/hetzner/apalla/security/control-admission) for how it differs from a webhook. **Combine with other controls.** Pair a PSS level with a default-deny NetworkPolicy (see [Segment with network policies](/docs/hetzner/apalla/security/segment-with-network-policies)) and a ResourceQuota or LimitRange. Together they address the main risks in a shared namespace. ## Related - [Set security contexts](/docs/hetzner/apalla/security/set-security-contexts) - [Control admission](/docs/hetzner/apalla/security/control-admission) - [Segment with network policies](/docs/hetzner/apalla/security/segment-with-network-policies) - [Workload runtimes and isolation](/docs/hetzner/apalla/security/workload-runtimes-and-isolation) - [Security architecture](/docs/hetzner/apalla/security/security-architecture)