Kapitel 4. Policy as Code und Kubernetes

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Im Jahr 2018 übernahm ich die Leitung eines Teams, das Werkzeuge für die Bereitstellung von Kubernetes entwickelte, ähnlich wie Kubernetes auf die harte Tour. Wir bauten Tools, hauptsächlich mit Bash-Shell-Skripten, um Cluster bereitzustellen und zu verwalten. Zu diesem Zeitpunkt hatte ich bereits seit fast zwei Jahren in Teilzeit mit Kubernetes gearbeitet. Wir hatten Tools wie kOps in Betracht gezogen, um Cluster aufzubauen und zu aktualisieren, aber kOps entsprach damals nicht unseren Anforderungen an Cloud Computing.

Tipp

Trotz der automatisierten Dienste, Plattformen, APIs und Tools, die heute für den Aufbau und die Verwaltung von Kubernetes zur Verfügung stehen, empfehle ich dir, Kubernetes auf die harte Tour zu erforschen und zu testen. Diese Erfahrung war für meine Lernkurve von unschätzbarem Wert.

Eine meiner ersten Aufgaben war es, Kontrollen innerhalb von Kubernetes zu implementieren, um ungewollte Änderungen an Clustern zu verhindern. Wir veranstalteten einen internen Hackathon, um Ideen für Kubernetes-Kontrollen zu entwickeln. Damals habe ich zum ersten Mal PaC für Kubernetes verwendet und entdeckt, wie ich Richtlinien in die Kubernetes-API integrieren kann.

In diesem Kapitel werden wir untersuchen, wie PaC mit Kubernetes verwendet wird, um Kontrollen zu erstellen, die unerwünschte Änderungen verhindern, Leitplanken für bewährte Methoden zu errichten und Autorisierungen durchzuführen. Ich führe in die Konzepte von PaC und der Kubernetes-Integration ein und verwende Beispiele, wenn nötig. In den folgenden Kapiteln werden wir die einzelnen PaC-Lösungen näher beleuchten.

Ich beginne mit einem kurzen Überblick über die CNCF und stelle die Kubernetes-Community-Organisation vor. Dann bitte ich dich, dich mit mir auf eine kurze Tour durch die Anwendungsfälle und Lösungen zu begeben, die PaC Kubernetes bieten kann. Im weiteren Verlauf des Buches werden wir jede PaC-Lösung genauer unter die Lupe nehmen.

CNCF und Politikmanagement

In Kapitel 1 habe ich die Cloud Native Computing Foundation (CNCF) und ihre Projektstruktur vorgestellt. Hier möchte ich kurz darauf eingehen, wie sich Kubernetes einfügt. Kubernetes wurde 2016 in die CNCF aufgenommen und ist jetzt ein graduiertes Projekt. Die CNCF ist jedoch nach wie vor stark an der Ausrichtung von Kubernetes und der Kubernetes-Projektgemeinschaft beteiligt.

Innerhalb der Kubernetes-Projektgemeinschaft ist die Verwaltung der Aktivitäten in speziellen Interessengruppen (SIGs) organisiert. Diese SIGs umfassen ein breites Spektrum an Bemühungen zur Verbesserung von Kubernetes. Jede SIG ist eine Community of Practice, die sich auf bestimmte und verwandte Aktivitäten im Kubernetes-Projekt konzentriert. Die Auth Special Interest Group, auch bekannt als sig-auth, konzentriert sich auf die laufenden Bemühungen im Zusammenhang mit der Kubernetes-Authentifizierung (AuthN), Autorisierung (AuthZ) und Sicherheitsrichtlinien.

Wenn ein engerer Fokus für eine zeitlich begrenzte Arbeit erforderlich ist, werden Arbeitsgruppen (WGs) gebildet. SIGs sponsern Arbeitsgruppen und sind auch an den Bemühungen der Arbeitsgruppen beteiligt.

Die Policy WG konzentriert sich auf die Architektur der Richtlinienverwaltung und Vorschläge für Kubernetes. Sig-auth sponsert die Policy WG und ist ein gelisteter Stakeholder. In Kapitel 3 hast du das Kubernetes Policy Management Whitepaper kennengelernt, das im sig-security Projekt gespeichert ist.

Hinweis

Weitere Informationen über den Lebenszyklus von Kubernetes SIGs, WGs und User Groups (UGs) findest du in der Kubernetes Community Dokumentation.

Nach diesem Überblick über die Kubernetes SIG und die WG-Organisation wollen wir uns nun ansehen, wie wir den Kubernetes-Betrieb mit PaC verbessern und kontrollieren können.

Implementierung von Sicherheitskontrollen und Kontrolle von Verhaltensweisen

Durch die umfangreichen Funktionen von Kubernetes werden häufige Aufgaben und schwere Arbeiten, die mit dem Betrieb von Containern im großen Maßstab verbunden sind, reduziert oder sogar ganz eliminiert. Zu diesen Funktionen gehören auch Sicherheitskonfigurationen und Einstellungen, die nach bewährten Methoden vorgenommen werden können. Genauso wie Sicherheit nicht einfach am Ende eines Softwareprojekts aufgeschraubt werden kann, erfordert die Planung eines Kubernetes-Clusters - oder einer Sammlung davon - Sicherheitsdesigns und -entscheidungen am Anfang - Tag 0, wenn du so willst - deiner Kubernetes-Reise.

Auch wenn du alle Hebel in Bewegung setzen kannst, um deine Kubernetes-Anwendungen zu sichern und die Robustheit ihrer Ausführung zu erhöhen, zwingt dich Kubernetes nicht, bewährte Methoden oder empfohlene Sicherheitseinstellungen zu befolgen. Diese Lücke kann mit PaC geschlossen werden. Um zu verstehen, wie PaC Kubernetes verbessert und dabei hilft, das Verhalten innerhalb von Clustern zu kontrollieren, müssen wir zunächst untersuchen, wie Änderungen innerhalb eines laufenden Kubernetes-Clusters über API-Server-Anfragen vorgenommen werden.

API-Server-Anfragen

Kubernetes-Komponenten werden unterteilt in solche, die auf der Control Plane (CP) laufen, und solche, die auf den Knoten in der Data Plane (DP) laufen. Die CP-Komponenten verwalten den Cluster. Ohne zu tief in die Kubernetes-Architektur einzutauchen, zeigt Abbildung 4-1 die Beziehung zwischen den CP-Komponenten und den DP-Komponenten (Knotenpunkten).

Abbildung 4-1. Komponenten der Steuerungsebene und der Datenebene

Die Schlüsselkomponente, die alles zusammenhält, ist der Kubernetes-API-Server. Der API-Server bearbeitet Anfragen von externen Clients, wie z.B. kubectl, sowie von internen Clients, wie z.B. Node-Kubelet-Agenten und Controllern.

Hinweis

Kubectl ist die CLI, die für die Verwaltung und Interaktion mit Kubernetes-Clustern verwendet wird.

Wie du in Abbildung 4-1 sehen kannst, läuft die Kommunikation zwischen den Kubelet-Agenten und dem CP über den API-Server. Kein Kubelet-Agent spricht direkt mit den CP-Komponenten. Außerdem kommuniziert keine Komponente - mit Ausnahme des API-Servers - direkt mit etcd, dem Kubernetes-Schlüsselwertspeicher, in dem die Clusterkonfigurationen gespeichert sind. Die Idee hinter dieser Architektur ist, dass Änderungen, die über den API-Server kommen, in etcd gespeichert werden. Dann werden diese Änderungen an die CP-Komponenten gesendet, um Änderungen am Cluster vorzunehmen.

Kubernetes-API-Server-Anfragen können über Zulassungssteuerungen, die in den API-Server-Code kompiliert oder von AuthZ-Diensten validiert werden, geändert oder validiert werden. Als Nächstes werden wir die Zulassungssteuerungen, ihre Aktivierung und ihre Typen untersuchen.

Zutrittskontrolleure

In Kubernetes ist ein Admission Controller ein Code, der ausgeführt wird, nachdem API Serveranfragen authentifiziert und autorisiert wurden, aber bevor die Anfrage zu einer Änderung in etcd führt. Admission Controller werden in Kubernetes kompiliert und sind dazu gedacht, eingehende Anfragen abzufangen. Diese Controller können vom Typ Mutating, Validating oder sogar beides sein und fügen integrierte Kontrollen für die Sicherheit und das Verhalten des Clusters hinzu. Wie die folgenden Einträge im Protokoll des Minikube-API-Servers zeigen, sind in Minikube 1.27.1 standardmäßig 12 mutierende und 11 validierende Admission Controller geladen:

# Mutating Admission Controllers
Loaded 12 mutating admission controller(s) successfully in the following 
order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,
TaintNodesByCondition,Priority,DefaultTolerationSeconds,
DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,
DefaultIngressClass,MutatingAdmissionWebhook.
# Validating Admission Controllers
Loaded 11 validating admission controller(s) successfully in the 
following order: LimitRanger,ServiceAccount,PodSecurity,Priority,
PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,
CertificateSigning,CertificateSubjectRestriction,
ValidatingAdmissionWebhook,ResourceQuota.
Hinweis

Admission Controller reagieren nicht auf Kubernetes-Leseoperationen, wie get, watch und list. Um diese Vorgänge zu verhindern, musst du AuthZ wie RBAC verwenden.

Wie du in den vorangegangenen Zulassungssteuerungslisten sehen kannst, sind LimitRanger und ServiceAccount beide mutierende und validierende Zulassungssteuerungsstellen. Du wirst auch feststellen, dass zwei "Webhook"-Zulassungssteuerungen aufgeführt sind:

  • MutatingAdmissionWebhook

  • ValidatingAdmissionWebhook

Diese beiden "Webhook"-Zulassungssteuerungen werden verwendet, um konfigurierte dynamische Zulassungssteuerungen aufzurufen, wie die oben erwähnten Policy Engine-Dienste. Im nächsten Abschnitt werden wir uns näher mit den dynamischen Zulassungssteuerungen befassen und damit, wie sie die Kubernetes zur Laufzeit verbessern.

Dynamische Zulassungssteuerungen

Praktisch jede Änderung in einem Kubernetes-Cluster wird über den API-Server eingegeben. Das bedeutet, dass Änderungen aufihrem Weg zu etcd denselben API-Server-Anfragefluss durchlaufen, wie in Abbildung 4-2dargestellt. Mit dynamischen Zulassungscontrollern können Clusternutzer unter benutzerdefinierte Controller zu diesem API-Server-Anfragefluss hinzufügen, um die Art und Weise zu ändern, wie Anfragen zugelassen werden, ohne dass der API-Server angepasst werden muss. Auf diese Weise sind dynamische Zulassungssteuerungen eine Erweiterung des API-Servers. Wenn der Inhalt einer Anfrage nicht in etcd gespeichert wird, wird diese Änderung auch nicht im Cluster vorgenommen. Eine der wichtigsten Aufgaben der Kubernetes-Komponenten ist es, den Zustand des Clusters auf der Grundlage der in etcd gespeicherten Daten zu erhalten.

In dem in Abbildung 4-2 dargestellten Anfragefluss gibt es zwei Schritte, in denen PaC verwendet werden kann, um die eingehende Anfrage zu beeinflussen. Der Webhook für die mutierende Zulassung ruft einen konfigurierten Policy Engine Service auf, der auf den Knoten im DP-Cluster läuft. Die Nutzdaten der Anfrage werden an diesen Dienst gesendet. Wenn eine mutierende Richtlinie auf die Anfrage zutrifft, ändert der Dienst die Nutzdaten, bevor er zur Validierung des Objektschemas übergeht.

Abbildung 4-2. Kubernetes API Server Anfragefluss

Nach der Validierung des Objektschemas gibt es einen zweiten Schritt im Ablauf, bei dem ein DP-Dienst die eingehende Anfrage beeinflussen kann. Der Webhook für die Validierung der Zulassung ruft einen konfigurierten Dienst auf, um die aktuelle Nutzlast validieren zu lassen. Wenn der DV-Dienst mit einer Policy Engine verbunden ist, wird die Validierung durch alle übereinstimmenden Richtlinien durchgeführt. Wenn die Validierung den Wert "true" ergibt, wird die Änderung in etcd gespeichert, und nachgelagerte Prozesse ändern den Cluster. Wenn die Validierung jedoch ein falsches (ungültiges) Ergebnis liefert, wird die Anfrage abgebrochen und der API-Server gibt den Status sofort an den aufrufenden Client zurück. Ein Beispiel für eine Fehlermeldung bei der Verwendung des Container-Image-Tags latest ist in der folgenden Konsolenausgabe zu sehen:

Error from server ("DEPLOYMENT_INVALID": "GOOD_REGISTRY/read-only-container:
latest" container image "latest" tag/version is not allowed. Resource ID 
(ns/name/kind): "opa-test/test/Deployment"): error when creating "test.yaml":
 admission webhook <WEBHOOK_NAME>" denied the request: "DEPLOYMENT_INVALID": 
 "GOOD_REGISTRY/read-only-container:latest" container image "latest" 
 tag/version is not allowed. Resource ID (ns/name/kind): 
 "opa-test/test/Deployment"

Nachdem wir nun verstanden haben, warum es dynamische Zulassungssteuerungen gibt, wollen wir ihre Funktionsweise genauer untersuchen, indem wir uns ansehen, wie sie mit dem API-Server kommunizieren.

Nutzdaten der API-Server-Anforderung

Der Kubernetes-API-Server sendet ein AdmissionReview-API-Objekt an die konfigurierten Webhook-Dienste. Dieses Objekt enthält den Request Payload, den der Client, der die Änderung des Clusters anfordert, an den API-Server sendet. Mutating- und Validating-Webhook-Dienste werden über POST-Anfragen als Content-Type: "application/json" an den API-Server gesendet. Diese POST-Anfragen liefern das AdmissionReview API-Objekt, das von den Webhook-Diensten abgeglichen und verarbeitet wird. Je nach verwendeter PaC-Lösung können diese AdmissionReview-Objekte aus den Protokollen der Policy Engine erfasst werden.

Eine der einfachsten Möglichkeiten, um zu sehen, wie dieses AdmissionReview-Objekt aussieht, ist die Erstellung eines solchen Objekts aus einer YAML-Datei der Kubernetes-Ressource. Das GitHub-Projekt kube-review ist ein Dienstprogramm, mit dem du ein AdmissionReview-Objekt aus einer YAML-Quelldatei modellieren kannst. Das ist auch sehr praktisch, wenn du Prototypen erstellen oder Richtlinien für die Zulassung testen willst.

Die folgende YAML-Datei des Pods wird verwendet, um ein AdmissionReview-Objekt aus einer Anfrage des API-Servers mit dem Befehl kube-review create zu erstellen:

# test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: policy-test
spec:
  containers:
    - name: test-pause
      image: <IMAGE_URL>
      imagePullPolicy: Always
      securityContext:  
        allowPrivilegeEscalation: false  
        runAsUser: 1000  
        readOnlyRootFilesystem: true

Nach der Installation des Dienstprogramms kube-review kann der folgende Befehl verwendet werden, um ein AdmissionReview-Objekt zu erstellen, das widerspiegelt, was der Kubernetes-API-Server an die Webhook-Dienste von admission senden würde:

# Use kube-review to create AdmissionReview object
$ kube-review create test-pod.yaml > kube-review.json
{
    "kind": "AdmissionReview",
    "apiVersion": "admission.k8s.io/v1",
    "request": {
        "uid": "d44c6009-1a75-428d-80ac-ba2ad13b985e",
        "kind": {
            "group": "",
            "version": "v1",
            "kind": "Pod"
        },
        "resource": {
            "group": "",
            "version": "v1",
            "resource": "pods"
        },
        "requestKind": {
            "group": "",
            "version": "v1",
            "kind": "Pod"
        },
        "requestResource": {
            "group": "",
            "version": "v1",
            "resource": "pods"
        },
        "name": "test-pod",
        "namespace": "test",
        "operation": "CREATE",
        "userInfo": {
            "username": "kube-review",
            "uid": "66befdaa-1097-4249-9279-4fe5ed2fa4f3"
        },
        "object": {
            "kind": "Pod",
            "apiVersion": "v1",
            "metadata": {
                "name": "test-pod",
                "namespace": "test",
                "creationTimestamp": null
            },
            "spec": {
                "containers": [
                    {
                        "name": "test-pause",
                        "image": "<IMAGE_URL>",
                        "resources": {},
                        "imagePullPolicy": "Always",
                        "securityContext": {
                            "runAsUser": 1000,
                            "readOnlyRootFilesystem": true,
                            "allowPrivilegeEscalation": false
                        }
                    }
                ]
            },
            "status": {}
        },
        "oldObject": null,
        "dryRun": true,
        "options": {
            "kind": "CreateOptions",
            "apiVersion": "meta.k8s.io/v1"
        }
    }
}

Das vorangehende JSON AdmissionReview-Objekt würde von Diensten, die in den API-Server integriert sind und auf Knoten in der DP laufen, interpretiert und möglicherweise protokolliert werden. Wenn die Anfrage nicht durch die Zulassungskontrolle gestoppt wird, führt sie letztendlich zu einem etcd-Update und zur Erstellung eines Pods (test-pod) im policy-test Namespace.

Antwort auf die Zulassung

Die Webhook-Dienste antworten auf die Anfrage des API-Servers mit AdmissionReview-Objekten, die Antwortelemente enthalten. Dies ist ein Vertrag, den sie erfüllen müssen. Die folgenden Antworten reichen von minimalen bis hin zu erweiterten Antworten mit Statuscodes, Meldungen, Patches (Mutationen) und Warnungen:

// minimal response - allowed and uid
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true
  }
}
// Response with error code and message
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "something wasn’t allowed"
    }
  }
}
// Mutating webhook response with base64 encoded patch
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "patchType": "JSONPatch",
    "patch": "base64 encoded Patch"
  }
}
// Response with warnings
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "warnings": [
      "<WARNING_1>",
      "<WARNING_2>"
    ]
  }
}

Jede Antwort muss das Element allowed und das Element uid zurückgeben. Das Element uid muss mit dem Element uid der ursprünglichen POST-Anfrage des API-Servers übereinstimmen.

Die PaC-Engines nutzen die Eigenschaften des AdmissionReview-Objekts, um Richtlinien mit eingehenden Anfragen abzugleichen und Anfragen zu ändern oder zu validieren. Die wichtigste Methode zur Integration von PaC in den Kubernetes-API-Server sind die Admission Controller. Wir werden diese Komponenten als Nächstes untersuchen.

Dynamische Zulassungssteuerungen konfigurieren

Dynamische Kubernetes-Zulassungssteuerungen werden möglich, indem beim Start des API-Servers die kompilierten Zulassungssteuerungen MutatingAdmissionWebhook und ValidatingAdmissionWebhook geladen werden. Mit diesen beiden Zulassungssteuerungen können wir Erweiterungen des API-Server-Anfrageflusses zur Laufzeit konfigurieren, indem wir Dienste auf den DP-Knoten nutzen. Das bedeutet, dass wir, nachdem der API-Server hochgefahren und der Cluster in Betrieb ist, der DP zur Laufzeit Policy-Engine-Dienste hinzufügen und sie so konfigurieren können, dass sie von API-Server-Webhooks aufgerufen werden. Mit diesem Ansatz müssen die API-Server-Einstellungen nicht mehr von Cluster zu Cluster angepasst werden.

Ändern der Webhook-Konfiguration

Die Webhooks für die dynamische Zulassung werden wie die kompilierten Zulassungssteuerungen als mutierend und validierend kategorisiert. Mit den Webhook-Konfigurationen wird festgelegt, wie DP-Dienste - die nach dem Start des Clusters und der Konfiguration des API-Servers installiert werden - mit dem API-Server kommunizieren und ihn erweitern können. Beginnen wir mit einem Beispiel für eine mutierende Webhook-Konfiguration. Mit dem folgenden kubectl Befehl können wir ein Beispiel für eine mutatingwebhookconfiguration Ressource untersuchen:

# Get mutating webhook configuration
$ kubectl get mutatingwebhookconfiguration <WEBHOOK_CONFIGURATION_NAME> \
-o yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: <WEBHOOK_CONFIGURATION_NAME>
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    caBundle: <x509_CERT>
    url: https://127.0.0.1:23443/mutate
  failurePolicy: Ignore
  matchPolicy: Equivalent
  namespaceSelector: {}
  objectSelector: {}
  reinvocationPolicy: IfNeeded
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: '*'
  sideEffects: None
  timeoutSeconds: 10

Schauen wir uns einige der Einstellungen genauer an:

admissionReviewVersions

Array der unterstützten Versionen für die Webhook-Integration. Der API-Server sendet eine Anfrage mit der ersten Version in der Liste und durchläuft die Liste, bis eine passende (wenn überhaupt) Version gefunden wird.

clientConfig.caBundle

Ein x509-Zertifikat, das zur Authentifizierung des Kubernetes-API-Servers (Client) gegenüber dem Webhook-Dienst über TLS für eine sichere Kommunikation verwendet wird.

clientConfig.url

Interne Clusteradresse des Webhook-Dienstes.

failurePolicy

Die Webhook-Fehlerrichtlinie legt fest, was passiert, wenn ein Aufruf vom API-Server an den Webhook nicht innerhalb der konfigurierten Zeitspanne zurückkehrt.

rules

Regeln dafür, welche Kubernetes-Ressourcenanfragen vom API-Server an den Webhook-Dienst gesendet werden sollen.

Mit den obigen Einstellungen erhält dieser Mutations-Webhook authentifizierte Anfragen vom API-Server, wenn Pods erstellt werden. Der API-Server wartet maximal 10 Sekunden auf eine Antwort vom Mutationsdienst. Der API-Server ignoriert Fehler und fährt mit dem nächsten Schritt im Anfragefluss fort, unabhängig davon, ob er erfolgreich war oder nicht.

Validierung der Webhook-Konfiguration

Validierende Webhooks werden ähnlich konfiguriert wie mutierende Webhooks. Schauen wir uns ein Beispiel mit dem folgenden kubectl Befehl an:

# Get validating admission webhook configuration
$ kubectl get validatingwebhookconfiguration <WEBHOOK_CONFIGURATION_NAME> -oyaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: <WEBHOOK_CONFIGURATION_NAME>
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    caBundle: <x509_CERT>
    Service:  
      name: <WEBHOOK_SERVICE_NAME>
      namespace: <WEBHOOK_SERVICE_NAMESPACE>
      port: 443
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: <WEBHOOK_NAME>
  namespaceSelector:  
    matchExpressions:
    - key: <LABEL_NAME>
      operator: NotIn
      values:
      - ignore
  objectSelector: {}
  rules:
  - apiGroups:
    - '*'
    apiVersions:
    - '*'
    operations:
    - CREATE
    - UPDATE
    resources:
    - '*'
    scope: '*'
  sideEffects: None
  timeoutSeconds: 10

Interne Kubernetes-Dienstadresse des validierenden Webhook-Dienstes, der vom API-Server aufgerufen wird

Regel zur Auswahl von Namensräumen, um Namensräume von der Verarbeitung mit diesem validierenden Webhook ein- oder auszuschließen

Mit den oben genannten Einstellungen erhält dieser Validierungs-Webhook authentifizierte Anfragen vom API-Server, wenn eine Kubernetes-Ressource mit einer beliebigen API-Gruppe oder Version erstellt oder aktualisiert wird. Die Ausnahme von dieser Regel ist, dass keine Anfrage für Ressourcen, die in Namensräumen mit dem folgenden Label erstellt oder aktualisiert werden, an den validierenden Webhook-Dienst gesendet wird:

# Label to ignore a namespace
metadata.label.<LABEL_NAME>=ignore
Tipp

In der Regel gilt es als bewährte Methode, System-Namensräume und Namensräume, in denen die Webhook-Dienste ausgeführt werden, auszuschließen. Dies geschieht, damit der Clusterbetrieb nicht durch zu restriktive oder falsche Richtlinieneinstellungen beeinträchtigt wird. Eine kompensierende Kontrolle wäre die Verwendung von RBAC, um den Zugang zu diesen Namensräumen zu beschränken. Wenn die dynamische Mutation oder die Validierung der Zulassungssteuerungen Probleme mit dem Cluster verursachen, können die entsprechenden Konfigurationen deaktiviert werden, um die Kontrolle über den Cluster wiederzuerlangen.

Der API-Server wartet maximal 10 Sekunden auf eine Antwort des Validierungsdienstes. Wenn der API-Server innerhalb der konfigurierten Zeitspanne keine Antwort vom Dienst erhält, schlägt die Anfrage, ob gültig oder nicht, fehl.

Hinweis

Kubernetes Dynamic Admission Controller nutzen die Webhook-Integration, um Validierungs- und Mutierungsdienste aufzurufen. Für diese Aufrufe gibt es Timeout-Einstellungen (max. 30 Sekunden, Standard 10 Sekunden), die festlegen, wie lange der Aufruf warten soll, bevor eine Zeitüberschreitung eintritt. Die Webhook-Konfigurationen enthalten auch eine failurePolicy Einstellung, um zu konfigurieren, wie der API-Server reagieren soll, wenn der Webhook-Aufruf nicht innerhalb der konfigurierten Zeitspanne zurückkommt. Der Webhook schlägt entweder fehl (die Anfrage des API-Servers darf fortgesetzt werden) oder er wird geschlossen (die Anfrage des API-Servers wird blockiert). Standardmäßig wird er geschlossen fehlschlagen.

Für jedes Ausfallszenario gibt es Kompromisse. Während ein fehlgeschlagenes offenes Szenario als potenzielles Sicherheitsproblem angesehen werden könnte, könnte ein fehlgeschlagenes geschlossenes Szenario zu Problemen im Betrieb des Clusters führen. Weitere Informationen zu Ausfallrichtlinien findest du in der Kubernetes-Dokumentation.

Daten über AdmissionReview hinaus

Mit dem AdmissionReview-Objekt sind Richtlinienvalidierungen kontextabhängig, wobei der Kontext innerhalb der Grenzen der ausgewerteten API Serveranfrage liegt. Das bedeutet , dass die primäre Datenquelle der Richtlinienüberprüfung das AdmissionReview-Objekt ist. Es gibt jedoch Szenarien, in denen zusätzliche externe Daten nützlich, wenn nicht sogar erforderlich sind. Einige dieser Anwendungsfälle sind:

Überprüfung der Container-Image-Signatur

Externe Daten liefern die Signatur des Container-Images und den erforderlichen öffentlichen Schlüssel, der zur Verifizierung des Images verwendet wird.

Cluster-basierte Validierung

Wenn die Validierung einer API-Server-Anfrage die vorhandenen Cluster-Ressourcen berücksichtigen muss, um eine Entscheidung zu treffen.

Für die Validierung benötigte Nicht-Kubernetes-Daten

Gelegentlich gibt es Abhängigkeiten, die modelliert werden müssen, damit eine korrekte Validierung stattfinden kann.

Externe Daten können auf verschiedene Arten in PaC-Lösungen einfließen. Die folgenden sind nur einige davon, die wir später in diesem Buch behandeln werden:

  • Externe Daten werden je nach Bedarf bei der Auswertung herangezogen.

  • Externe Clusterdaten werden von einem Sidecar-Container gesammelt, der auf Veränderungen im Cluster achtet und regelmäßig in der Policy Engine aktualisiert wird.

  • Externe Daten von außerhalb des Clusters werden beim Start und bei Datenänderungen in die Policy Engine übertragen.

Sehen wir uns nun die erste Operation an, die im Kubernetes-API-Server-Anfragefluss durchgeführt werden kann: die Mutation.

Mutierende Ressourcen

Kubernetes mutating admission controllers können eingehende Serveranfragen durch In-Place-Patching verändern, bevor Anfragen validiert und zur Veränderung des Clusters verwendet werden. Dies ist ein bekanntes Muster in der Anwendungsentwicklung, bei dem clientseitige Daten durch serverseitige Aktionen übersetzt, erzwungen oder geändert werden, bevor die Daten validiert und in der serverseitigen Speicherung gespeichert werden. Um zu sehen, wie das funktioniert, kannst du es mit kubectl ausprobieren. Zunächst wenden wir die folgenden Namespace- und Pod-Ressourcen an:

apiVersion: v1
kind: Namespace
metadata:
  name: test
---
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: test
spec:
  containers:
    - name: test-pause
      image: <IMAGE_URL>
      imagePullPolicy: Always
      securityContext:  
        allowPrivilegeEscalation: false  
        runAsUser: 1000  
        readOnlyRootFilesystem: true

Als Nächstes können wir mit dem folgenden kubectl patch Befehl ein Label zum Pod hinzufügen:

# Add a label to a Pod via the kubectl patch command
$ kubectl -n test patch pod test-pod --patch-file patch.yaml -o yaml
# patch.yaml
metadata:
  labels:
    owner: jimmy

Die folgende YAML-Datei enthält die neue Pod-Konfiguration:

# Patched Pod
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: test
  labels:
    owner: jimmy

Die Mutation von Kubernetes-API-Serveranfragen wird ständig genutzt, meist unbemerkt. Einige der Anwendungsfälle, die ich gesehen und genutzt habe, sind :

  • Injiziere Sidecar-Container in Deployments zur Instrumentierung, z. B. Service Mesh Proxies, Observability und PaC-Lösungen

  • Hinzufügen von Beschriftungen oder Kommentaren zu Ressourcen

  • Sicherheitseinstellungen in Pods und Containern ändern

  • Hinzufügen von Toleranzen und Node-Affinity-Einstellungen zu Pods für Multitenancy-Lösungen

  • Container-Image-Registrierungen ändern

Du kannst dir auch einige der mutierenden Zutrittskontrolleure in der vorangegangenen Liste ansehen.

Tipp

Auch nach der Mutation gehört es zu den guten Zero-Trust- und DiD-Praktiken, eingehende Kubernetes-API-Server-Anfragen zu validieren, um sicherzustellen, dass die gewünschten Einstellungen auch nach der Mutation in der Nutzlast der Anfrage enthalten sind.

In der Kubernetes-Gemeinschaft gibt es unterschiedliche Meinungen zur Verwendung der Mutation eingehender API-Server-Anfragen. Einige sind der Meinung, dass sie dazu beiträgt, die Anzahl der Validierungsfehler zu reduzieren und den Kubernetes-Nutzern das Leben leichter macht. Andere sind der Meinung, dass sie die API-Server-Anfrage abdriften lässt und nicht verwendet werden sollte. Ich sehe beide Seiten dieses Arguments als stichhaltig an, obwohl ich Mutation eher selten verwende.

Als Nächstes wollen wir uns mit der Validierung eingehender API-Server-Anfragen befassen.

Ressourcen validieren

Die Validierung von Kubernetes-API-Serveranfragen verhindert unerwünschte Änderungen an deinen Clustern. Wie bereits erwähnt, ist dies ein guter Ansatz, um zusätzliche Sicherheit und bewährte Methoden zu implementieren.

Die Anwendungsfälle der Validierung umfassen Sicherheitskontrollen und bewährte Methoden. Einige der Validierungsanwendungsfälle, die ich gesehen habe und für die ich PaC-Richtlinien geschrieben habe, sind:

  • Pod- und Container-Sicherheitskonfigurationen durchsetzen

  • Container-Ressourceneinstellungen durchsetzen

  • Einschränken, woher die Container-Images bezogen werden

  • Verhindere die Verwendung von latest Tags oder keine Versionstags bei Container-Images

  • Durchsetzung von Multitenancy-Einstellungen (Node-Affinität, Taints, Toleranzen usw.)

  • Container-Image-Signaturen validieren

  • Überprüfe den Ingress und biete Kollisionsschutz

  • Quoten für Namensräume durchsetzen und Bereiche begrenzen

  • Pod-Prioritätsklassen durchsetzen

  • Durchsetzung von Budgets für Pod-Störungen

  • Verhindern, dass externe IP-Adressen in ClusterIP-Diensten verwendet werden

  • Die Erstellung oder Änderung von Ressourcen in bestimmten Namensräumen verhindern (dies ist auch ein Anwendungsfall von AuthZ)

Unter findest du ein Beispiel für eine Validierungsrichtlinie , die in der jsPolicy PaC-Lösung verwendet wird:

# Example jsPolicy validating policy
apiVersion: policy.jspolicy.com/v1beta1
kind: JsPolicy
metadata:
  name: "no-default-ns.jimmyray.io"
spec:
  operations: ["CREATE","UPDATE"]
  resources: ["*"]
  scope: Namespaced
  javascript: |
    if (request.namespace === "default") {
      deny("Create and Update in the default namespace is not allowed!");
    }

Wie du siehst, gilt die obige Richtlinie für die Operationen CREATE und UPDATE für jede Ressource im Standard-Namespace. Die Eigenschaften des AdmissionReview-Objekts -Namespace und operation- werden von dieser Richtlinie verwendet, um die eingehende Anfrage des Kubernetes-API-Servers zu bewerten und diese unerwünschten Änderungen zu verhindern, wie in der Richtlinie festgelegt.

Tipp

Es ist eine bewährte Methode, in einem Kubernetes-Cluster nicht den Standard-Namespace zu verwenden. Da das kubectl CLI standardmäßig den Standard-Namensraum auswählt, wenn im Befehl oder in der kubeconfig-Datei kein Namensraum angegeben ist, kann der Standard-Namensraum leicht verunreinigt und potenziell gefährdet werden. Die Verwendung einer validierenden Zulassungsrichtlinie, die die Verwendung des Standard-Namensraums verhindert, verringert die Wahrscheinlichkeit einer irrtümlichen Verwendung dieses Namensraums und eines nicht deterministischen Verhaltens erheblich.

Im folgenden Abschnitt werden wir uns kurz mit der Latenz von API-Server-Anfragen beschäftigen.

Latenz der API-Server-Anfrage und Webhook-Reihenfolge

In Kapitel 6 von Site Reliability Engineering (O'Reilly) haben die Autoren Betsy Beyer, Chris Jones, Niall Richard Murphy und Jennifer Petoff das Überwachungskonzept der "Four Golden Signals" vorgestellt: Latenz, Traffic, Fehler und Sättigung. In diesem Abschnitt gehen wir kurz auf die Latenz ein und untersuchen, wie sie sich auf API-Server-Anfragen im Rahmen der Zugangskontrolle auswirkt.

Im Fall des Kubernetes-API-Servers ist die Latenz die Zeit, die der API-Server benötigt, um auf eine Anfrage zu antworten oder sie zu bedienen. Diese Latenzzeit wird direkt von der Anzahl der Admission Controller und der dynamischen Admission Controller-Webhooks beeinflusst, die die eingehenden API-Server-Anfragen verarbeiten. Um Probleme mit dem API-Server aufgrund von Latenzzeiten zu vermeiden, solltest du die Anzahl der Zulassungssteuerungen und der Zulassungs-Webhooks sowie die Gesamt-Timeout-Einstellungen für alle diese Steuerungen berücksichtigen.

Die Reihenfolge, in der die Zulassungssteuerungen ausgeführt werden, ist auch wichtig, um die Latenzzeit des API-Servers zu kontrollieren. Bei dynamischen Zulassungssteuerungen ruft der API-Server zum Beispiel mutierende und validierende Webhooks unterschiedlich auf. Mutating-Webhooks werden seriell aufgerufen - in zufälliger Reihenfolge, um Kollisionen zu vermeiden. Validierende Webhooks werden gleichzeitig aufgerufen - parallel. Im Fall von mutierenden Webhooks können individuell konfigurierte Timeout-Einstellungen die Gesamt-Timeout-Zeit der Mutationsphase erhöhen und zur Gesamtlatenz der API-Server-Anfrage beitragen.

Die Standardeinstellung für die Zeitüberschreitung von Kubernetes-API-Server-Anfragen ist 60 Sekunden. Eine Änderung dieser Einstellung kann dazu führen, dass die Zeit für die erfolgreiche Ausführung von API-Server-Anfragen nicht ausreicht oder zu lang ist, was zu Denial-of-Service-Angriffen führen kann. Da die maximale Zeitüberschreitung für Admission Controller 30 Sekunden beträgt, kann die Verarbeitung von API-Server-Anfragen durch mehrere Admission Controller zu einer Zeitüberschreitung führen, die in fehlgeschlagenen Anfragen resultiert.

Es ist eine bewährte Methode, die Timeout-Einstellungen deines API-Servers unter Berücksichtigung der PaC-Webhooks, die potenziell in deinen Clustern ausgeführt werden, einzustellen. Diese Einstellung kann sich ändern, wenn deine Cluster stärker ausgelastet sind. Zur Abstimmung gehört auch, dass du die jeweiligen Timeouts auf die niedrigstmögliche Dauer einstellst, ohne dass es zu Timeout-Fehlern kommt.

Wir haben uns angeschaut, wie Kubernetes-API-Server-Anfragen geändert und validiert werden können und wie Admission Controller die Latenzzeit von API-Servern beeinflussen. Aber was machen wir mit Ressourcen, die bereits vor dem Hinzufügen neuer Richtlinien existierten oder wenn die PaC-Engines keine Verstöße erkennen? Werfen wir einen Blick auf das PaC-Auditing und das Background Scanning.

Auditing und Background Scanning Vorhandene Ressourcen

Wie bereits erwähnt, können mutierende und validierende dynamische Zulassungssteuerungen so konfiguriert werden, dass sie offen oder geschlossen fehlschlagen. In den vorangegangenen Beispielen für die Mutation und Validierung wurde die Konfiguration des mutierenden Webhooks so eingestellt, dass er offen fehlschlägt, während die Konfiguration des validierenden Webhooks auf geschlossen fehlschlägt. Wenn du die Webhook-Konfiguration so einstellst, dass sie offen fehlschlägt -failurePolicy: Ignore-, solltest du auf Nummer sicher gehen. Wenn der Webhook aus irgendeinem Grund fehlschlägt, werden die Änderungen unabhängig von der fehlenden Mutation oder Validierung durchgeführt. Diese Einstellung bietet jedoch auch die größte Chance, dass unerwünschte Änderungen den Cluster aktualisieren. Sie gilt als weniger sicher.

Wenn du die Webhooks so einstellst, dass sie fehlschlagen (failurePolicy: Fail), können keine Änderungen vorgenommen werden, wenn die Webhooks nicht erfolgreich antworten. Obwohl dies sicherer ist, kannst du auch den Betrieb deines Clusters gefährden, indem du Änderungen verhinderst. Du musst dich daran erinnern, dass viele Änderungen an einem Cluster von innerhalb des Clusters kommen und nicht von externen Client-Anfragen. Wenn du den Betrieb deines Clusters auf diese Weise gefährdest, wird dein Cluster auch als "Bricking" bezeichnet.

Um Lücken in deinen Sicherheits- und bewährten Methoden zu vermeiden, unterstützen einige Kubernetes-PaC-Lösungen Auditing (z.B. Gatekeeper) oder Background Scanning (z.B. Kyverno). Mit Auditing oder Background Scanning ergänzen PaC-Lösungen die ereignisgesteuerte Natur von API-Server-Anfragen und protokollieren oder melden verpasste Verstöße. So können ungewollte Änderungen verhindert werden, die durchgeschlüpft sind, weil die Webhook-Dienste nicht innerhalb der vorgegebenen Zeitspanne geantwortet haben und die Anfrage fortgesetzt wurde. Ein weiterer wichtiger Anwendungsfall für Auditing und Background Scanning ist die Durchführung von Auswirkungstests und Analysen bei der Freigabe neuer Richtlinien für Cluster, ohne dass bestehende Cluster-Ressourcen durch potenziell störende Durchsetzungsrichtlinien beeinträchtigt werden. Wenn wir tiefer in die einzelnen Kubernetes PaC-Lösungen eintauchen, werden wir diese Funktionen und ihre jeweiligen Einstellungen und Abläufe untersuchen.

Als Nächstes schauen wir uns an, wie wir den Aufwand für Kubernetes und die Richtlinienverwaltung reduzieren können, indem wir die PaC-Automatisierung nutzen, um Ressourcen und Richtlinien zu erstellen.

Ressourcen und Politiken generieren

Das Beste an der PaC-Integration in die Kubernetes-API-Serveranfragen ist, dass die von uns implementierten Kontrollen in erster Linie präventiv sind. Mit PaC können wir unerwünschte Änderungen in unseren Clustern ändern oder sogar verhindern, bevor sie passieren. Es gibt jedoch auch Anwendungsfälle, bei denen wir auf Ereignisse im Cluster reagieren müssen. Der Kubernetes Cluster Autoscaler reagiert zum Beispiel auf nicht planbare Pod-Ereignisse, indem er zusätzliche Cluster-Rechenknoten bereitstellt.

PaC-Lösungen können auch verwendet werden, um auf Cluster-Ereignisse zu reagieren. Einige PaC-Lösungen für Kubernetes bieten zum Beispiel die Möglichkeit, Kubernetes-Ressourcen und -Richtlinien als Reaktion auf die Anwendung anderer Kubernetes-Ressourcen im laufenden Betrieb zu generieren. Mit einer kompatiblen PaC-Lösung kannst du den Aufwand für die Richtlinienverwaltung reduzieren, indem du die Ressourcen- und Richtliniengenerierung nutzt. Die Ressourcen- und Richtliniengenerierung erhöht die Aussagekraft der Richtlinien und verringert den Aufwand für die Pflege der Richtlinien für alle Ressourcen.

Mit Kyverno kannst du zum Beispiel Richtlinien für Pods schreiben und die Kyverno Auto-Gen-Funktion nutzen, um Richtlinien für die entsprechenden Controller-Ressourcen zu erstellen, die Pods erstellen, wie z. B. Bereitstellungsressourcen. Mit der Richtlinien-Auto-Gen-Funktion kannst du die Notwendigkeit reduzieren, Richtlinien für mehrere Ressourcentypen zu verwalten. Gatekeeper verfügt mit der Richtlinienerweiterung über eine ähnliche Funktion. Ich behandle Gatekeeper und Kyverno in Kapitel 7 bzw. 8.

Tipp

Kyverno Auto-Gen funktioniert auch mit dem Kyverno CLI. Mit der CLI kannst du Richtlinien auf YAML außerhalb des Kubernetes-Clusters anwenden, bevor API-Server-Anfragen gestellt werden. Kyverno Auto-Gen funktioniert übrigens auch mit der CLI.

Eine weitere Kyverno-Funktion, generate resources, verwendet generate-policy-Typen, um Richtlinien als Reaktion auf angewandte Kubernetes-Ressourcen zu erstellen. In einem mandantenfähigen Anwendungsfall, in dem Anwendungen in ihren jeweiligen Namensräumen isoliert werden, können generierte Richtlinien verwendet werden, um den Namensraum nach seiner Erstellung und vor der Bereitstellung der Anwendungen für die Nutzung vorzubereiten.

Mit einer generierten Richtlinie kannst du zum Beispiel für jeden bereitgestellten Namespace als Teil des Bereitstellungsprozesses eine "Deny-All"-Netzwerkrichtlinienressource erstellen. Dadurch wird der Egress- und Ingress-Netzwerkverkehr für alle Pods im Namespace blockiert. Wenn die Anwendung schließlich in den Namespace eingebracht wird, können Netzwerkrichtlinien mit entsprechenden Egress- und Ingress-Regeln verwendet werden, um den am wenigsten privilegierten Netzwerkzugang für die jeweilige Anwendung zu ermöglichen. Anschließend werden die entsprechenden Mutations- oder Validierungsrichtlinien angewandt, um sicherzustellen, dass in den angewandten Netzwerkrichtlinien kein unautorisierter Netzwerkzugang konfiguriert ist.

Hinweis

jsPolicy verfügt über die Funktion der Controller-Policy - ähnlich wie bei der Erzeugung von Ressourcen - die auf Cluster-Ereignisse reagiert. In Kapitel 9 gehe ich auf jsPolicy ein.

PaC-Lösungen sind nicht immer aus dem Kubernetes-Ökosystem. Als Nächstes werden wir uns "native" Kubernetes PaC-Lösungen ansehen.

Kubernetes Native Policy Funktionen

Bisher haben wir uns auf PaC-Lösungen konzentriert, die zu Clustern hinzugefügt werden, um sie mit zusätzlichen Funktionen auszustatten und bewährte Methoden und Sicherheitskontrollen durchzusetzen. Kubernetes enthält jedoch auch systemeigene Tools, mit denen sich ähnliche Kontrollen durchführen lassen. Jetzt werden wir diese nativen Tools untersuchen.

Pod Sicherheit

Pods sind die atomare Recheneinheit in Kubernetes; sie enthalten Container. Bei der Erstellung von Pods wird die Sicherheit hauptsächlich über die securityContext-Elemente auf Pod- und Containerebene konfiguriert. Der securityContext auf Pod-Ebene, der in der Pod-Spezifikation zu finden ist, ist weniger detailliert als sein Pendant auf Containerebene und wird vom securityContext auf Containerebene außer Kraft gesetzt, wenn sich die Einstellungen überschneiden. Ansonsten werden die securityContext-Einstellungen auf Pod-Ebene von allen Containern innerhalb des Pods verwendet und oft mit den Einstellungen auf Containerebene kombiniert.

Ein Beispiel für einen securityContext auf Containerebene mit Einstellungen, die als bewährte Methoden für die Containersicherheit gelten, ist im folgenden YAML-Snippet dargestellt:

# Container-level securityContext element - best practice settings
securityContext:  
  allowPrivilegeEscalation: false  
  runAsUser: 1000  
  readOnlyRootFilesystem: true
  runAsNonRoot: true
  capabilities:
    drop: ["ALL"]  
  seccompProfile:
    type: "RuntimeDefault"

Die vorangegangenen securityContext-Einstellungen auf Containerebene wurden entwickelt, um einen Container mit den geringsten Rechten zu betreiben und gleichzeitig zu verhindern, dass der Container seine Privilegien ausweitet und unerwünschte Aufrufe an den Kernel des Knotenbetriebssystems macht.

Hinweis

In der Linux-Welt enthalten Container Anwendungen - Code, Bibliotheken und Interpreter -, die im Benutzerraum laufen. Der Benutzerraum ist der Bereich außerhalb des Betriebssystemkerns. Der Bereich innerhalb des Kernels wird Kernel-Space genannt. Damit Anwendungen im Benutzerbereich erfolgreich arbeiten können, benötigen sie Ressourcen - CPU, Speicher, Festplatte, Netzwerk usw. - aus dem Kernelbereich. Container enthalten den Benutzerbereich und machen Systemaufrufe zum Kernelbereich. Die Containersicherheit dient dazu, die Aktivitäten eines Containers im Userspace auf das zu beschränken, was er zum Betrieb benötigt. Sie dient auch dazu, die Systemaufrufe zu begrenzen, die von einem Container an den Kernel gerichtet werden können.

In den Anfängen von Kubernetes wurden PodSecurityPolicy (PSP)-Ressourcen verwendet, um Pods zu sichern und zu verhindern, dass unerwünschte Verhaltensweisen von Containern darin ausgehen. PSPs setzten die Sicherheitseinstellungen von Pods und Containern durch, die in den securityContext-Elementen enthalten sind. Allerdings waren PSPs bekanntermaßen schwierig zu konfigurieren und zu verwenden, was zu vielen Fehlkonfigurationen führte, die die gewünschte Sicherheit verringerten oder sogar den Clusterbetrieb behinderten.

Viele Kubernetes-Administratoren fügten ihren Clustern PaC-Lösungen hinzu, um die Sicherheit zu erhöhen, gewünschte Verhaltensweisen durchzusetzen und unerwünschte Verhaltensweisen zu verhindern. Das bedeutete oft, dass die Standard-PSPs im privilegierten Modus weit offen gelassen wurden. PSP-Ressourcen wurden in Kubernetes 1.21 veraltet und in Kubernetes 1.25 entfernt.

Pod Sicherheit Zulassung

PSPs waren ein Kubernetes In-Tree-Feature, und deshalb war es sehr wünschenswert, sie durch eine andere In-Tree-Lösung zu ersetzen. Der Pod Security Admission (PSA)- Controller ist der Kubernetes-in-Tree Ersatz für PSP. Dieser Admission Controller wurde in Kubernetes v1.23 als Beta-Version und in v1.25 als Stable-Version eingeführt. PSA implementiert die Kubernetes Pod Security Standards (PSS), die 17 Sicherheitskontrollen für Pod-Konfigurationen definieren und in drei Stufen unterteilt sind:

  • Privilegiert (ungesichert)

  • Baseline (sicher)

  • Eingeschränkt (hochsicher)

Hinweis

Der Begriff " in-tree" bezieht sich im Zusammenhang mit Kubernetes auf den Ort, an dem die Kubernetes-Funktionen liegen. Wenn eine Funktion mit einem Kubernetes-Release ausgeliefert wird und ohne Installation zusätzlicher Software genutzt werden kann, wird diese Komponente als " in-tree" bezeichnet. Die PSPs waren ein In-Tree-Feature. PaC-Lösungen werden vom Kubernetes-Ökosystem zur Verfügung gestellt und in Kubernetes installiert oder integriert; PaC-Lösungen gelten als out-of-tree.

Um das klarzustellen: Der Name "In-Tree" ist nicht unbedingt ein Indikator dafür, wie einfach zu konfigurieren oder benutzerfreundlich eine Funktion ist. Tatsächlich wurden die PSPs zum großen Teil wegen ihrer Komplexität und schwierigen Benutzerfreundlichkeit aus Kubernetes entfernt.

PSA implementiert PSS mit drei Betriebsmodi. Die Modi bilden zusammen mit den PSS-Stufen logische Sicherheitsrichtlinien, die zur Kontrolle der Pod-Sicherheit innerhalb eines Kubernetes-Clusters verwendet werden, indem sie die Einstellungen in den Pod- und Container-SecurityContext-Elementen durchsetzen. Die PSA-Modi sind:

Erzwinge

Richtlinienverstöße verhindern die Bereitstellung von Pods.

Warnen

Richtlinienverstöße führen dazu, dass der Kubernetes-API-Server mit Warnungen reagiert.

Audit

Richtlinienverstöße führen zu Audit-Anmerkungen bei Ereignissen, die in den Audit-Protokollen des Kubernetes-API-Servers aufgezeichnet werden.

PSA ist ein Admission Controller, der geladen wird, wenn der Kubernetes-API-Server startet. Wie in k8s-psa-pss-testing - einemOSS-Projekt, das ich während meiner Zeit im AWS Kubernetes-Team erstellt habe und für das ich verantwortlich bin - zu sehen ist, kann PSA beim Start des API-Servers angepasst werden. Ohne eine solche Anpassung sind die clusterweiten Pod-Sicherheitseinstellungen, die PSA und PSS verwenden, für alle drei PSA-Modi standardmäßig auf die privilegierte PSS-Ebene eingestellt. Diese Standardeinstellungen führen zu unsicheren Pod-Sicherheitseinstellungen in den jeweiligen Clustern.

Um sicherere PSA- und PSS-Einstellungen anwenden zu können, müssen Kubernetes-Namensräume mithilfe von Kubernetes-Namensraum-Labels für mehr Sicherheit optieren. Wie im folgenden Codeschnipsel aus dem k8s-psa-pss-testing-Projekt zu sehen ist, werden Labels verwendet, um den Namespace für bestimmte PSA-Modi und PSS-Stufen zu wählen:

# Namespace labels for PSA and PSS settings
apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:    
    # pod-security.kubernetes.io/enforce: privileged
    # pod-security.kubernetes.io/audit: privileged
    # pod-security.kubernetes.io/warn: privileged
    
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/audit: baseline
    pod-security.kubernetes.io/warn: baseline
    
    # pod-security.kubernetes.io/enforce: restricted
    # pod-security.kubernetes.io/audit: restricted
    # pod-security.kubernetes.io/warn: restricted

Außerdem können mehrere Kombinationen von PSA-Modi und PSS-Stufen verwendet werden. Im folgenden Beispiel ist der PSA-Erzwingungsmodus für die PSS-Basisebene eingestellt, während PSA-Prüfung und -Warnung auf PSS eingeschränkt eingestellt sind. Dies erfüllt den Anwendungsfall, in dem du die Sicherheit eines Pods auf der Basisstufe erzwingen möchtest, aber auch die möglichen Auswirkungen eines Wechsels zur PSS-Eingeschränkt-Stufe kennst:

# Mixed PSA modes and PSS levels
apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:        
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

PSA Enforce ist der einzige PSA-Modus, der verhindert, dass Änderungen in einem Kubernetes-Cluster physisch stattfinden. Zum Zeitpunkt der Erstellung dieses Dokuments reagiert PSA enforce nur auf Pod-Änderungen; es verhindert nicht, dass andere Kubernetes-Ressourcen, die Pods erstellen oder aktualisieren, erstellt oder aktualisiert werden - und auch keine anderen Ressourcen. Wenn beispielsweise eine Kubernetes-Bereitstellungsressource auf einen Cluster angewendet wird und die Pod-Spezifikationen gegen die aktuellen PSA/PSS-Einstellungen für den Namespace verstoßen, in dem sich die Pods befinden, werden die Pods abgelehnt und können nicht bereitgestellt werden. Das Deployment würde jedoch erstellt werden, ohne dass es einen Hinweis darauf gibt, dass etwas nicht in Ordnung ist. Die Pods würden einfach nicht starten, weil die Änderungen für die Bereitstellung der Pods nicht validiert und für etcd freigegeben wurden.

Da der PSA-Erzwingungsmodus das Kubernetes-Deployment nicht anhält, musst du den Status der Bereitstellungsressource untersuchen, um festzustellen, warum die Pods nicht gestartet wurden:

# Examine Deployment status to determine Pod start issues
$ kubectl -n policy-test get deploy test -oyaml

...
status:
  conditions:
...
  - lastTransitionTime: "2022-07-12T23:56:10Z"
    lastUpdateTime: "2022-07-12T23:56:10Z"
    message: >
          'pods "test-59955f994-wl8hf" is forbidden: violates 
          PodSecurity "restricted:latest":
          allowPrivilegeEscalation != false (container "test" 
          must set securityContext.allowPrivilegeEscalation=false),
          unrestricted capabilities (container "test" must set 
          securityContext.capabilities.drop=["ALL"]),
          runAsNonRoot != true (pod or container "test" must set 
          securityContext.runAsNonRoot=true),
          seccompProfile (pod or container "test" must set 
          securityContext.seccompProfile.type
          to "RuntimeDefault" or "Localhost")'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
...

Das ist eine schwierige Benutzererfahrung und ein weiterer Grund, warum du alle PSA-Modi verwenden solltest, nicht nur den Enforce-Modus. Die PSA-Warn- und Audit-Modi reagieren auf Pod-Controller wie Kubernetes Deployment- und DaemonSet-Ressourcen. Auch wenn die Deployments also nicht verhindert werden, erhalten zumindest der Kubernetes API-Server-Client und die API-Server-Audit-Logs Hinweise auf eine mögliche Sicherheitsverletzung. Ein Beispiel für eine Warnung, die an Kubernetes-API-Server-Clients gesendet wird, ist in der folgenden Konsolenausgabe zu sehen:

Warning: would violate PodSecurity "restricted:latest": 
allowPrivilegeEscalation != false (container "test" must 
set securityContext.allowPrivilegeEscalation=false), 
unrestricted capabilities (container "test" must set 
securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true 
(pod or container "test" must set securityContext.runAsNonRoot=true), 
seccompProfile (pod or container "test" must set 
securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
deployment.apps/test created

Für Leute, die PSP ersetzen oder Pod-Sicherheit implementieren, ist PSA eine relativ einfache Migration, solange die PSS-Stufen ihren Sicherheitsanforderungen entsprechen. Wenn Kubernetes-Benutzer jedoch mehr Granularität und Flexibilität für ihre Pod-Sicherheitsschemata benötigen oder sogar andere Verhaltensweisen für zusätzliche Kubernetes-Ressourcen erzwingen wollen, sind PaC-Lösungen die bessere Wahl.

Es ist auch wichtig zu verstehen, dass PaC auf demselben Cluster wie PSA und PSS betrieben werden kann; sie schließen sich nicht gegenseitig aus. Es gibt sogar Anwendungsfälle, in denen PaC die Sicherheit von PSA und PSS und die Benutzerfreundlichkeit verbessert. PaC kann zum Beispiel die Nutzung von PSA und PSS verbessern, indem es das PSA/PSS Opt-in-Modell durchsetzt. Mithilfe der dynamischen Kubernetes-Zugangssteuerung, die weiter oben in diesem Kapitel behandelt wurde, kann PaC die PSA/PSS-Labels für Namensräume, über Richtlinien und für mutierende und validierende Webhook-Konfigurationen durchsetzen.

Hinweis

Weitere Informationen und Beispiele für den Einsatz von PaC-Lösungen mit Kubernetes PSA/PSS findest du in diesem Blogbeitrag, den ich gemeinsam mit Jim Bugwadia von Nirmata verfasst habe: "Managing Pod Security on Amazon EKS with Kyverno".

Nicht alle PaC-Lösungen kommen von außerhalb von Kubernetes, d.h. nicht alle PaC-Lösungen stammen aus dem Kubernetes-Ökosystem und sind außerhalb des Baums angesiedelt. In diesem Abschnitt haben wir PSA vorgestellt, eine bauminterne Funktion, die in Verbindung mit PSS die Möglichkeit bietet, Richtlinien für die Pod-Sicherheit anzuwenden. Wir haben die Pod-Sicherheit in Kubernetes jedoch nur oberflächlich erkundet; in den folgenden Kapiteln werden wir tiefer eintauchen, wenn wir uns mit spezifischen PaC-Lösungen und ihrer Verwendung in Kubernetes beschäftigen.

Jetzt wollen wir eine neue Kubernetes In-Tree-Funktion entdecken, die PaC als Teil eines Kubernetes In-Tree-Angebots internalisiert.

Validierung der Zulassungspolitik

Seit Kubernetes v1.28 ist eine neue bauminterne PaC-Lösung, Validating Admission Policy (VAP), in der Beta-Phase. VAP ist eine native (bauminterne) PaC-Lösung, die in Kubernetes eingebettet und "hoch konfigurierbar" ist. Mit VAP können Clusteradministratoren und -betreiber Richtlinien erstellen, um bewährte Methoden und Sicherheitskontrollen durchzusetzen.

Anders als PSA funktioniert VAP sowohl mit Pods als auch mit Nicht-Pods. Die Funktionalität von VAP ähnelt den PaC-Lösungen, die ich in den nächsten Kapiteln behandeln werde. Im Gegensatz zu diesen PaC-Lösungen ist VAP jedoch in Kubernetes integriert und erfordert keine zusätzlich installierte Software.

Um diese Funktion zu nutzen, musst du die richtigen Konfigurationen anwenden, um sie in deinem Cluster zu aktivieren:

  • Aktiviere das ValidatingAdmissionPolicy Feature Gate.

  • Aktiviere die Zulassungsregistrierung.k8s.io/v1alpha1 API.

Die für VAP benötigten Kubernetes-Ressourcen sind:

ValidatingAdmissionPolicy

Enthält die Regellogik für die Richtlinie, einschließlich der Ressourcen und Vorgänge, auf die die Richtlinie angewendet wird, sowie die Common Expression Language (CEL)-Ausdrücke

ValidatingAdmissionPolicyBinding

Bindet die Richtlinie an einen bestimmten Bereich, wie einen Namespace, und bindet Parameterressourcen an die Richtlinie

ParameterResource

Ermöglicht es, die Richtlinienkonfiguration von der Richtliniendefinition zu trennen

VAP geht über die Pod-Sicherheit hinaus und bietet eine in Kubernetes integrierte PaC-Lösung, die auf CEL von Google basiert. Die CEL-Syntax ähnelt der von C oder Java und ist in YAML-Ressourcen eingebettet, wie die folgenden VAP-Ressourcenbeispiele zeigen:

# ValidatingAdmissionPolicy resource
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
  name: "deploy-history-policy.jimmyray.io"
spec:
  failurePolicy: Fail
  paramKind:
    apiVersion: rules.jimmyray.io/v1
    kind: HistoryLimit
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.revisionHistoryLimit <= params.historyLimit"
      reason: Invalid

Die vorangehende ValidatingAdmissionPolicy-Ressource gilt für Einsätze, die erstellt oder aktualisiert werden. Die CEL-Syntax sucht nach der revisionHistoryLimit-Einstellung in der Einsatzspezifikation. Das Feld failurePolicy kann auf Fail oder Ignore gesetzt werden. Fail bedeutet, dass der API-Vorgang fehlschlägt, wenn der CEL-Ausdruck falsch ist, und der Cluster nicht verändert wird.

Das Feld paramKind legt die Parameter-Ressource fest, die die ValidatingAdmissionPolicy verwenden wird, um die Parameter zu erhalten, die sie für die Auswertung der API-Server-Anfrage benötigt. Diese Parameter-Ressource ist in der folgenden YAML-Datei dargestellt:

# Resource parameter
apiVersion: rules.jimmyray.io/v1
kind: HistoryLimit
metadata:
  name: "deploy-history-limit.jimmyray.io"
historyLimit: 3

Um alles miteinander zu verbinden, brauchen wir die ValidatingAdmissionPolicyBinding, die in der folgenden YAML dargestellt ist:

# Binding
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "deploy-history-binding.jimmyray.io"
spec:
  policy: "deploy-history-policy.jimmyray.io"
  paramsRef:
    name: "deploy-history-limit.jimmyray.io"
  matchResources:
    namespaceSelectors:
    - key: environment,
      operator: In,
      values: ["policy-test"]

Die ValidatingAdmissionPolicyBinding verknüpft die Richtlinien- und Parameterressourcen und legt den Geltungsbereich der Richtlinienanwendung auf den policy-test Namensraum fest.

Um diese Richtlinie zu testen, werden wir zwei Einsätze verwenden:

  • Bekannte gute Spezifikationen mit revisionHistoryLimit <= 3

  • Bekannte fehlerhafte Spezifikationen mit revisionHistoryLimit > 3 (einschließlich eines fehlenden revisionHistoryLimit-Feldes, das standardmäßig auf 10 gesetzt ist)

Die Fehlermeldung, die durch die fehlgeschlagene Validierung ausgelöst wurde, wird in der folgenden Konsolenausgabe angezeigt:

# VAP error message
ValidatingAdmissionPolicy 'deploy-history-policy.jimmyray.io' with binding 
'deploy-history-binding.jimmyray.io' denied request: failed expression: 
object.spec.revisionHistoryLimit <= 3

Nach jahrelanger Arbeit mit PaC bin ich vorsichtig optimistisch, was das Potenzial der ValidatingAdmissionPolicy-Funktion angeht, die von CEL unterstützt wird. ValidatingAdmissionPolicy hat das Potenzial, den Aufwand für die Verwaltung der Infrastruktur für PaC-Lösungen zu verringern, die nach der Bereitstellung von Clustern in Kubernetes installiert werden müssen. Ich freue mich auch darauf, endlich tiefer in CEL einzutauchen, mit Anwendungsfällen, die es mir leicht machen, meine PaC-Erfahrung anzuwenden.

Hinweis

Zusätzlich zu der Funktion ValidatingAdmissionPolicy arbeitet das Kubernetes-Projekt an einem neuen Kubernetes Enhancement Proposal (KEP) für eine MutatingAdmissionPolicy-Funktion. KEP-3962 behandelt dieses neue MutatingAdmissionPolicy-Feature. Zum Zeitpunkt der Erstellung dieses Dokuments soll die Funktion erstmals in Kubernetes Version v1.31 veröffentlicht werden.

Lassen wir nun kurz die Zugangskontrolle hinter uns und wenden uns der Zugriffskontrolle zu, um zu sehen, wie PaC für AuthZ-Anwendungsfälle in Kubernetes genutzt werden kann.

AuthZ Webhook Modus

An dieser Stelle möchte ich den Fokus von der Zugangskontrolle abwenden und eine andere Möglichkeit zur Integration von PaC in Kubernetes für AuthZ betrachten. AuthZ in Kubernetes wurde entwickelt, um den Zugriff von externen und internen Clients auf den Kubernetes API-Server zu kontrollieren. Kubernetes unterstützt mehrere Arten von AuthZ, darunter Zugriff auf Knotenebene, Client-AuthZ über Webhook-Integration sowie RBAC und ABAC, die wir in Kapitel 3 untersucht haben.

Im Gegensatz zu PaC für die Zulassungskontrolle - die hauptsächlich in dynamische Zulassungssteuerungen integriert ist - wird die AuthZ-Webhook-Integration beim Start des Clusters auf dem API-Server konfiguriert. Diese Art von AuthZ wird nicht so häufig verwendet wie RBAC, kann aber mit RBAC kombiniert werden, um eine granularere Sicherheit zu erreichen. Der Webhook-Modus kann bei der Integration von externen AuthN-Anbietern sehr effektiv sein. Der AuthZ-Webhook wird nicht so häufig genutzt wie RBAC, vor allem weil der Kubernetes-API-Server so konfiguriert werden muss, dass er in Cluster- oder Remote-Dienste integriert werden kann, um AuthZ-Entscheidungen zu verarbeiten. Außerdem sind Entscheidungen für die Kubernetes-Multitenancy, wie die Verwendung von granularen Namensräumen mit RBAC und die Erweiterung von AuthZ mit mutierenden und validierenden Zulassungsdiensten, die über dynamische Zulassungssteuerungen integriert werden, traditionell einfacher zu konfigurieren und zu unterstützen.

Wie bei der Zugangskontrolle sendet der Kubernetes-API-Server JSON-Payloads an die AuthZ-Webhook-Dienste und erhält Entscheidungen über die Zulassung oder Ablehnung. Der folgende Beispiel-Payload wird vom API-Server gesendet:

// SubjectAccessReview
{
    "apiVersion": "authorization.k8s.io/v1beta1",
    "kind": "SubjectAccessReview",
    "spec": {
        "resourceAttributes": {
            "namespace": "kube-system",
            "verb": "get",
            "resource": "pods",
            "version": "v1"
        },
        "user": "jimmy",
        "groups": [
            "system:authenticated",
            "devops"
        ]
    }
}

Als erstes fällt auf, dass der SubjectAccessReview-Beispiel-Payload kleiner ist als der AdmissionReview-Beispiel-Payload von weiter oben in diesem Kapitel. Das liegt vor allem daran, dass die AdmissionReview die tatsächlich gewünschten Änderungen an den Cluster-Ressourcen enthält, während die SubjectAccessReview vor allem die gewünschten Aktionen und die AuthN-Prinzipinformationen, wie z. B. die Gruppenzugehörigkeit, enthält.

Nachdem wir nun die Idee des AuthZ-Webhook-Modus verstanden haben, wollen wir uns genauer ansehen, wie die AuthZ-Entscheidungen bei diesem Ansatz getroffen werden.

AuthZ-Entscheidungen

Die YAML-Konfiguration für den AuthZ-Webhook verwendet das kubeconfig-Format, wobei der clusters-Knoten zur Konfiguration der AuthZ-Dienste - auch Autorisierergenannt -verwendet wird , dievom API-Server aufgerufen werden. Der Benutzer-Knoten wird verwendet, um den API-Server für die sichere Kommunikation mit dem Webhook-Dienst zu konfigurieren. Das bedeutet, dass für AuthZ-Entscheidungen des API-Servers mehrere Authorizer verwendet werden können.

Entscheidungen, die von den Autorisierern zurückgegeben werden, sind entweder erlaubt oder verweigert, und diese Entscheidungen schließen sich nicht immer gegenseitig aus. Da mehrere Autorisierer so konfiguriert werden können, dass sie AuthZ-Entscheidungen zurückgeben, ist es möglich, dass nicht alle Autorisierer einem Nutzer die Durchführung einer Aktion "erlauben" oder genehmigen können. Und wenn ein Autorisierer den Zugriff nicht erlauben kann, heißt das nicht unbedingt, dass derselbe Autorisierer den Zugriff auch verweigern kann. Es könnte sein, dass der Autorisierer, der den Zugriff nicht erlauben kann, einfach keine deterministischen Regeln hat, um den Zugriff zu verweigern.

Bei mehreren Berechtigungsgebern kann ein Berechtigungsgeber, der einen Zugriff aus irgendeinem Grund nicht zulassen kann, die Entscheidung an nachfolgende Berechtigungsgeber weitergeben, indem er eine allowed=false Bedingung zurückgibt, ohne eine denied=true Bedingung zurückzugeben. Ein Autorisierer, der über die entsprechenden Regeln verfügt, um auf die über SubjectAccessReview übermittelten Informationen zu reagieren, kann die Anfrage auch mit einer deterministischen Logik ablehnen. Mit mehreren Autorisierern werden die Zugriffsentscheidungen sehr granular. Tabelle 4-1 erklärt, wie Genehmigungs- und Ablehnungsentscheidungen kombiniert werden können.

Tabelle 4-1. Mögliche Entscheidungen des Genehmigungsinhabers
erlaubt = true Autorisierer erlauben kann.
erlaubt = false Autorisierer können nicht erlauben, aber auch nicht verweigern.
erlaubt = false verweigert = true Der Autor kann verweigern.

Die Autorisierer geben ein SubjectAccessReview JSON-Objekt mit dem Feld status zurück, das die Entscheidung und die entsprechenden Meldungen enthält:

// Decision to allow access, message not necessary
{
  "apiVersion": "authorization.k8s.io/v1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": true
  }
}
// Decision to disallow access, but not deny, additional authorizers can deny
{
  "apiVersion": "authorization.k8s.io/v1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "reason": "Non-admin users cannot access admin namespaces."
  }
}
// Decision to disallow and deny
{
  "apiVersion": "authorization.k8s.io/v1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "denied": true,
    "reason": "Non-admin users cannot access admin namespaces."
  }
}

Wenn eine denied=true Antwort von einem Autorisierer zurückgeschickt wird, müssen keine weiteren konfigurierten Autorisierer für eine Entscheidung kontaktiert werden.

Da wir nun wissen, was die Autorisierer tun, schauen wir uns an, wie sie es tun: mit der PaC-Integration.

AuthZ Webhook und PaC

Angesichts der Tatsache, dass JSON zwischen dem API-Server und den AuthZ-Diensten ausgetauscht wird, kann man sich leicht vorstellen, wie die PaC-Tools genutzt werden können, um die AuthZ-Entscheidungen für den API-Server zu treffen. Der Trick besteht darin, den API-Server zu starten und den AuthZ-Webhook zu konfigurieren, ohne notwendige Änderungen zu verhindern, bevor der AuthZ-Webhook konfiguriert ist.

Die Kubernetes-Dokumentation scheint darauf hinzuweisen, dass ein externer Dienst zur Erstellung der AuthZ-Webhook-Integration verwendet wird. Eine bewährte Methode von PaC ist jedoch, die Entscheidungsmaschinen so nah wie möglich an den Entscheidungspunkten zu platzieren. Das Hinzufügen zusätzlicher Netzwerksprünge durch den Aufruf von Diensten außerhalb des Clusters kann die Antwort langsamer und die Lösung brüchig machen. Der Betrieb von PaC-Engines innerhalb des Clusters mit mehreren Pods schafft außerdem Partitionstoleranz.

Ein Beispiel für eine solche Konfiguration, die kind, OPA und kubeadm verwendet, um diese Lösung zu demonstrieren, findest du in einem OPA-Beitrag. Bei dieser Lösung wird OPA auf den Kubernetes-Kontrollknoten mit DaemonSet-Ressourcen ausgeführt. In der Konfiguration(Abbildung 4-3) wird eine statische IP-Adresse für die Kubernetes Service-Ressource festgelegt, die den vom DaemonSet erstellten OPA-Pods vorgelagert ist.

Abbildung 4-3. AuthZ Webhook-Konfiguration mit OPA

Mithilfe von Kubernetes Taints und Toleranzen werden die in Abbildung 4-3 gezeigten Pods von den Kubernetes CP-Knoten isoliert. Alle Dienste, die empfindlich auf den Clusterbetrieb reagieren - wieder API-Server AuthZ - sollten auf den CP-Knoten isoliert werden.

Beispiel Politik

Um die oben genannten Autorisierungsentscheidungen zu erstellen, wird mit dem SubjectAccessReview-Objekt in der folgenden Beispiel-OPA-Richtlinie Nicht-Admin-Nutzern der Zugriff auf Admin-Namensräume verweigert:

# OPA policy to restrict admin Namespace access
package k8s.authz

import future.keywords.in

# Admin namespaces
admin_nss := ["kube-system","admin","opa"]

# Non-admin users cannot access admin namespaces.
deny[reason] {
	input.spec.resourceAttributes.namespace in admin_nss
	not "admin" in input.spec.groups
	reason := "Non-admin users cannot access admin namespaces."
}

decision = {
	"apiVersion": "v1",
	"kind": "SubjectAccessReview",
	"status": {
		"allowed": count(deny) == 0,
		"deny": count(deny) > 0,
		"reason": concat(" | ", deny),
	},
}

Der Zweck des AuthZ-Webhooks ist es, den Zugriff auf Kubernetes-API-Server-Aktionen zu erlauben oder zu verweigern, und zwar auf der Grundlage von Informationen über das authentifizierte Prinzip, das versucht, die Aktion durchzuführen. Diese AuthN-Informationen werden im AdmissionReview-Objekt, das bei der Mutation und Validierung der Zulassung über dynamische Zulassungssteuerungen verwendet wird, nicht angezeigt, es sei denn, solche Daten werden der eingehenden Anfrage auf nicht autorisierende Weise hinzugefügt. Obwohl PaC also problemlos verwendet werden kann, um eingehende API-Server-Anfragen über dynamische Admission Controller zu validieren, sind die Informationen, die für die Autorisierung von Anfragen benötigt werden - wie sie von den AuthZ-Webhook-Autorisierern vorgenommen werden - nicht verfügbar. Zurzeit können dynamische Kubernetes-Zugangssteuerungen nicht für diese Art von AuthZ-Anwendungsfall verwendet werden.

Hinweis

Das AWS Kubernetes (EKS)-Team hat im Dezember 2023 "vereinfachte Zugriffskontrollen für Amazon EKS" vorgestellt. Dieser Ansatz basiert auf der Kubernetes-Authorizer-Funktionalität. Während meiner Zeit bei AWS im Kubernetes-Team habe ich mitgeholfen, die Funktion zu testen und zu diesem Blogbeitrag beigetragen, in dem sie vorgestellt wird. Mein Name wurde nicht als Autor genannt, da ich AWS verlassen habe, bevor die Funktion veröffentlicht wurde.

Bevor wir dieses Kapitel abschließen, müssen wir noch untersuchen, wie das PaC-Reporting umgesetzt und genutzt werden kann.

Berichterstattung über die Politik

Verschiedene PaC-Lösungen bieten unterschiedliche Möglichkeiten zur Berichterstattung und Protokollierung. Alle PaC-Lösungen, mit denen ich gearbeitet habe, verfügen über verschiedene Protokollierungsstufen, aber Protokollierung ist nicht gleich Berichterstattung, genauso wenig wie das Sammeln von Kennzahlen gleich Protokollierung ist. Die PaC-Berichterstattung dient dazu, zu überprüfen, ob deine PaC-Lösung das gewünschte Ergebnis liefert. Darüber hinaus können mit der PaC-Berichterstattung informatorische und prüfbare Artefakte erstellt werden, um interne und externe gesetzliche Anforderungen zu erfüllen.

Wenn es um PaC-Reporting in Kubernetes geht, ist die Implementierung, auf die ich mich als Standard stützen würde, das offene Format, das von der Kubernetes Policy WG definiert wurde. Dieser PolicyReport wird von einer Kubernetes Custom Resource Definition (CRD) unterstützt:

# policyreports.wgpolicyk8s.io CRD
$ kubectl get crd policyreports.wgpolicyk8s.io -oyaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:

  name: policyreports.wgpolicyk8s.io
spec:
  conversion:
    strategy: None
  group: wgpolicyk8s.io
  names:
    kind: PolicyReport
    listKind: PolicyReportList
    plural: policyreports
    shortNames:
    - polr
    singular: policyreport
  scope: Namespaced
  versions:

    name: v1alpha2

Wie du an der gekürzten Ausgabe siehst - CRDs sind bekanntermaßen lang und unübersichtlich - ist die aktuelle Version des PolicyReport CRD v1alpha2. In Anbetracht des Adoptionsmusters von Kuberrnetes steigt die Akzeptanz neuer Funktionen in der Regel erst, wenn die Funktion zumindest den Beta-Status erreicht hat. Abgesehen davon habe ich PolicyReports mit der Kyverno Policy Engine verwendet.

Tipp

Kubernetes CRDs werden verwendet, um die Kubernetes-API zu erweitern. Der folgende kubectl api-resources Befehl kann verwendet werden, um die vorhandenen API-Ressourcen in deinem Cluster anzuzeigen:

$ kubectl api-resources | grep pod
NAME        SHORTNAMES   APIVERSION    NAMESPACED   KIND
pods        po           v1            true         Pod
…

Der folgende kubectl explain Befehl kann verwendet werden, um die APIs zu erkunden:

$ kubectl explain pod.spec.containers.name
KIND:     Pod
VERSION:  v1

FIELD:    name <string>

DESCRIPTION:
     Name of the container specified as a DNS_LABEL. 
     Each container in a Pod must have a unique name 
     (DNS_LABEL). Cannot be updated.

PolicyReports können in Namespaced und Cluster eingeteilt werden. Das folgende Beispiel enthält einen PolicyReport mit Namespace-Zugriff aus dem Namespace kyverno:

# List PolicyReports in the kyverno namespace
$ kubectl -n kyverno get policyreports.wgpolicyk8s.io
NAME           PASS   FAIL   WARN   ERROR   SKIP   AGE
cpol-example   2      0      0      0      0       1h10m
# Review PolicyReport
$ kubectl -n kyverno get policyreports.wgpolicyk8s.io cpol-example \
-o yaml
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:

  labels:
    app.kubernetes.io/managed-by: kyverno
  name: cpol-example
  namespace: kyverno
results:
- message: Validation rule 'autogen-example' passed.
  policy: example
  resources:
  - apiVersion: apps/v1
    kind: Deployment
    name: kyverno
    namespace: kyverno
  result: pass
  rule: autogen-example
  scored: true
  source: kyverno

summary:
  error: 0
  fail: 0
  pass: 2
  skip: 0
  warn: 0

Der vorstehende PolicyReport bezieht sich auf die Kyverno ClusterPolicy cpol-example, die automatisch für Bereitstellungsressourcen aus einer definierten Pod-Policy generiert wurde. Der Bericht zeigt auch, dass die Richtlinie zwei Validierungen bestanden hat. Der PolicyReport ist ein nützliches Artefakt für den menschlichen Verzehr, und da er aus YAML besteht, ist er auch maschinenlesbar, wie die Richtlinien selbst.

Nicht alle PaC-Lösungen beinhalten ein Berichtswesen. Wir werden diese und andere Funktionen in den folgenden Kapiteln näher betrachten, wenn wir uns mit spezifischen PaC-Lösungen beschäftigen.

Zusammenfassung

Wenn es eine Sache gibt, die ich durch meine Arbeit mit Kubernetes in den letzten sieben Jahren gelernt habe, dann ist es die, dass sich Kubernetes ständig weiterentwickelt, angetrieben von seiner Gemeinschaft aus Entwicklern und Interessenvertretern. Wenn sich Kubernetes weiter verändert, wird sich auch PaC zwangsläufig weiterentwickeln. Wir erleben diese Entwicklung gerade mit neuen PaC-Funktionen aus dem Kubernetes-Ökosystem und neuen bauminternen Lösungen.

In diesem Kapitel habe ich mehrere Kubernetes-Anwendungsfälle vorgestellt, die mit PaC erfüllt werden können. Du solltest jetzt grundlegende Kenntnisse über Admission Controller und ihren Zweck in deinen Clustern haben. Ich habe erklärt, wie du eingehende API-Server-Anfragen mit dynamischen Admission Controllern verändern und validieren kannst und wie du mit Richtlinien auf Ereignisse in deinem Cluster reagierst, z. B. auf Ressourcenänderungen (CREATE und UPDATE).

Wir haben neue bauminterne Lösungen wie Pod Security Admission und Validating Admission Policy erkundet. Wir haben einen kurzen Abstecher gemacht, um herauszufinden, wie PaC mit dem Kubernetes-Webhook-Modus für AuthZ genutzt werden kann. Zum Schluss haben wir uns die neuen Standards der Policy WG zum Policy-Reporting angesehen.

Nach all diesen Informationen, die du in diesem Kapitel verarbeiten sollst, möchte ich dich an meine ursprüngliche Empfehlung aus Kapitel 1 erinnern, wie du die richtige PaC-Lösung für deine Bedürfnisse auswählst. In diesem Kapitel gibt es mehrere Anwendungsfälle, die du für deine PaC Solution Selection Scorecard berücksichtigen solltest, wenn du Kubernetes-Cluster betreibst und absicherst. Während wir uns in den nächsten Kapiteln jede Lösung ansehen, solltest du überlegen, wie gut die Lösung zu dem beschriebenen Anwendungsfall sowie zu deinen Bedürfnissen und Fähigkeiten passt. Ich werde dich auf deinem Weg begleiten, indem ich die Eignung diskutiere, Probleme und Herausforderungen aufzeige und Empfehlungen gebe.

Wir beginnen unsere Übersicht über spezifische PaC-Lösungen in Kapitel 5, in dem wir untersuchen, wie OPA mit Kubernetes installiert und verwendet wird, um die Sicherheit, Kontrolle und Benutzerfreundlichkeit zu verbessern.

Get Politik als Kodex now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.