Hat man mit Kubernetes erst einmal losgelegt und befindet sich im produktiven Betrieb, kommen im Laufe der Zeit häufig neue Anforderungen auf die bestehenden Umgebungen zu. Diese können vielfältig sein: Von konsistenten Labels für ausgerollte Anwendungen bis hin zu sicherheitsrelevanten Einstellungen gibt es Vieles, was sich ohne eine Art von Kubernetes Policies nicht umsetzen lässt.
Durch seinen modularen Ansatz bietet Kubernetes hier nicht viele Möglichkeiten von sich aus – Netzwerkverkehr lässt sich unter Zuhilfename eines CNI mit NetworkPolicies regeln, und der Pod Security Admission Controller bietet verschiedene vordefinierte Profile für Anwendungssicherheit, doch darüber hinaus muss man sich mit externen Anwendungen behelfen.
Zwei häufig genutzte Tools hierfür sind Kyverno und der Open Policy Agent (OPA) mit seiner Kubernetes Implementierung Gatekeeper, um Kubernetes Policies einzuführen – um Gatekeeper wird es im heutigen Tutorial gehen.
Verfolge dieses Tutorial live und in einer echten Kubernetes Umgebung in unserem kostenlosen NWS Playground.
Open Policy Agent und Gatekeeper erklärt
Der Open Policy Agent (OPA) bietet Policy-basierte Kontrolle für cloud-native Umgebungen – diese beschränken sich nicht nur auf Kubernetes: OPA bietet deklarative, kontextabhängige Unterstützung für Policies rund um Kubernetes, Envoy, verschiedene HTTP und GraphQL APIs, Kafka und andere Szenarien.
Ermöglicht wird diese Vielfalt von Anwendungsbereichen durch OPAs Policy Sprache Rego, die sich wiederum stark an Datalog orientiert und von ihr inspiriert wurde. Jede Abfrage in Rego definiert und prüft hierbei gewisse Annahmen zu Daten, die OPA zum Zeitpunkt der Überprüfung zur Verfügung stehen, bspw. der Inhalt von API-Anfragen. Eine einfache Kubernetes Policy könnte in Rego z.B. wie folgt aussehen:
package kubernetes.admission
deny contains msg if {
input.request.kind.kind == "Pod"
image := input.request.object.spec.containers[_].image
not startswith(image, "myregistry.com/")
msg := sprintf("image '%v' comes from untrusted registry", [image])
}Diese Policy lehnt Anfragen an die Kubernetes-API zur Erstellung von Pods ab, wenn alle in der Regel definierten Annahmen als true evaluiert wurden. OPA iteriert bei der Evaluierung der Anfrage von sich aus über alle Eingaben und prüft, ob die Annahmen im jeweiligen Fall true oder false sind.
Im obigen Beispiel würde also die Erstellung eines Pods verweigert werden, wenn mindestens ein im Pod definierter Container ein Image aus einer anderen Registry als myregistry.com beziehen möchte.
Um Kubernetes Policies für OPA zu testen, bietet sich der offizielle Rego Playground an. In diesem können Rego-Module definiert und mit beliebigen Eingaben getestet werden.
Gatekeeper ist eine Implementierung von OPA speziell für Kubernetes Policies, mit ein paar nützlichen Erweiterungen:
- eine erweiterbare, parametrisierbare Bibliothek mit Kubernetes Policies
- Kubernetes
CustomResourceDefinitions(CRDs) für den Import und die Nutzung bestehender Policies (Constraints) - Kubernetes CRDs für die Definition neuer Policies (
ConstraintTemplates) - Kubernetes CRDs für die Mutation von Anfragen und Daten
- Audit-Funktionalität
- Support für externe Daten und Datenquellen
Installation von OPA Gatekeeper
Möchte man Kubernetes Policies mit dem OPA Gatekeeper umsetzen, installiert man ihn zuerst in seiner Umgebung. Dies kann bspw. mit Hilfe der offiziellen Helmchart geschehen:
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install -n gatekeeper-system gatekeeper gatekeeper/gatekeeper --create-namespaceNach ein paar Sekunden sind der Gatekeeper Controller und der Audit Helfer im entsprechenden Namespace zu finden:
kubectl get all -n gatekeeper-system
NAME READY STATUS RESTARTS AGE
pod/gatekeeper-audit-8948486cd-754x6 1/1 Running 0 118s
pod/gatekeeper-controller-manager-5f9dfb6899-4m92s 1/1 Running 0 118s
pod/gatekeeper-controller-manager-5f9dfb6899-8ns8j 1/1 Running 0 118s
pod/gatekeeper-controller-manager-5f9dfb6899-drmqn 1/1 Running 0 118s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/gatekeeper-webhook-service ClusterIP 10.254.22.244 <none> 443/TCP 118s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/gatekeeper-audit 1/1 1 1 118s
deployment.apps/gatekeeper-controller-manager 3/3 3 3 118s
NAME DESIRED CURRENT READY AGE
replicaset.apps/gatekeeper-audit-8948486cd 1 1 1 118s
replicaset.apps/gatekeeper-controller-manager-5f9dfb6899 3 3 3 118sAuch die zuvor erwähnten CRDs sind bereits installiert und unserem Cluster bekannt:
kubectl get crds | grep gatekeeper
assign.mutations.gatekeeper.sh 2025-02-19T10:09:19Z
assignimage.mutations.gatekeeper.sh 2025-02-19T10:09:19Z
assignmetadata.mutations.gatekeeper.sh 2025-02-19T10:09:19Z
configpodstatuses.status.gatekeeper.sh 2025-02-19T10:09:19Z
configs.config.gatekeeper.sh 2025-02-19T10:09:19Z
constraintpodstatuses.status.gatekeeper.sh 2025-02-19T10:09:19Z
constrainttemplatepodstatuses.status.gatekeeper.sh 2025-02-19T10:09:19Z
constrainttemplates.templates.gatekeeper.sh 2025-02-19T10:09:19Z
expansiontemplate.expansion.gatekeeper.sh 2025-02-19T10:09:19Z
expansiontemplatepodstatuses.status.gatekeeper.sh 2025-02-19T10:09:19Z
modifyset.mutations.gatekeeper.sh 2025-02-19T10:09:19Z
mutatorpodstatuses.status.gatekeeper.sh 2025-02-19T10:09:19Z
providers.externaldata.gatekeeper.sh 2025-02-19T10:09:19Z
syncsets.syncset.gatekeeper.sh 2025-02-19T10:09:19ZIm nächsten Schritt können nun bestehende Kubernetes Policies aus der OPA Gatekeeper Library importiert oder eigene Policies geschrieben werden.
Nutzung bestehender Kubernetes Policies
Möchte man bestehende Kubernetes Policies aus der OPA Gatekeeper Library einbinden, geschieht das durch ein einfaches kubectl apply. Für diesen Blogposts werden wir uns drei bestehende Policies anschauen:
K8sAllowedReposzur Einschränkung, aus welchen Registries Containerimages bezogen werden dürfenK8sRequiredLabelszur Forcierung zwingend zu setzender LabelK8sContainerLimitszur Forcierung zwingend zu setzenderResourceLimits
Installation bestehender ConstraintTemplates
Die entsprechenden ConstraintTemplates können wir mit kubectl direkt aus dem Policy Repository installieren:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/allowedrepos/template.yaml
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/requiredlabels/template.yaml
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/template.yamlDie erfolgreiche Installation kann ebenfalls überprüft werden:
kubectl get ConstraintTemplates
NAME AGE
k8sallowedrepos 8s
k8scontainerlimits 8s
k8srequiredlabels 8sEin ConstraintTemplate definiert grundsätzlich zwei verschiedene Dinge:
- den Aufbau des auf dem
ConstraintTemplateaufbauenden, parametrisierbarenConstraintsals OpenAPI Spezifikation - die anzuwendende(n) Regel(n)
Nutzung installierter ConstraintTemplates durch Constraints
Sind die ConstraintTemplates erst einmal installiert, können sie durch Constraints in konkrete Kubernetes Policies gegossen werden.
Ein Constraint kann hierbei je nach OpenAPI Spezifikation ganz unterschiedlich aussehen. Die folgenden Beispiele definieren und forcieren aufbauend auf den drei zuvor installierten ConstraintTemplates konkrete Kubernetes Policies für das betroffene Cluster.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: repo-is-quay
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "default"
parameters:
repos:
- "quay.io/"Dieser Constraint erlaubt im Namespace default ausschließlich Containerimages, die von quay.io bezogen werden.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: all-must-have-team
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
message: "All deployments must have a `team` label that points to your team name"
labels:
- key: team
allowedRegex: "^team-[a-zA-Z]+$"Dieser Constraint forciert die Existenz eines Labels team für alle Deployments. Der Wert des Labels muss zusätzlich der Form team-xxx entsprechen.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sContainerLimits
metadata:
name: container-must-have-memory-limits
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "limited-resources"
parameters:
cpu: "-1" # do not enforce CPU limits
memory: "1Gi"Dieser Constraint forciert im Namespace limited-resources ein zu setzendes Memory Limit von 1Gi (1 Gibibyte).
Die entsprechenden YAML-Blöcke können mittels kubectl apply installiert und im Anschluss auf Funktionalität überprüft werden.
Testen der definierten Kubernetes Policies
Die definierten Policies können durch Erstellung erlaubter oder eben unerlaubter Objekte getestet werden. Für die Kubernetes Policies K8sAllowedRepos erstellen wir zwei Pods im Namespace default:
kubectl run -n default denied-pod --image mysql:latest
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [repo-is-quay] container <denied-pod> has an invalid image repo <mysql:latest>, allowed repos are ["quay.io/"]
kubectl run -n default allowed-pod --image quay.io/fedora/mysql-80
pod/allowed-pod created
Gatekeeper verwehrt die Erstellung des ersten Pods mit einer konkreten Fehlermeldung, was dem Constraint widerspricht.
Für die Policy K8sRequiredLabels erstellen wir die folgenden zwei Deployments:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
name: denied-deployment
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: quay.io/fedora/mysql-80
name: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
name: allowed-deployment
labels:
team: team-database
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: quay.io/fedora/mysql-80
name: mysqlAuch in diesem Szenario erhalten wir von Gatekeeper eine Fehlermeldung für das nicht entsprechend der Kubernetes Policy gelabelte Deployment:
kubectl apply -f deployments.yaml
deployment.apps/allowed-deployment created
Error from server (Forbidden): error when creating "deployments.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [all-must-have-team] missing required label, requires all of: team
[all-must-have-team] regex mismatchFür das dritte Szenario erstellen wir drei weitere Pods:
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: unaffected-pod
spec:
containers:
- image: quay.io/fedora/mysql-80
name: mysql
---
apiVersion: v1
kind: Pod
metadata:
namespace: limited-resources
name: affected-pod-without-limits
spec:
containers:
- image: quay.io/fedora/mysql-80
name: mysql
---
apiVersion: v1
kind: Pod
metadata:
namespace: limited-resources
name: affected-pod-with-limits
spec:
containers:
- image: quay.io/fedora/mysql-80
name: mysql
resources:
limits:
memory: 1GiErneut erhalten wir eine Fehlermeldung für den Pod affected-pod-without-limits. Anzumerken ist außerdem, dass der Pod unaffected-pod trotz fehlender Limits erfolgreich erstellt wird – der Constraint greift laut Definition nur im Namespace limited-resources.
kubectl create namespace limited-resources
kubectl apply -f pods.yaml
pod/unaffected-pod created
pod/affected-pod-with-limits created
Error from server (Forbidden): error when creating "pods.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [container-must-have-memory-limits] container <mysql> has no resource limitsUnit-Tests für Kubernetes Policies
Da es in realen Szenarien selten sinnvoll ist, alle Kubernetes Policies im laufenden Betrieb manuell zu testen, ermöglicht OPA Gatekeeper automatisiertes Unit-Testing definierter Policies. Hierfür wird auf die gator CLI des Gatekeeper-Projekts zurückgegriffen.
Tests können zum Einen via gator test angestoßen werden und testen eine beliebige Anzahl an gegebenen Kubernetes Objekten gegen ein Set an ConstraintTemplates und Constraints.
Zum Anderen können mittels gator verify ganze Testsuites bestehend aus mehreren Tests und Testcases überprüft werden. Ein Test besteht hierbei aus einem konkreten ConstraintTemplate und Constraint sowie zu testenden Eingaben in Form von Kubernetes Objekten (als Cases).
Dieses Vorgehen ermöglicht automatisiertes Testen erstellter Kubernetes Policies bspw. in CI Pipelines, ohne manuell in der tatsächlichen Umgebung testen zu müssen. In der gator CLI Dokumentation finden sich weiterführende Erklärungen und Beispiele.
Kubernetes Policies für Jedermann
OPA Gatekeeper mit seiner umfangreichen Bibliothek an vorgefertigten Kubernetes Policies für viele erdenkliche Szenarien bietet einen guten Einstieg, den Alltagsbetrieb auf Kubernetes zu vereinheitlichen und abzusichern.
Sollten noch spezifischere Richtlinien nötig sein, hilft der Rego Playground und die Möglichkeit, erstellte Policies unabhängig von der tatsächlichen Umgebung testen zu können. Auf diese Weise kann ein sicherer Betrieb, auch in Multi-Tenancy-Szenarien oder stark reglementierten Umgebungen gewährleistet werden.
Wichtig ist an dieser Stelle, dass OPA Gatekeeper nicht die einzige Lösung für diese Probleme bietet – Anwendungen wie Kyverno haben ebenfalls viele Anwender, und kombiniert mit Anwendungen zur Laufzeitabsicherung Deiner Kubernetes Umgebung wie Tetragon oder Falco kann ein ganzheitlicher Ansatz zur Sicherung von Kubernetes Umgebungen geschaffen werden. Auf den ein oder anderen Lösungsansatz werden wir sicher in Zukunft auch hier auf unserem Blog eingehen.
Bis dahin wünschen wir dir viel Erfolg beim Umsetzen der für dich nützlichen Kubernetes Policies – solltest du hierbei Hilfe benötigen, steht dir unser MyEngineer® natürlich jederzeit zur Verfügung!





0 Kommentare