- Authors

- Name
- Phillip Pham
- @ddppham
GitOps Security fuer Kubernetes: Supply-Chain absichern mit ArgoCD, OPA und Cosign
TL;DR
- GitOps verlagert die Sicherheitsgrenze vom Cluster zum Git-Repository. Wer das Repo kontrolliert, kontrolliert die Infrastruktur.
- Pull-basierte Deployments (ArgoCD, Flux) reduzieren die Angriffsflaeche gegenueber Push-basierten CI/CD-Pipelines erheblich.
- Drei Sicherheitsebenen sind noetig: Image Signing (Cosign), Policy Enforcement (OPA/Kyverno) und Drift Detection (ArgoCD Auto-Sync).
- Secrets gehoeren nie in Git. Sealed Secrets oder External Secrets Operator sind Pflicht.
- Ein Break-Glass-Verfahren fuer Notfaelle muss dokumentiert und getestet sein, bevor selfHeal aktiviert wird.
Warum GitOps die Sicherheit veraendert
In einer klassischen CI/CD-Pipeline hat die Pipeline Schreibzugriff auf den Cluster. Jenkins, GitLab CI oder GitHub Actions fuehren kubectl apply aus und brauchen dafuer ein Service-Account-Token mit weitreichenden Rechten. Das Token liegt als Secret in der CI-Umgebung. Wird die CI kompromittiert, ist der Cluster offen.
GitOps dreht dieses Modell um. Der GitOps-Operator (ArgoCD oder Flux) laeuft im Cluster und pullt Aenderungen aus einem Git-Repository. Die CI-Pipeline hat keinen Cluster-Zugriff mehr. Sie baut nur noch Images, pusht sie in eine Registry und aktualisiert die Manifeste im Git-Repo. Der Cluster holt sich die Aenderungen selbst.
Dieses Pull-Modell hat drei Sicherheitsvorteile:
- Keine Cluster-Credentials in CI. Kein Token, das gestohlen werden kann.
- Git als Audit-Log. Jede Aenderung hat einen Commit mit Autor, Timestamp und Review-History.
- Drift Detection. Der Operator erkennt, wenn jemand manuell etwas im Cluster aendert, und kann es automatisch zuruecksetzen.
Aber GitOps allein ist kein Sicherheitskonzept. Es verschiebt nur die Angriffsflaeche. Jetzt muss man das Git-Repository, die Image-Supply-Chain und die Policy-Enforcement-Schicht absichern. Und genau darum geht es in diesem Artikel.
Sicherheitsebene 1: Image Signing mit Cosign
Container-Images sind die Grundlage jedes Deployments. Wenn ein Angreifer ein manipuliertes Image in die Registry schiebt, wird es vom Operator ohne Pruefung deployed. Image Signing verhindert das.
Cosign (Teil des Sigstore-Projekts) signiert Images kryptografisch. Die Signatur wird in der Registry neben dem Image gespeichert (OCI-Artifact). Beim Deployment prueft ein Admission Controller (Kyverno oder Connaisseur) die Signatur und lehnt unsignierte Images ab.
# Image bauen und pushen
docker build -t registry.example.com/myapp:v1.2.3 .
docker push registry.example.com/myapp:v1.2.3
# Image signieren (OIDC-basiert, keyless)
cosign sign registry.example.com/myapp:v1.2.3
# Signatur verifizieren
cosign verify registry.example.com/myapp:v1.2.3 \
--certificate-identity=ci@example.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
Im keyless-Modus nutzt Cosign kurzlebige Zertifikate von Fulcio, die an eine OIDC-Identitaet (z.B. GitHub Actions Workflow) gebunden sind. Kein langlebiger Signing-Key, der kompromittiert werden kann.
Keyless Signing ist die empfohlene Variante. Bei Key-basiertem Signing muss der private Key sicher gespeichert und rotiert werden -- ein zusaetzlicher operativer Aufwand, der oft unterschaetzt wird. Mit OIDC ist die Identitaet des Signierers an den CI-Workflow gebunden, nicht an einen statischen Key.
Sicherheitsebene 2: Policy Enforcement mit OPA Gatekeeper
OPA Gatekeeper laeuft als Admission Controller im Cluster. Jede Kubernetes-Resource, die erstellt oder geaendert wird, durchlaeuft zuerst die Policy Engine. Gatekeeper prueft gegen Rego-Policies und lehnt non-konforme Ressourcen ab.
Fuer GitOps-Szenarien sind folgende Policies essentiell:
# ConstraintTemplate: Images muessen aus erlaubter Registry kommen
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
spec:
crd:
spec:
names:
kind: K8sAllowedRepos
validation:
openAPIV3Schema:
type: object
properties:
repos:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedrepos
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not startswith(container.image, input.parameters.repos[_])
msg := sprintf("Image '%v' kommt nicht aus einer erlaubten Registry", [container.image])
}
---
# Constraint: Nur Images aus unserer privaten Registry erlauben
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: require-private-registry
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "production"
- "staging"
parameters:
repos:
- "registry.example.com/"
Beachten Sie die Namespace-Einschraenkung im Constraint. In Entwicklungsumgebungen wollen Sie moeglicherweise flexibler sein, waehrend Production und Staging strikt kontrolliert werden.
Weitere sinnvolle Policies:
| Policy | Zweck | Auswirkung |
|---|---|---|
Keine latest-Tags | Erzwingt explizite Versionen | Reproduzierbare Deployments |
| Keine privilegierten Container | Blockiert securityContext.privileged: true | Reduziert Container-Escape-Risiko |
| Resource Limits Pflicht | Erzwingt CPU/Memory Limits | Verhindert Noisy-Neighbor-Probleme |
| Nur signierte Images | Prueft Cosign-Signatur | Supply-Chain-Schutz |
| ReadOnly Root Filesystem | Erzwingt readOnlyRootFilesystem: true | Erschwert Persistence nach Compromise |
| Keine hostNetwork/hostPID | Blockiert Host-Namespace-Zugriff | Verhindert Node-Escape |
Ein Tipp zur Einfuehrung: Starten Sie mit Gatekeeper im dryrun-Enforcement-Modus. So sehen Sie, welche bestehenden Ressourcen gegen die Policies verstossen, ohne sie zu blockieren. Erst wenn alle Violations behoben sind, schalten Sie auf deny um.
Sicherheitsebene 3: Drift Detection mit ArgoCD
ArgoCD synchronisiert den Cluster-Zustand mit dem Git-Repository. Wenn jemand manuell kubectl edit ausfuehrt, erkennt ArgoCD die Abweichung (Drift) und kann sie automatisch korrigieren.
Die Konfiguration dafuer ist einfach:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/infra/k8s-manifests.git
targetRevision: main
path: apps/myapp
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Loescht Ressourcen, die nicht mehr in Git sind
selfHeal: true # Korrigiert manuellen Drift automatisch
syncOptions:
- CreateNamespace=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
selfHeal: true ist die entscheidende Einstellung. Ohne sie erkennt ArgoCD zwar Drift, korrigiert ihn aber nicht. Mit selfHeal wird jede manuelle Aenderung innerhalb von Sekunden zurueckgesetzt.
Das retry-Block ist ebenfalls wichtig. Temporaere Fehler (z.B. ein kurzfristiger API-Server-Timeout) sollen nicht sofort zu einem Failed-Sync fuehren. Drei Retries mit exponentiellem Backoff fangen die meisten transienten Probleme ab.
Das hat Implikationen: Kein Entwickler und kein Admin sollte noch manuell kubectl apply gegen Produktion ausfuehren. Alle Aenderungen gehen ueber Git. Das ist ein kultureller Wandel, der klar kommuniziert werden muss.
Break-Glass-Verfahren fuer Notfaelle
SelfHeal verhindert manuelle Aenderungen -- das ist im Normalbetrieb gewuenscht. Aber es gibt Situationen, in denen ein Admin direkt eingreifen muss: ein Zero-Day-Exploit, ein kaskadierender Ausfall, ein fehlerhafter Sync.
Dafuer brauchen Sie ein Break-Glass-Verfahren:
- Dedizierter Break-Glass-ServiceAccount mit Admin-Rechten, dessen Token verschluesselt in einem Vault liegt.
- ArgoCD Application temporaer auf Manual Sync setzen:
kubectl patch application myapp -n argocd --type merge -p '{"spec":{"syncPolicy":null}}' - Manuelle Aenderung durchfuehren und dokumentieren.
- Aenderung in Git nachziehen und ArgoCD wieder auf Auto-Sync setzen.
- Post-Mortem mit dem Team: Warum war Break-Glass noetig? Kann das Szenario kuenftig durch ein Playbook abgedeckt werden?
Testen Sie dieses Verfahren regelmaessig. Ein Break-Glass-Prozess, der im Notfall zum ersten Mal ausgefuehrt wird, scheitert mit hoher Wahrscheinlichkeit.
Secrets-Management: Was nicht in Git darf
Das groesste Anti-Pattern in GitOps: Kubernetes Secrets im Klartext ins Git-Repository committen. Auch base64-Encoding (wie in Kubernetes Secrets) ist kein Schutz -- das ist Encoding, keine Verschluesselung.
Zwei bewaehrte Loesungen:
Sealed Secrets: Der Controller laeuft im Cluster und besitzt einen privaten Schluessel. Secrets werden mit dem oeffentlichen Schluessel verschluesselt und koennen sicher in Git gespeichert werden. Nur der Controller kann sie entschluesseln.
External Secrets Operator: Secrets liegen in einem externen Vault (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault). Der Operator synchronisiert sie in Kubernetes Secrets. In Git steht nur eine Referenz auf den Vault-Pfad.
| Ansatz | Vorteile | Nachteile |
|---|---|---|
| Sealed Secrets | Einfach, keine externe Abhaengigkeit | Key-Rotation erfordert Re-Encryption aller Secrets |
| External Secrets + Vault | Zentrale Verwaltung, Rotation, Audit | Zusaetzliche Infrastruktur (Vault) |
| SOPS + Age/GPG | Git-nativer Workflow | Manuelle Key-Verteilung, keine zentrale Rotation |
Fuer die meisten Teams empfehle ich den External Secrets Operator mit einem Vault-Backend. Die zentrale Verwaltung und automatische Rotation ueberwiegen den Mehraufwand bei der Einrichtung. Sealed Secrets ist der bessere Einstieg fuer kleine Teams, die noch keinen Vault betreiben.
Git-Repository absichern
Da das Git-Repository die Single Source of Truth ist, muss es entsprechend geschuetzt werden:
- Branch Protection Rules: Keine direkten Pushes auf
main. Nur Merges ueber Pull Requests mit mindestens einem Review. - CODEOWNERS: Aenderungen an sicherheitskritischen Pfaden (z.B.
policies/,rbac/,network-policies/) erfordern Review vom Security-Team. - Signed Commits: GPG- oder SSH-signierte Commits stellen sicher, dass Commits tatsaechlich vom angegebenen Autor stammen. ArgoCD kann so konfiguriert werden, dass es nur signierte Commits synchronisiert.
- MFA fuer alle Accounts: Jeder Nutzer mit Zugang zum Infra-Repository braucht aktivierte Zwei-Faktor-Authentifizierung.
- Audit-Logs aktivieren: GitHub, GitLab und Bitbucket bieten Audit-Logs fuer Repository-Zugriffe. Diese sollten an ein SIEM weitergeleitet werden.
- Pre-Commit-Hooks: Lokale Checks, die verhindern, dass Secrets, Credentials oder andere sensitive Daten versehentlich committet werden (z.B. mit detect-secrets oder gitleaks).
Vergleich: ArgoCD vs. Flux CD
Beide Tools implementieren das GitOps-Prinzip, unterscheiden sich aber in Philosophie und Architektur:
| Aspekt | ArgoCD | Flux CD |
|---|---|---|
| UI | Web-UI mit Visualisierung | CLI-only (Weave GitOps als optionale UI) |
| Multi-Tenancy | AppProjects mit RBAC | Namespace-basierte Isolation |
| Helm-Support | Nativer Helm-Renderer | Helm Controller (separate Komponente) |
| Notifications | Integriert (ArgoCD Notifications) | Notification Controller |
| RBAC | Feingranular (SSO-Integration) | Kubernetes-natives RBAC |
| Resource-Verbrauch | Hoeher (Web-UI, API-Server) | Niedriger (kein zentraler Server) |
| Image-Update-Automation | Ueber Argo Image Updater | Nativ (Image Reflector + Automation) |
| Commit-Signatur-Pruefung | Ja (GPG) | Ja (GPG, Cosign) |
ArgoCD eignet sich besser fuer Teams, die eine visuelle Uebersicht wollen und Multi-Tenancy brauchen. Die Web-UI macht es einfach, den Sync-Status aller Applikationen auf einen Blick zu erfassen und Probleme zu debuggen.
Flux ist leichtgewichtiger und fuegt sich natuerlicher in Kubernetes ein, da es auf Custom Resources statt einer zentralen UI setzt. Es eignet sich besser fuer Setups, in denen viele Cluster zentral verwaltet werden (Fleet Management).
CI-Pipeline: Security Gates integrieren
Die GitOps-Pipeline hat zwei Haelften: die CI-Seite (Build, Scan, Sign) und die CD-Seite (ArgoCD/Flux). Auf der CI-Seite sollten folgende Security Gates implementiert sein:
# Beispiel: GitLab CI Pipeline mit Security Gates
stages:
- build
- scan
- sign
- update-manifests
build:
stage: build
script:
- docker build -t $REGISTRY/$IMAGE:$CI_COMMIT_SHA .
- docker push $REGISTRY/$IMAGE:$CI_COMMIT_SHA
scan:
stage: scan
script:
- trivy image --exit-code 1 --severity CRITICAL $REGISTRY/$IMAGE:$CI_COMMIT_SHA
- trivy image --format sarif --output trivy-report.sarif $REGISTRY/$IMAGE:$CI_COMMIT_SHA
artifacts:
reports:
sast: trivy-report.sarif
sign:
stage: sign
script:
- cosign sign $REGISTRY/$IMAGE:$CI_COMMIT_SHA
only:
- main
update-manifests:
stage: update-manifests
script:
- git clone $MANIFESTS_REPO
- cd k8s-manifests
- kustomize edit set image $IMAGE=$REGISTRY/$IMAGE:$CI_COMMIT_SHA
- git commit -am "Update $IMAGE to $CI_COMMIT_SHA"
- git push
only:
- main
Der entscheidende Punkt: Die CI-Pipeline hat Schreibzugriff auf das Manifests-Repository, aber keinen Zugriff auf den Kubernetes-Cluster. Das Manifests-Repository ist der einzige Kommunikationskanal zwischen CI und Cluster.
Praktische Checkliste
Bevor Sie GitOps in Produktion nutzen, sollten folgende Punkte abgehakt sein:
- Branch Protection auf
mainaktiviert (kein direct push) - CODEOWNERS-Datei fuer sicherheitskritische Pfade eingerichtet
- Pre-Commit-Hooks fuer Secret-Detection konfiguriert
- Image Signing in der CI-Pipeline konfiguriert (Cosign)
- Image-Scanning mit Blockierung bei CRITICAL CVEs
- Admission Controller deployed (OPA Gatekeeper oder Kyverno)
- Policy: Nur signierte Images aus erlaubten Registries
- Policy: Keine privilegierten Container, keine Host-Mounts
- Secrets-Management eingerichtet (Sealed Secrets oder External Secrets)
- ArgoCD/Flux mit selfHeal und Prune konfiguriert
- Audit-Logging fuer Git-Repository und Cluster aktiviert
- Break-Glass-Verfahren dokumentiert und getestet
- Monitoring fuer Sync-Status und Policy-Violations eingerichtet
Weitergehende Themen
- ArgoCD GitOps Tutorial -- Schritt-fuer-Schritt-Einstieg in ArgoCD
- Kubernetes RBAC fuer Enterprises -- Zugriffsrechte im Cluster granular steuern
- Network Policies fuer Fortgeschrittene -- Segmentierung als zweite Verteidigungslinie
- Security Scanning in der CI/CD-Pipeline -- Shift-Left fuer Container-Images
- Kubernetes API Security -- API-Server absichern und ueberwachen
Sie planen die Einfuehrung von GitOps und wollen sichergehen, dass Ihre Pipelines von Anfang an sicher sind? Sprechen Sie uns an unter /kontakt.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
GitOps Secrets: SOPS und Sealed Secrets im Vergleich
Secrets sicher in Git verwalten mit Sealed Secrets, Mozilla SOPS und External Secrets Operator. Praxisvergleich für GitOps-Workflows.
ArgoCD ApplicationSets: Multi-App Deployment Patterns
ArgoCD ApplicationSets automatisieren Multi-App und Multi-Cluster Deployments mit Generatoren. Praxis-Patterns für Git, Cluster und Matrix.
GitOps Repository-Struktur: Best Practices
Die richtige Repository-Struktur entscheidet über den Erfolg von GitOps. Mono-Repo vs. Multi-Repo, Verzeichnisaufbau und Environment-Promotion praxisnah erklärt.
Multi-Cluster Management: Kubernetes-Flotten steuern
Mehrere Kubernetes-Cluster zentral verwalten mit ArgoCD ApplicationSets und Rancher Fleet. Topologien, Drift-Erkennung und praxisnahe YAML-Beispiele.
ArgoCD installieren und erstes Projekt deployen
ArgoCD auf Kubernetes installieren und das erste Projekt Schritt für Schritt deployen, von der CLI-Einrichtung bis zur automatischen Synchronisation.