Veröffentlicht am

Kubernetes PKI: cert-manager und mTLS einrichten

Teilen:
Authors

Kubernetes PKI und Zertifikatsmanagement in der Praxis

TL;DR

  • Jeder Kubernetes-Cluster hat eine eingebaute PKI: die Cluster-CA signiert Zertifikate fuer API-Server, kubelet, etcd und Service Accounts.
  • cert-manager automatisiert den gesamten Lebenszyklus von X.509-Zertifikaten -- Ausstellung, Erneuerung, Revokation -- als native Kubernetes-Ressource.
  • Fuer interne Services ist eine private CA (z.B. via HashiCorp Vault) besser geeignet als Let's Encrypt. Fuer oeffentliche Endpoints ist ACME/Let's Encrypt der Standard.
  • mTLS zwischen Services eliminiert eine ganze Klasse von Angriffen (MITM, Spoofing). cert-manager plus ein Service Mesh wie Istio oder Linkerd macht das praktikabel.
  • Automatisiertes Zertifikatsmanagement spart nicht nur Zeit, sondern verhindert die haeufigste Ursache fuer Produktionsausfaelle: abgelaufene Zertifikate.

Kubernetes-interne PKI: Was der Cluster mitbringt

Bevor wir ueber cert-manager sprechen, lohnt sich ein Blick auf die PKI, die Kubernetes selbst mitbringt. Bei der Cluster-Installation (via kubeadm, RKE2, k3s etc.) wird automatisch eine Certificate Authority erstellt. Diese Cluster-CA signiert Zertifikate fuer:

KomponenteZertifikatZweck
kube-apiserverapiserver.crtTLS fuer die API, Client-Auth gegenueber etcd
kubeletkubelet.crtAuthentifizierung gegenueber dem API-Server
etcdetcd/server.crtTLS fuer die etcd-Kommunikation
front-proxyfront-proxy-client.crtAggregation Layer (Metrics Server etc.)
Service Accountsa.key / sa.pubToken-Signierung fuer ServiceAccounts

Diese Zertifikate haben standardmaessig eine Laufzeit von einem Jahr. kubeadm erneuert sie beim Cluster-Upgrade automatisch -- aber nur dann. Wer selten upgradet, riskiert abgelaufene Zertifikate und damit einen toten Cluster.

# Laufzeiten aller Cluster-Zertifikate pruefen (kubeadm)
kubeadm certs check-expiration

# Alle Zertifikate manuell erneuern
kubeadm certs renew all

# Einzelnes Zertifikat inspizieren
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates -subject

Das ist die Basis. Fuer Applikations-Zertifikate (Ingress TLS, mTLS zwischen Pods, Webhook-Zertifikate) brauchen wir cert-manager.

cert-manager: Der Standard fuer Kubernetes-Zertifikate

cert-manager ist ein CNCF-Projekt und der De-facto-Standard fuer Zertifikatsmanagement in Kubernetes. Es fuehrt Custom Resources ein (Certificate, Issuer, ClusterIssuer, CertificateRequest), die den gesamten Lebenszyklus abbilden.

Installation

# cert-manager via Helm installieren
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.14.4 \
  --set installCRDs=true \
  --set prometheus.enabled=true

# Installation verifizieren
kubectl get pods -n cert-manager
kubectl get crds | grep cert-manager

Die Option prometheus.enabled=true exponiert Metriken, mit denen sich Zertifikatsablaeufe und Fehler ueberwachen lassen. Mehr zum Thema Monitoring unter Kubernetes Monitoring mit Open Source.

Issuer-Typen: Welche CA fuer welchen Zweck?

Issuer-TypAnwendungsfallVorteilNachteil
ACME (Let's Encrypt)Oeffentliche Ingress-EndpointsKostenlos, automatisiert, trustedNur fuer oeffentliche Domains
CA (self-signed)Interne Services, Dev/StagingEinfach, keine externe AbhaengigkeitNicht oeffentlich vertrauenswuerdig
Vault PKIInterne ProduktionVolle Kontrolle, HSM-Support, AuditVault-Betrieb erforderlich
VenafiEnterprise, regulierte UmgebungenPolicy Engine, ComplianceLizenzkosten

Beispiel 1: Let's Encrypt fuer oeffentliche Endpoints

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: platform-team@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - http01:
        ingress:
          ingressClassName: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-public-tls
  namespace: production
spec:
  secretName: api-public-tls
  duration: 2160h    # 90 Tage
  renewBefore: 720h  # 30 Tage vor Ablauf erneuern
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - api.example.com
  - www.example.com

cert-manager erstellt automatisch eine ACME-Challenge, validiert die Domain und speichert das Zertifikat als Kubernetes Secret. 30 Tage vor Ablauf wird automatisch erneuert.

Beispiel 2: Private CA fuer interne mTLS

Fuer die Kommunikation zwischen Microservices innerhalb des Clusters ist eine private CA die bessere Wahl. Kein externer Netzwerkzugriff noetig, volle Kontrolle ueber Laufzeiten und Policies.

# Schritt 1: Self-Signed Issuer fuer die Root-CA
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
# Schritt 2: Root-CA-Zertifikat erstellen
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-root-ca
  namespace: cert-manager
spec:
  isCA: true
  commonName: internal-root-ca
  duration: 87600h  # 10 Jahre
  secretName: internal-root-ca-secret
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
---
# Schritt 3: ClusterIssuer basierend auf der Root-CA
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: internal-ca-issuer
spec:
  ca:
    secretName: internal-root-ca-secret
---
# Schritt 4: Zertifikat fuer einen internen Service
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: payment-service-tls
  namespace: backend
spec:
  secretName: payment-service-tls
  duration: 2160h    # 90 Tage
  renewBefore: 360h  # 15 Tage vor Ablauf
  isCA: false
  usages:
  - server auth
  - client auth
  dnsNames:
  - payment-service.backend.svc.cluster.local
  - payment-service.backend.svc
  - payment-service
  issuerRef:
    name: internal-ca-issuer
    kind: ClusterIssuer

Das resultierende Secret payment-service-tls enthaelt tls.crt, tls.key und ca.crt. Die Applikation (oder ein Sidecar-Proxy) mountet dieses Secret und nutzt es fuer mTLS.

mTLS in der Praxis

Mutual TLS bedeutet: Beide Seiten einer Verbindung authentifizieren sich mit Zertifikaten. Nicht nur der Server beweist seine Identitaet (wie bei normalem HTTPS), sondern auch der Client. Das eliminiert:

  • Man-in-the-Middle-Angriffe: Ein Angreifer kann sich nicht zwischen zwei Services schalten, da er kein gueltiges Client-Zertifikat besitzt.
  • Service Spoofing: Ein kompromittierter Pod kann sich nicht als anderer Service ausgeben.
  • Unverschluesselte interne Kommunikation: "Wir sind ja im internen Netz" ist kein Sicherheitskonzept.

Es gibt zwei Wege, mTLS umzusetzen:

Variante 1: Application-Level mTLS. Die Applikation selbst laedt die Zertifikate und konfiguriert TLS. Funktioniert, bedeutet aber Aufwand in jeder Sprache und jedem Framework.

Variante 2: Service Mesh (Istio / Linkerd). Ein Sidecar-Proxy (Envoy bei Istio) uebernimmt mTLS transparent. Die Applikation kommuniziert weiterhin per HTTP auf localhost, der Proxy verschluesselt automatisch. Fuer Details zu Service Meshes siehe Istio vs. Linkerd Vergleich.

Monitoring: Abgelaufene Zertifikate verhindern

Der haeufigste Grund fuer zertifikatsbedingte Ausfaelle: Niemand hat bemerkt, dass ein Zertifikat bald ablaeuft. cert-manager exponiert Prometheus-Metriken, die das verhindern.

Wichtige Metriken:

  • certmanager_certificate_expiration_timestamp_seconds -- Ablaufzeitpunkt jedes Zertifikats
  • certmanager_certificate_ready_status -- Ist das Zertifikat aktuell gueltig?
  • certmanager_certificate_renewal_timestamp_seconds -- Wann wird die naechste Erneuerung getriggert?

Eine Alerting-Regel fuer Prometheus:

groups:
- name: cert-manager-alerts
  rules:
  - alert: CertificateExpiringSoon
    expr: |
      (certmanager_certificate_expiration_timestamp_seconds - time()) < 7 * 24 * 3600
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "Zertifikat {{ $labels.name }} laeuft in weniger als 7 Tagen ab"
      description: "Namespace: {{ $labels.namespace }}, Secret: {{ $labels.name }}"
  - alert: CertificateNotReady
    expr: certmanager_certificate_ready_status{condition="True"} == 0
    for: 15m
    labels:
      severity: critical
    annotations:
      summary: "Zertifikat {{ $labels.name }} ist nicht im Ready-Status"

CA-Hierarchie fuer regulierte Umgebungen

In Umgebungen mit erhoehten Compliance-Anforderungen (DSGVO, BSI-Grundschutz, ISO 27001) reicht eine flache CA-Struktur nicht aus. Eine zweistufige Hierarchie ist Standard:

Root CA (offline): Wird nur zum Signieren von Intermediate CAs verwendet. Der private Schluessel liegt in einem HSM oder auf einem Air-Gapped-System. Laufzeit: 10-20 Jahre.

Intermediate CA (online): Signiert die eigentlichen Workload-Zertifikate. Laeuft in HashiCorp Vault mit PKI Secrets Engine. Laufzeit: 1-3 Jahre.

Die Vorteile:

  • Kompromittierung der Intermediate CA erfordert nicht den Austausch der Root CA
  • Root-CA-Schluessel ist nie einem Netzwerk ausgesetzt
  • Verschiedene Intermediate CAs fuer verschiedene Umgebungen (Dev, Staging, Prod)

Fuer die Integration von Vault als Issuer in cert-manager gibt es einen dedizierten Vault-Issuer-Typ. Details zur Absicherung der gesamten Kubernetes-Infrastruktur finden Sie unter Kubernetes Security Hardening.

Haeufige Fehler und wie man sie vermeidet

1. Zertifikats-Laufzeiten nicht anpassen. Die Standardwerte von cert-manager sind konservativ. Fuer interne Services sind kuerzere Laufzeiten (24h-72h) mit automatischer Erneuerung sicherer als 90-Tage-Zertifikate.

2. renewBefore zu knapp setzen. Wenn die Erneuerung fehlschlaegt (z.B. weil Vault kurzzeitig nicht erreichbar ist), braucht cert-manager Puffer. Mindestens 30 % der Laufzeit als renewBefore einplanen.

3. CA-Secret nicht schuetzen. Das Secret der CA enthaelt den privaten Schluessel. Zugriff per RBAC auf den Namespace cert-manager strikt einschraenken. Besser: Vault als externen Key Store verwenden.

4. Kein Monitoring. Ohne Alerting auf certmanager_certificate_ready_status und Ablaufzeiten fliegen Sie blind. Siehe den Monitoring-Abschnitt oben.

5. DNS-Namen vergessen. Wenn ein Service unter payment-service, payment-service.backend.svc und payment-service.backend.svc.cluster.local erreichbar ist, muessen alle drei als dnsNames im Certificate stehen. Sonst schlaegt TLS-Validation fehl.

Checkliste: Zertifikatsmanagement produktionsreif machen

  • cert-manager installiert und Prometheus-Metriken aktiviert
  • ClusterIssuer fuer interne CA und (falls noetig) ACME konfiguriert
  • Alle Ingress-Endpoints mit TLS-Zertifikaten versehen
  • mTLS zwischen kritischen Services aktiviert (direkt oder via Service Mesh)
  • Alerting-Regeln fuer ablaufende und nicht-bereite Zertifikate eingerichtet
  • RBAC: Zugriff auf CA-Secrets und cert-manager-Namespace eingeschraenkt
  • Backup der CA-Schluessel (verschluesselt, extern)
  • Revokationsprozess dokumentiert und getestet
  • Cluster-interne Zertifikate (kubeadm) regelmaessig geprueft
  • Disaster Recovery: CA-Wiederherstellung getestet

Weitergehende Themen


Zertifikatsmanagement richtig aufzusetzen ist kein Wochenprojekt -- aber eines, das sich sofort auszahlt. Wenn Sie Unterstuetzung beim Design Ihrer CA-Hierarchie, der cert-manager-Konfiguration oder der Einfuehrung von mTLS brauchen, melden Sie sich 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