Kubernetes Logo mit Vorhängeschloss

Ersatz für Kubernetes PodSecurityPolicies – passende Alternativen

Lesezeit
11 ​​min

Mit Kubernetes 1.21 wurden PodSecurityPolicies (PSP) abgekündigt. Also Panik angesagt? Nein, die PSPs bleiben uns zuerst einmal erhalten und werden vermutlich erst in 1.25 entfernt. Trotzdem ist es eine gute Gelegenheit sich schon jetzt nach Alternativen umzuschauen. In diesem Artikel konzentrieren wir uns auf drei Open-Source-Tools und die „PSP Replacement Policy“

Zu den möglichen Alternativen gehören:

  • der Kubernetes-eigene Ersatz (vorläufiger Name „PSP Replacement Policy“, KEP 2579)
  • OPA Gatekeeper
  • Kyverno
  • k-rail
  • Azure Policy, Aqua, Twistlock (Prisma), Sysdig

Admission Controller

Die von uns untersuchten vier Alternativen funktionieren alle einigermaßen ähnlich: Der Kubernetes API Server unterstützt Admission Controller, die bei jedem API-Request befragt werden. Entweder prüfen sie nur und geben ja/nein zurück (validating) oder sie können die Objekte verändern (mutating). Damit könnte man z. B. ein bestimmtes Label an einen Pod setzen.

Eine ganze Reihe dieser Admission Controller sind bereits in den API Server eingebaut (builtin), aber der API Server kann auch zur Laufzeit mit externen Admission Controllern erweitert werden (Dynamic Admission Controllers). Diese nennt man dann MutatingAdmissionWebhook oder ValidatingAdmissionWebhook.

Alle hier vorgestellten Alternativen stellen einen solchen Admission Controller bereit.

Tools

PodSecurityPolicies Replacement Policy

Der bisherige PodSecurityPolicy Controller war ein eingebauter, mutating admission controller. Der „mutating”-Aspekt ist unter anderem ein Grund, warum der Controller jetzt überarbeitet wird: Bei manchen Feldern wurde die PodSpec verändert, damit der Pod die Policy erfüllt. Dieses Verhalten war für viele verwirrend, ebenso die Art und Weise, wie man die Rechte vergab, damit jemand eine Policy verwenden durfte. Zudem mussten bisher immer explizit die Rechte vergeben werden, eine bestimmte PSP nutzen zu dürfen. Eine einzelne PSP musste dann den Pod erlauben, auch wenn ein Nutzer mehrere PSPs verwenden darf und ein Kombination der PSPs den Pod erlaubt hätte.

Mit der neuen PSP Replacement Policy soll das alles klarer werden. Im Gegenzug gibt man etwas (kaum genutzte) Flexibilität auf. Da die erste Alpha-Version erst in 1.22 kommt, ist noch nicht genau klar, wie die Lösung endgültig aussehen wird. Im Moment sieht es so aus, als ob es die drei Stufen restricted, baseline und privileged geben wird, die sich an den bisherigen Empfehlungen für PSPs orientieren. Welche Policy für welchen Namespace gilt, wird dann mit Labels am Namespace festgelegt:

Damit wird zum Beispiel verboten, einen Pod mit hostPath zu starten, aber nur gewarnt, wenn ein Container im Pod als root läuft. Da die Policies sich mit Kubernetes selbst verändern können, kann man mit dem version-Label auf eine bestimmte Version festlegen oder mit latest immer auf die neueste Version festlegen.

Geplant ist zudem eine Audit-Funktion, feinere Abstufungen um bei bestimmten User-Namen oder RuntimeClasses explizit nicht zu prüfen und Warnungen, wenn eine Änderung der Namespace-Labels Pods darin betreffen.

OPA, kube-mgmt, OPA Gatekeeper

Im Universum des Open Policy Agent (OPA) gibt es gleich mehrere Tools: kube-mgmt war der alte Weg ihn zu deployen, heute wird eher der OPA Gatekeeper verwendet.

Policies werden in der eigenen Sprache rego geschrieben (empfehlenswert zum Ausprobieren: https://play.openpolicyagent.org). Bei kube-mgmt werden die rego-Regeln mittels ConfigMaps geladen, der Gatekeeper definiert dagegen Kubernetes‘ CRDs für u. a. ConstraintTemplates. Mit ConstraintTemplates wird die Policy-Bibliothek erweitert, z. B. dem rego-Code für „prüfe ob eine Annotation x einen bestimmten Wert y hat“. Der Gatekeeper erzeugt daraus wieder eine CRD, die dann instanziiert werden kann; also z. B. dass die Annotation „force-ssl-redirect“ an einem Ingress nicht „true“ sein darf. Diese Konstruktion erlaubt eine Trennung, sodass Techniker die ConstraintTemplates erstellen und Nicht-Techniker diese selbstständig verwenden können, um ihre Vorstellungen umzusetzen. Andererseits ist diese Konstruktion auch manchmal schwer zu durchschauen.

Ein Vorteil vom OPA ist, dass es ihn auch für alle möglichen anderen Stellen (ssh, ingress, …) gibt, so dass man theoretisch über seinen ganzen Stack dieselben Policies verwenden kann. In der Praxis habe ich noch nicht gesehen, dass man die Kubernetes-spezifischen Policies irgendwo anders wiederverwenden könnte …

Auch ansonsten macht OPA(-Gatekeeper) einen soliden Eindruck:

  • Er stellt Metriken bereit, damit man seine Funktion überwachen kann
  • Er kann hochverfügbar betrieben werden – zumindest mit validierenden Regeln
  • Die gatekeeper-library liefert viele Beispiele (besonders hilfreich, wenn man mit rego neu anfängt)
  • Wie es aktuell aussieht, entwickelt sich OPA zu so etwas wie dem de-facto Standard (kommt z. B. auch im CKS vor, viele Vorträge auf der KubeCon)
  • OPA-Gatekeeper bietet auch einen Audit-Modus, in dem er Regelverstöße nicht verbietet, sondern nur auflistet mit  kubectl describe ConstraintTemplateGeneratedCRD (ähnlich wie SELinux‘ permissive mode)

Der OPA-Gatekeeper erlaubt prinzipiell auch Mutating-Regeln, um Pods zu verändern. Das Feature ist aktuell aber noch in einem experimentellen Stadium (Github issue).

Kyverno

Kyverno ist deutlich auf Kubernetes ausgelegt: Die Konfiguration erfolgt mittels eigener CRDs und im yaml-Format, wie in der Kubernetes-Welt gewohnt.

Auch Kyverno bietet:

  • Metriken
  • Eine policy library mit vielen Beispielen
  • Sehr gute Dokumentation
  • Unterstützt mutating Policies
  • Kyverno unterstützt ebenfalls einen Audit-Modus und generiert daraus PolicyReportskubectl get policyreport --all-namespaces

Mit kyverno-cli kann man komfortabel seine Regel-Konfiguration testen mit kyverno validate <file> oder kyverno apply <policy> --resource <yaml>

Bis vor Kurzem konnte Kyverno noch nicht hochverfügbar betrieben werden, HA ist aber mit Version 1.4.0 gekommen (Github issues), die kurz vor diesem Blogartikel erschienen ist.

k-rail

k-rail ist ein eher kleineres Projekt:

  • Hat einen Monitor-Modus
  • Hat eine Reihe von eingebauten Regeln, erweiterbar über go Plugins
  • Ist vergleichsweise wenig komplex
  • Die Dokumentation ist nicht besonders ausführlich (es existiert quasi nur eine README)

k-rails bietet deutlich weniger Features als OPA oder Kyverno, dafür kann es aber auch eine deutlich geringere Komplexität und wir wollen im Vergleich herausfinden, ob es sich vielleicht als einfaches Tool eignet, um genau den PSP Controller zu ersetzen.

Szenarien

Einfach PodSecurityPolicies ersetzen

In der folgenden Tabelle ist aufgeführt, welches Tool welche Teile einer PSP abdeckt, indem man nur mitgelieferte Beispiele verwendet. Also quasi: wie viel einer PSP kann man mit dem Tool ohne Mehraufwand abdecken:

PSP AspektOPA GatekeeperKyvernok-railPSP Replacement
(voraussichtlich)
privileged✓ (baseline)
hostPID, hostIPCnur PID✓(baseline)
hostNetwork, hostPortsnetwork✓ (baseline)
volumesnur PVs erlaubt (restricted)
allowedHostPathskomplett verbotenverboten für PVs, für docker socket oder komplettkomplett verboten (baseline)
allowedFlexVolumesnur PVs erlaubt (restricted)
fsGroupnicht mutating *nicht mutating *initial nicht implementiert
readOnlyRootFilesystem✓(in best-practices)
runAsUser, runAsGroup, supplementalGroupsnicht mutating *nicht mutating *✓ (restricted) (Gruppen werden initial nicht implementiert sein)
allowPrivilegeEscalation, defaultAllowPrivilegeEscalation✓ (restricted)
defaultAddCapabilities, requiredDropCapabilities, allowedCapabilitiesverbiete „add“verbiete „add“feste Liste erlaubter Capabilities (baseline)
seLinuxfeste Liste erlaubter Werte (baseline)
allowedProcMountTypesdefault✓✓ (baseline)
AppArmor/seccomp annotations„undefined“ oder „default“seccomp: immer „default“ setzen, apparmor: verbiete „unconfined“seccomp: verbiete „unconfined“ (baseline), muss gesetzt sein (restricted), apparmor: verbiete „unconfined“ (baseline)
forbiddenSysctls, allowedUnsafeSysctlsfeste  Liste erlaubter Werte

* Der PSP Controller mutiert den Pod bei manchen Feldern, um default-Werte für manche Felder zu setzen, falls diese nicht explizit gesetzt sind. Die hier vorgestellten Admission Controller unterstützen das nicht, können aber validieren, ob erlaubte Werte in den Feldern stehen.

Quellen: Was die PSP Replacement Policy vermutlich ermöglichen wird, ist aus den Pod Security Standards und dem Kubernetes Enhancement Proposal abgeleitet. Für OPA-Gatekeeper gibt es eine Vergleichstabelle, für Kyverno gibt es einen Ordner mit Pod Security Policies in den Beispiel-Policies (mit Unterordnern, die den Pod Security Standards entsprechen) und für k-rails wurden die eingebauten Policies analysiert.

Wie man in der Tabelle schon sieht, kann k-rails knapp die Hälfte der Fälle nicht abdecken. Die anderen drei unterscheiden sich in Details, aber decken zumindest grob den kompletten Funktionsumfang einer PSP ab.

Komplexere Szenarien

Die Admission Controller können natürlich noch mehr außer nur die PSP zu ersetzen.

So können zum Beispiel nur Ingress-yamls zugelassen werden, deren Domain überhaupt zum Cluster passt. Oder der Inhalt bestimmter ConfigMaps kann validiert werden (z. B., wenn darin Prometheus-Alerts definiert werden, die dieser zentral einsammelt). Oder es kann validiert werden, dass jeder Namespace ein Label mit der Kostenstelle trägt. Oder der Zeitstempel des letzten Apply soll automatisch annotiert werden. Oder ein bestimmter Service darf nur bei zunehmendem Mond geändert werden.

Okay, das letzte Beispiel war vielleicht nicht ganz realistisch, aber alle diese Beispiele lassen sich mit dynamischen Admission Webhooks umsetzen. Die meisten davon lassen sich auch mit den vorgestellten Admission Controllern umsetzen. Schwierig wird es, wenn entweder dynamische Daten von außerhalb nötig sind (z. B. ein Abgleich mit einer Liste von Benutzer:innen) oder wenn die Validierung sehr komplex ist (z. B., ob eine ConfigMap eine valide Konfiguration enthält). In solchen komplizierten Fällen kann es sich auch lohnen, einen eigenen Admission Controller zu schreiben. Das ist gar nicht so kompliziert wie es klingt, ein Kollege bereitet schon einen Artikel für diesen Blog vor. Stay tuned …

Fazit

Das Schöne ist, dass man nicht auf eine Lösung festgelegt ist: alle können beliebig kombiniert werden. Um die eigentliche PSP in den meisten mir bisher untergekommenen Clustern zu ersetzen ist die „PSP Replacement Policy“ vollkommen ausreichend. Da sie schon mit Kubernetes mitkommt ist der Aufwand minimal und wird hoffentlich in mehr Clustern einfach mal angeschaltet. Dafür bietet sie etwas weniger Flexibilität für komplexe Szenarien.

Bei komplexeren Anforderungen können sowohl OPA Gatekeeper als auch Kyverno geeignet sein. Beide sind in diversen Projekten bei inovex im Einsatz und um es kurz zusammenzufassen: beide haben ihre kleinen Macken, aber funktionieren gut. Mich persönlich würde hauptsächlich die ungewohnte rego-Syntax und das komplizierte ContraintTemplate-Konstrukt vom OPA Gatekeeper abhalten, und eher zu Kyverno tendieren lassen. Denn der beste Admission Controller nützt nichts, wenn er nicht verwendet wird.

Ich freue mich sehr von Euren Erfahrungen in den Kommentaren lesen: Welche Policies habt Ihr? Welche Ecken nerven Euch an OPA, Kyverno oder einem der kommerziellen Admission Controller?

Hat dir der Beitrag gefallen?

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Ähnliche Artikel