· Kubernetes  · 7 Min. Lesezeit

PodSecurity restricted: die vollständige Sammlung der Stolpersteine

Zehn Fälle, an denen eine Migration auf enforce restricted hängen bleibt: von der Wildcard über initContainers bis zur Exemption, die den halben Cluster öffnet. Jeder Fall im Schema Symptom, Ursache, Prüfung und Lösung, mit Prüfbefehlen zum Kopieren.

Zehn Fälle, an denen eine Migration auf enforce restricted hängen bleibt: von der Wildcard über initContainers bis zur Exemption, die den halben Cluster öffnet. Jeder Fall im Schema Symptom, Ursache, Prüfung und Lösung, mit Prüfbefehlen zum Kopieren.

Die Fälle unten sind für zwei Situationen gedacht. Die erste: Sie planen eine Migration auf enforce: restricted und wollen vorher wissen, was Sie erwartet, statt es beim Rollout herauszufinden. Die zweite: Bei Ihnen scheitert bereits etwas, und Sie suchen die passende Ursache zu einem Symptom, das Sie vor sich haben.

Der Ablauf selbst, also Bestandsaufnahme, Trockenlauf, securityContext am Workload, Version-Pin und die Verankerung der Labels in GitOps, steht im Hauptbeitrag PodSecurity restricted: der Namespace, in dem keine Pods starten. Wenn Sie noch am Anfang stehen, arbeiten Sie den zuerst durch und nehmen diese Seite als Begleitung daneben.

1. Das Deployment wird angelegt, es entsteht kein Pod

Symptom: kubectl apply des Deployments läuft fehlerfrei durch. kubectl get deploy zeigt dauerhaft 0/2, ohne dass am Deployment ein Grund steht.

Ursache: enforce wird laut Doku zur Pod Security Admission nicht auf Workload-Ressourcen angewendet, sondern nur auf die resultierenden Pod-Objekte. Das Deployment passiert die Admission also ungeprüft. Abgewiesen wird erst, wenn der ReplicaSet-Controller den Pod erzeugen will. Der Status des Deployments ist dafür der falsche Ort.

Prüfung:

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

Lösung: Setzen Sie zu enforce immer auch warn und audit auf demselben Level. warn greift auf die Workload-Ressource, dadurch sehen Sie den Verstoß schon beim apply des Deployments und nicht erst im Event einer Zwischenressource.

2. Der ungelabelte Namespace ist nicht streng, sondern offen

Symptom: Ein frisch angelegter Namespace ohne PodSecurity-Label verhält sich nicht restriktiv, unsichere Pods laufen dort durch.

Ursache: Der eingebaute Default der PodSecurityConfiguration ist privileged. Ohne eigene AdmissionConfiguration fällt ein ungelabelter Namespace auf diese offenste Stufe zurück, nicht auf eine strenge.

Prüfung:

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

Lösung: Entweder den Default über eine AdmissionConfiguration am kube-apiserver anheben, oder jeden Namespace explizit labeln. Die zweite Variante ist die einzige, die auch in Clustern funktioniert, in denen Sie die apiserver-Konfiguration nicht anfassen können.

3. Der Pod erfüllt scheinbar alles und wird trotzdem abgewiesen

Symptom: Der Anwendungscontainer trägt einen vollständigen securityContext, der Pod wird dennoch nicht zugelassen.

Ursache: Die Kontrollen der Pod Security Standards gelten mit Wildcard für containers[*], initContainers[*] und ephemeralContainers[*]. Ein vergessener initContainer oder ein nachträglich injizierter Sidecar reißt den ganzen Pod mit. Dazu kommen zwei leicht übersehene Punkte: runAsUser darf nicht 0 sein, und spec.volumes[*] ist auf acht Typen begrenzt.

Prüfung:

kubectl get pod <name> -n <namespace> \
  -o jsonpath='{.spec.initContainers[*].securityContext}{"\n"}{.spec.containers[*].securityContext}{"\n"}'
kubectl get pod <name> -n <namespace> -o jsonpath='{.spec.volumes[*]}{"\n"}'

Lösung: Setzen Sie runAsNonRoot und seccompProfile auf Pod-Ebene, damit sie für alle Container-Listen gelten. capabilities.drop: ["ALL"] muss je Container stehen. hostPath ersetzen Sie durch emptyDir, configMap, secret, projected oder persistentVolumeClaim. Beachten Sie außerdem, dass das gitRepo-Volume-Plugin mit Kubernetes 1.36 standardmäßig deaktiviert ist und sich laut Changelog nicht wieder einschalten lässt.

4. Nach dem Cluster-Upgrade werden Pods abgewiesen, die vorher liefen

Symptom: Am Manifest hat sich nichts geändert, nach dem Upgrade der Control Plane scheitert der Rollout trotzdem.

Ursache: Ohne Version-Label gilt latest. Die Definition eines Levels kann sich zwischen Minor-Versionen ändern. restricted wurde in v1.25 auf spec.os.name umgestellt, die Capabilities-Kontrolle kam in v1.22 dazu, die Kontrolle gegen runAsUser: 0 in v1.23.

Prüfung:

kubectl get ns <namespace> -o jsonpath='{.metadata.labels}{"\n"}'

Lösung: pod-security.kubernetes.io/<MODE>-version explizit auf eine Minor-Version pinnen und beim Upgrade bewusst nachziehen, statt die Änderung im laufenden Betrieb zu erben.

5. Die Admission ergänzt keine fehlenden Felder

Symptom: Ein Pod ohne securityContext wird abgewiesen, obwohl man erwartet hätte, dass die Admission die Pflichtfelder setzt.

Ursache: Pod Security Admission ist ausdrücklich nicht mutierend. Anders als die früheren PodSecurityPolicies setzt sie keine Defaults, sie prüft nur. Die offizielle Anleitung zur Migration von PodSecurityPolicy zu Pod Security Admission benennt das als bekannte Lücke.

Prüfung: Ein kubectl apply eines Pods ohne securityContext in einem restricted-Namespace muss fehlschlagen, nicht still ergänzt werden.

Lösung: Entweder jeden Workload anpassen, oder laut derselben Anleitung einen Mutating Admission Webhook einsetzen. Bei fremden Helm-Charts heißt das in der Praxis, die securityContext-Werte über die Values zu setzen.

6. Ein Team hebelt die Härtung im eigenen Namespace aus

Symptom: Trotz restricted laufen in einem Namespace Pods, die dort nicht laufen dürften.

Ursache: Wer einen Namespace anlegen, ändern oder patchen darf, darf laut Migrationsanleitung auch dessen PodSecurity-Level ändern und die strengere Richtlinie damit umgehen.

Prüfung:

kubectl auth can-i patch namespaces --as=deploy@lab.rz.jabi-it.de
kubectl auth can-i create namespaces --as=deploy@lab.rz.jabi-it.de

Lösung: RBAC so schneiden, dass update, patch und create auf Namespaces nur privilegierten Rollen offenstehen. Wie Sie die eigenen Rechte in einer eingeschränkten Umgebung überhaupt feststellen, steht im Beitrag Wenn der einzige Zugang die Rancher-Shell ist.

7. Die Exemption wirkt nicht oder viel zu breit

Symptom: Eine Ausnahme für einen einzelnen Benutzer greift nicht. Oder eine Ausnahme für einen Controller-Account öffnet den Cluster weit über die Absicht hinaus.

Ursache: Die meisten Pods werden von einem Controller als Reaktion auf eine Workload-Ressource erzeugt. Eine Benutzer-Exemption wirkt deshalb nur beim direkten Anlegen von Pods. Eine Exemption für einen Controller-ServiceAccount wie system:serviceaccount:kube-system:replicaset-controller nimmt dagegen implizit jeden aus, der die entsprechende Workload-Ressource anlegen darf. Die Doku rät ausdrücklich davon ab.

Prüfung: Die AdmissionConfiguration am kube-apiserver lesen und sicherstellen, dass unter exemptions.usernames kein Controller-ServiceAccount steht.

Lösung: Ausnahmen über die Dimensionen namespaces oder runtimeClasses steuern, nicht über Benutzer oder Controller-Accounts.

8. Argo CD setzt die Labels nicht oder entfernt sie wieder

Symptom: Der Namespace entsteht, die PodSecurity-Labels aus der Application fehlen. Oder sie waren da und sind nach einem Sync verschwunden.

Ursache: Zwei getrennte Punkte, beide in der Argo-CD-Doku zu den Sync-Optionen benannt. managedNamespaceMetadata wirkt nur mit gesetzter Sync-Option CreateNamespace=true, und ein eigenes Namespace-Manifest in derselben Application überschreibt die Werte. Für den zweiten Fall warnt die Doku, dass Argo CD bestehende Labels und Annotationen entfernen kann, solange die Ressource nicht auf Server-Side Apply umgestellt ist.

Prüfung: Die Labels vor und nach einem Sync vergleichen:

kubectl get ns <namespace> -o jsonpath='{.metadata.labels}{"\n"}'

Lösung: CreateNamespace=true setzen, kein konkurrierendes Namespace-Manifest ausliefern, und die Namespace-Ressource auf Server-Side Apply umstellen.

9. Das Upgrade auf 1.35 bricht vor der Policy-Arbeit ab

Symptom: Beim Anheben des Clusters auf 1.35 startet das kubelet nicht mehr, oder kubeadm bricht mit einem Fehler der Systemprüfung ab.

Ursache: Die Release-Notes zu Kubernetes 1.35 nennen zwei Punkte mit dem Vermerk ACTION REQUIRED. Das kubelet-Flag --pod-infra-container-image wurde entfernt. Und der cgroups-Validator meldet ab KubeletVersion v1.35 einen Fehler statt einer Warnung, wenn auf dem Host cgroups v1 erkannt wird.

Prüfung:

stat -fc %T /sys/fs/cgroup/
kubectl get cm kubelet-config -n kube-system -o yaml

Lösung: Die Reihenfolge ist vorgegeben. Entfernen Sie --pod-infra-container-image vor dem kubelet-Upgrade manuell aus der Konfiguration, sonst startet das kubelet nicht. Und ergänzen Sie die ConfigMap kube-system/kubelet-config vor dem Upgrade um failCgroupV1: false, falls Sie cgroups v1 vorerst weiterfahren müssen. Liefert stat den Wert cgroup2fs, laufen Sie bereits auf cgroups v2 und der Punkt entfällt.

10. Windows-Pods verhalten sich anders

Symptom: In einem gemischten Cluster greift restricted bei Windows-Workloads nicht so, wie es bei Linux-Workloads greift.

Ursache: Seit v1.25 wertet das Profil pod.spec.os.name aus. Rechteausweitung, seccomp und Linux-Capabilities werden nur verlangt, wenn spec.os.name nicht windows ist.

Prüfung:

kubectl get pod <name> -n <namespace> -o jsonpath='{.spec.os.name}{"\n"}'

Lösung: In gemischten Clustern spec.os.name bewusst setzen und nicht davon ausgehen, dass restricted auf Windows dieselben Garantien liefert wie auf Linux.

Wie Sie diese Liste einsetzen

Die Fälle 1 bis 4 treten am Anfang einer Migration auf und blockieren den Rollout sichtbar. Die Fälle 5 bis 8 betreffen die Umgebung um die Policy herum, also Workload-Anpassung, RBAC, Ausnahmen und GitOps. Sie fallen später auf und sind dann teurer zu korrigieren. Fall 9 und 10 sind Randbedingungen des Clusters selbst, die mit der Policy nichts zu tun haben, den Zeitplan einer Härtung aber trotzdem bestimmen.

Ich gehe die Liste vor dem ersten scharfen enforce-Label einmal von oben nach unten durch und halte je Namespace fest, welcher Punkt zutrifft. Das kostet eine überschaubare Vorbereitung und verhindert, dass eine Absicherung mitten im Rollout wieder zurückgedreht wird. Der zugehörige Ablauf mit Trockenlauf und Prüfbefehlen steht im Beitrag PodSecurity restricted: der Namespace, in dem keine Pods starten.

Sie wollen einen Cluster, der sicher ist und trotzdem zuverlässig ausrollt? Ich härte Kubernetes-Umgebungen so, dass die Absicherung greift, ohne dass Deployments still liegen bleiben. Mehr unter IT-Sicherheit & ISMS, das Erstgespräch ist unverbindlich.

Alex Jabi, Jabi IT

Alex Jabi

Ich betreue IT, Informationssicherheit und Datenschutz für KMU und Praxen an der Bergstraße und im Odenwald, persönlich, dokumentiert und ohne Cloud-Zwang.

Mehr über mich
Zurück zum Blog

Passende Artikel

Alle Artikel »
PodSecurity restricted: der Namespace, in dem keine Pods starten

PodSecurity restricted: der Namespace, in dem keine Pods starten

Der enforce-Modus der Pod Security Admission prüft keine Deployments, sondern nur die daraus entstehenden Pods. Das Deployment wird fehlerfrei angelegt und trotzdem startet nichts. Wie Sie Namespaces sauber auf restricted heben: Trockenlauf, Version-Pin, securityContext und in GitOps verankerte Labels.

Wenn der einzige Zugang die Rancher-Shell ist

Wenn der einzige Zugang die Rancher-Shell ist

Kein kubeconfig, kein kubectl von außen, nur die Kubectl-Shell im Browser. Wie man darin sauber arbeitet: Rechte feststellen, Manifeste ohne Datei-Upload anlegen, vor dem Schreiben prüfen und Ergebnisse einsammeln, ohne interaktiv im Cluster herumzustochern.

Die eigene Registry mit Harbor: scannen, cachen, absichern

Die eigene Registry mit Harbor: scannen, cachen, absichern

Harbor speichert Container-Images nicht nur, es scannt sie mit Trivy und spiegelt fremde Registries als Proxy-Cache. Der Scanner ist dabei nicht im Standard-Setup enthalten, und die Sperre für verwundbare Images greift beim Pull statt beim Push. Aufbau mit Prüfbefehlen und die dokumentierten Fallen.

Harbor: die Stolpersteine im Überblick

Harbor: die Stolpersteine im Überblick

Die vollständige Fallsammlung zum Betrieb von Harbor: fehlender Scanner, Scan-on-push je Projekt, der Schwellwert, der negligible durchlässt, geerbte Upstream-Rechte im Proxy-Cache, ablaufende Robot-Accounts, TLS-Ketten, Reverse-Proxy-Header und die Selbstsperre über CONFIG_OVERWRITE_JSON. Jeder Fall mit Symptom, Ursache, Prüfung und Lösung.