Ein Cluster ohne Admission Policy zieht jedes Image, das du ihm gibst. kubectl apply oder helm install prüfen nur, ob die Referenz auflösbar ist, nicht wer sie gebaut hat. imagePullPolicy entscheidet, ob ein Image erneut gezogen wird, nicht ob dem Absender zu trauen ist. Ein kompromittiertes Registry-Credential, ein vertipptes Tag, das zufällig auf einen fremden Mirror zeigt, oder ein Base-Image, das nach dem letzten Build ausgetauscht wurde: all das landet genauso anstandslos im Cluster wie ein Release aus der eigenen Pipeline. RBAC und NetworkPolicies regeln, wer mit dem Cluster reden darf, nicht was darin laufen darf.

Die beiden letzten Posts der Serie haben zwei Teile davon einzeln behandelt: Kyverno und Policy Controller als Admission-Schicht (03.08., Admission Control im Cluster), BuildKit-Secret-Mounts, damit Zugangsdaten erst gar nicht im Image landen (14.08.). Dieser Post erklärt Kyverno/Policy-Controller-Setup nicht erneut, sondern baut die dort eingeführte ClusterPolicy direkt um eine Signatur- und Provenance-Prüfung aus: kein Nachweis, kein Deploy.

Was Signatur und Provenance jeweils absichern

Zwei unterschiedliche Fragen, zwei unterschiedliche Mechanismen.

cosign signiert den Image-Digest, nicht das Tag. Tags sind beweglich, jeder mit Push-Rechten kann latest oder v1.4 auf einen anderen Digest umbiegen, der Digest selbst nicht. Beim Keyless-Signing stellt Fulcio ein kurzlebiges Zertifikat aus, gebunden an eine OIDC-Identität wie einen bestimmten GitHub-Actions-Workflow, statt dass irgendwo ein privater Schlüssel über Monate verwaltet und rotiert werden muss. Der Signaturvorgang landet zusätzlich als Eintrag im Rekor-Transparenzlog, nachvollziehbar und unveränderlich. Das beantwortet: ist das exakt das Artefakt, das veröffentlicht wurde, und stammt die Signatur von der erwarteten Identität.

SLSA-Provenance und in-toto-Attestierungen beantworten die zweite, unabhängige Frage: wie und woraus wurde das Artefakt gebaut. Eine in-toto-Attestierung ist ein eigenes signiertes Statement mit dem Subject (dem Digest) und einem Predicate, zum Beispiel dem SLSA-Provenance-Schema, das Quell-Repo, Commit und Builder-Identität enthält. Eine gültige Signatur allein sagt nichts darüber, aus welchem Quellcode oder mit welcher Pipeline gebaut wurde, das leistet erst die Attestierung daneben.

cosign verify --certificate-identity-regexp "^https://github.com/org/repo/" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
registry.example.com/app@sha256:...
cosign verify-attestation --type slsaprovenance \
--certificate-identity-regexp "^https://github.com/org/repo/" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
registry.example.com/app@sha256:...

Die Admission-Policy, die beides durchsetzt

Die ClusterPolicy aus dem 03.08.-Post lässt sich um verifyImages erweitern. Kyverno prüft dann bei jedem Pod, ob Signatur und Attestierung zur erwarteten Identität passen, bevor der Scheduler das Objekt überhaupt sieht.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-and-attested-images
spec:
validationFailureAction: Audit
background: false
rules:
- name: verify-signature-and-provenance
match:
any:
- resources:
kinds: ["Pod"]
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/org/repo/.github/workflows/release.yml@*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
attestations:
- predicateType: https://slsa.dev/provenance/v1
attestors:
- entries:
- keyless:
subject: "https://github.com/org/repo/.github/workflows/release.yml@*"
issuer: "https://token.actions.githubusercontent.com"

Für Policy Controller (Sigstore) läuft dieselbe Prüfung über eine ClusterImagePolicy, mit authorities.keyless.identities statt attestors.keyless. Das Prinzip ist identisch: Digest, Signatur, Attestierung, alle drei müssen zusammenpassen, bevor der Pod überhaupt geplant wird.

Rollout ohne Downtime: erst Audit, dann Enforce

validationFailureAction: Audit blockiert nichts, meldet aber jeden Verstoß als PolicyReport. Vor dem Umschalten auf Enforce prüfst du, welche laufenden Images die Policy heute schon reißen würden, meist ältere Deploys, die vor der Signing-Pipeline gebaut wurden, oder Sidecars aus fremden Helm-Charts, die nie signiert waren.

kubectl get policyreport -A -o json \
| jq '.items[].results[] | select(.policy=="require-signed-and-attested-images" and .result=="fail")'

Erst wenn diese Liste leer ist oder die verbleibenden Treffer bewusst über eine Namespace-Exclude-Regel ausgenommen sind, patchst du validationFailureAction auf Enforce. Alles andere reißt beim ersten Deploy ungeplant Workloads runter, die vorher nie geprüft wurden, mitten im Tagesgeschäft.

Stolperfallen

Keyless-Verifikation braucht zum Zeitpunkt der Admission einen Weg zu Fulcio-Root und Rekor, entweder live oder über einen lokal gecachten Trust Root (TUF). In Clustern mit eingeschränktem Egress blockiert sonst jeder Deploy, sobald die Verbindung zum öffentlichen Sigstore-Instanzpaar wackelt. Das ist ein neuer Single Point of Failure, den es vor der Policy nicht gab, und er gehört ins eigene Betriebs-Monitoring.

Multi-Arch-Images brauchen Signatur und Attestierung auf dem Manifest-List-Digest zusätzlich zu den Digests je Architektur. Fehlt einer der beiden, scheitert die Verifikation nur auf einem Teil der Nodes, und der Fehler sieht dann aus wie ein Scheduling-Problem statt einer fehlenden Signatur.

Kyverno selbst (und Policy Controller ebenso) muss von der eigenen Policy ausgenommen werden, sonst blockiert ein Redeploy des Admission Controllers sich selbst, bevor er die nächste Prüfung überhaupt durchführen kann. Dasselbe gilt für System-Namespaces wie kube-system, die die eigene Registry-Signing-Pipeline nie durchlaufen haben.

Grenzen

Die Policy stellt sicher, woher ein Image kommt und dass niemand es nach dem Build verändert hat. Sie sagt nichts darüber, was in dem Image an Schwachstellen steckt, das bleibt Aufgabe der Scan-Pipeline aus den SBOM- und Trivy-Posts dieser Serie. Sie ist außerdem nur so streng wie das subject-Pattern in der Policy: ein zu weit gefasster Regex, der jeden Workflow im gesamten Org-Namespace matcht statt nur den konkreten Release-Workflow, lässt jede Pipeline in dieser Organisation eine gültige Signatur erzeugen. Und Enforce-Mode wirkt nur auf neue Deploys, was bereits im Cluster läuft, bleibt davon unberührt und braucht ein eigenes Audit.

Wie Image-Provenance und Admission Control in die Lieferketten-Perspektive einer ganzen Organisation passen, beschreibe ich ausführlicher auf digital-business.blog.

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..