Grundidee: drei fertige Stufen
Die Pod Security Standards sind drei vordefinierte Regelsätze, die das Sicherheitsspektrum grob abdecken – von „alles erlaubt“ bis „nur das Nötigste“. Sie sind kumulativ: jede Stufe enthält alles aus der darunter.
Die drei Stufen im Aufbau
Wer Restricted erfüllt, erfüllt automatisch auch Baseline. Die Stufen sind keine Alternativen zueinander, sondern Verschärfungen.
Die drei Stufen im Vergleich
Wofür jede Stufe gedacht ist und wer sie typischerweise braucht.
| Stufe | Bedeutung | Gedacht für |
|---|---|---|
| Privileged | Bewusst offen, ohne jede Einschränkung. Erlaubt auch bekannte Wege, die Container-Isolation zu umgehen – etwa direkten Zugriff auf das Host-Netzwerk. | System- und Infrastruktur-Workloads, die von vertrauenswürdigen Admins betrieben werden: CNI-Plugins, Storage-Treiber, Monitoring-Agents |
| Baseline | Minimal einschränkend. Verhindert bekannte Wege der Rechte-Ausweitung, lässt aber eine normale Pod-Konfiguration ohne Anpassung durchgehen. | Anwendungsbetrieb und Entwicklung nicht-kritischer Anwendungen |
| Restricted | Stark einschränkend, folgt dem aktuellen Stand der Pod-Härtung – auf Kosten der Kompatibilität mit manchen Images. | Sicherheitskritische Anwendungen und Umgebungen mit weniger vertrauenswürdigen Nutzern |
Welche Stufe passt?
Die ehrliche Kurzfassung: Restricted ist das Ziel für alles, was eine normale Anwendung ist. Baseline ist die Zwischenstation, wenn ein Image noch nicht mitspielt. Privileged bleibt der begründete Sonderfall in einem eigenen Namespace.
Standard oder securityContext – was ist was?
Die häufigste Anfängerverwirrung. Beide Begriffe reden über dieselben Felder, aber aus entgegengesetzter Richtung.
securityContext
Die Einstellung. Steht im Pod-Manifest und legt fest, wie dieser eine Pod läuft: als welcher Benutzer, mit welchen Capabilities, mit welchem seccomp-Profil.
Geschrieben vom Team, das die Anwendung deployt.
Pod Security Standard
Die Regel. Legt fest, welche Werte im securityContext überhaupt erlaubt sind – und welche zu einer Ablehnung führen.
Gesetzt vom Team, das den Cluster oder den Namespace verantwortet.
Und wer prüft das tatsächlich?
Der Standard ist zunächst nur ein Dokument. Durchgesetzt wird er vom Pod Security Admission Controller – der ist seit Kubernetes 1.25 fest eingebaut und muss nicht installiert werden. Er liest das Namespace-Label, prüft jeden neuen Pod dagegen und lehnt ab, was nicht passt.
Pod-Manifest wird eingereicht
│
▼
Pod Security Admission liest das Namespace-Label
│
▼
Prüfung des securityContext gegen die Stufe
│
├── passt → Pod wird erstellt
└── passt nicht → Ablehnung mit Begründung
Baseline: was verboten wird
Baseline sperrt die bekannten Wege, mit denen ein Container die Isolation zur Node durchbricht. Eine gewöhnliche Anwendung merkt davon in der Regel nichts.
Die Verbote in Alltagssprache
| Bereich | Was Baseline verlangt |
|---|---|
| Host-Namespaces | Kein Mitbenutzen von Netzwerk, Prozessliste oder IPC der Node (hostNetwork, hostPID, hostIPC) |
| Privilegierte Container | privileged: true ist nicht erlaubt |
| Capabilities | Nur ein fester Satz gängiger Capabilities darf hinzugefügt werden – alles darüber hinaus ist gesperrt |
| HostPath-Volumes | Keine Verzeichnisse der Node in den Pod mounten |
| Host-Ports | Möglichst gar nicht, sonst nur eine bekannte Liste |
| Probes und Lifecycle-Hooks | Das host-Feld darin darf nicht gesetzt werden (seit 1.34) |
| AppArmor | Das Standardprofil darf nicht abgeschaltet oder beliebig überschrieben werden |
| SELinux | Nur bestimmte Typen erlaubt; eigener SELinux-User oder eigene Rolle sind verboten |
/proc-Mount | Muss auf der Voreinstellung bleiben, damit die Schutzmasken greifen |
| Seccomp | Darf nicht ausdrücklich auf Unconfined gesetzt werden |
| Sysctls | Nur ein kleiner Satz als sicher eingestufter Kernel-Parameter |
| HostProcess (Windows) | Privilegierter Zugriff auf den Windows-Host ist nicht erlaubt |
Welche Capabilities Baseline zulässt
Hinzugefügt werden dürfen nur diese – Namen jeweils ohne CAP_-Präfix:
AUDIT_WRITE CHOWN DAC_OVERRIDE
FOWNER FSETID KILL
MKNOD NET_BIND_SERVICE SETFCAP
SETGID SETPCAP SETUID
SYS_CHROOT
Alles andere – insbesondere SYS_ADMIN, NET_ADMIN oder SYS_PTRACE – wird abgelehnt.
Restricted: was zusätzlich gilt
Restricted enthält alles aus Baseline und fordert darüber hinaus aktive Härtung. Hier reicht es nicht mehr, nichts Verbotenes zu tun – bestimmte Felder müssen gesetzt sein.
Die zusätzlichen Anforderungen
| Bereich | Was gefordert wird |
|---|---|
| Volume-Typen | Nur unkritische Typen: configMap, csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, secret |
| Rechte-Aufstieg | allowPrivilegeEscalation: false an jedem Container |
| Als non-root laufen | runAsNonRoot: true – darf auf Pod-Ebene stehen |
| Keine UID 0 | runAsUser darf nicht 0 sein (darf auch weggelassen werden) |
| Seccomp | Muss ausdrücklich auf RuntimeDefault oder Localhost stehen – Weglassen reicht hier nicht mehr |
| Capabilities | drop: ["ALL"] ist Pflicht; zurückgeben darf man nur NET_BIND_SERVICE |
Der Unterschied in einem Satz
Baseline
Prüft, ob du etwas Verbotenes tust. Ein Pod ganz ohne securityContext kommt durch.
Restricted
Prüft zusätzlich, ob du das Vorgeschriebene tust. Ein Pod ohne securityContext wird abgelehnt.
Ein Pod, der Restricted erfüllt
apiVersion: v1
kind: Pod
metadata:
name: beispiel
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginxinc/nginx-unprivileged
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
Das ist die Minimalform. runAsNonRoot und seccompProfile dürfen auf Pod-Ebene stehen und gelten dann für alle Container; allowPrivilegeEscalation und capabilities müssen an jeden einzelnen Container.
Aktivieren: ein Label pro Namespace
Die Stufe wird nicht im Pod hinterlegt, sondern am Namespace. Drei Modi bestimmen, was bei einem Verstoß passiert – und genau darin liegt der sichere Einstiegsweg.
Die drei Modi
| Modus | Wirkung bei Verstoß | Wofür |
|---|---|---|
enforce | Der Pod wird abgelehnt und startet nicht | Scharfschaltung |
audit | Der Pod läuft; der Verstoß landet im Audit-Log | stille Bestandsaufnahme |
warn | Der Pod läuft; der Nutzer bekommt sofort eine Warnung im Terminal | Feedback an Entwickler |
Die Modi lassen sich kombinieren – und das ist der entscheidende Trick: man kann eine strengere Stufe warnend beobachten, während eine mildere scharf geschaltet ist.
Labels setzen
# Baseline scharf, Restricted nur beobachtend
kubectl label namespace meine-app \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
Oder direkt im Namespace-Manifest:
apiVersion: v1
kind: Namespace
metadata:
name: meine-app
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.34
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
Wozu das -version-Label?
Die Standards entwickeln sich weiter – neue Kubernetes-Versionen ergänzen gelegentlich Prüfungen. Ohne Versionsangabe gilt immer die aktuellste Fassung, ein Cluster-Update kann dann unerwartet Pods ablehnen.
Mit einer festen Version friert man das Verhalten ein und hebt es bewusst an, wenn man Zeit zum Testen hat. Für produktive Namespaces ist das die ruhigere Variante.
Prüfen, was gesetzt ist
# Labels eines Namespace
kubectl get namespace meine-app --show-labels
# Alle Namespaces auf einmal
kubectl get namespaces -o custom-columns=\
NAME:.metadata.name,\
ENFORCE:.metadata.labels.pod-security\\.kubernetes\\.io/enforce
Einstieg ohne Ausfall
Direkt enforce: restricted auf einen bestehenden Namespace zu setzen ist der zuverlässigste Weg, einen Abend zu verlieren. Der schrittweise Weg kostet mehr Geduld und keinen Ausfall.
Der Weg in fünf Schritten
- Beobachten statt blockierenNur
warnundauditaufrestrictedsetzen,enforceweglassen. Nichts wird abgelehnt, aber jeder Verstoß wird sichtbar. - Bestand durchtestenDie vorhandenen Deployments einmal neu ausrollen und die Warnungen sammeln. Erst dieser Schritt zeigt, welche Workloads betroffen sind.
- Manifeste nachziehenFehlende Felder ergänzen, Images auf non-root umstellen. Das ist der eigentliche Aufwand – und der Grund, warum Schritt 1 vorher kommt.
- Baseline scharf schalten
enforce: baselinesetzen, währendwarnweiterhin aufrestrictedsteht. Die erste echte Schutzstufe steht. - Auf Restricted anhebenWenn keine Warnungen mehr auflaufen,
enforce: restrictedsetzen.
Typische Stolpersteine
- Image läuft als root – viele offizielle Images tun das. Für gängige Anwendungen gibt es unprivilegierte Varianten, sonst hilft ein eigenes Image mit passendem
USER. - Schreibzugriff aufs Root-Dateisystem – nicht von den Standards verboten, fällt aber beim Härten meist zusammen mit auf. Lösung sind
emptyDir-Volumes an den Schreibpfaden. - Sidecars aus fremden Charts – Service-Mesh-Proxies und Logging-Agents bringen eigene Anforderungen mit und sind nicht immer Restricted-tauglich.
- Nachträgliches Verschärfen – bestehende Pods laufen weiter, der Bruch zeigt sich erst beim nächsten Rollout. Deshalb in Schritt 2 aktiv neu ausrollen statt abzuwarten.
- Infrastruktur-Namespaces – CNI, CSI und Monitoring brauchen oft echten Host-Zugriff. Diese Namespaces bekommen bewusst
privileged, nicht das ganze Cluster.
restricted, einzelne Ausnahmen auf baseline mit Begründung, Infrastruktur-Namespaces auf privileged – und kein Namespace ganz ohne Label.