Wer lokale Kubernetes-Umgebungen betreibt, kennt das Problem: Echte TLS-Zertifikate von Let's Encrypt funktionieren nur mit öffentlich erreichbaren Domains. Für lokale Tests und Entwicklungsumgebungen braucht man eine andere Lösung. Mit mkcert lässt sich eine eigene lokale CA erstellen, die der Browser als vertrauenswürdig akzeptiert und mit cert-manager kann diese CA direkt im Cluster genutzt werden, um Zertifikate automatisch auszustellen.
Voraussetzungen
- Ein laufender Kubernetes-Cluster (z. B. k3s, kind, minikube)
kubectlkonfiguriert- cert-manager im Cluster installiert
- macOS mit Homebrew (die Konzepte funktionieren aber plattformübergreifend)
Schritt 1: mkcert installieren
brew install mkcert
brew install nss # Für Firefox-Unterstützung
nss wird benötigt, damit Firefox die CA ebenfalls als vertrauenswürdig akzeptiert.
Schritt 2: Lokale CA erstellen und im System installieren
mkcert -CAROOT # Zeigt das Verzeichnis mit der CA
mkcert -install # Installiert die CA im System-Truststore
mkcert -install trägt die CA in den System-Truststore ein, sodass alle Browser
auf dem lokalen Rechner Zertifikate dieser CA ohne Warnung akzeptieren. Das
CA-Verzeichnis liegt standardmäßig unter ~/Library/Application Support/mkcert
(macOS) bzw. ~/.local/share/mkcert (Linux).
Schritt 3: CA-Zertifikat als Kubernetes Secret hinterlegen
cert-manager benötigt das CA-Zertifikat und den zugehörigen Private Key als
kubernetes.io/tls-Secret im cert-manager-Namespace:
CAROOT=$(mkcert -CAROOT)
kubectl create secret tls mkcert-ca \
--cert="$CAROOT/rootCA.pem" \
--key="$CAROOT/rootCA-key.pem" \
--namespace=cert-manager \
--dry-run=client -o yaml | kubectl apply -f -
Der --dry-run=client -o yaml-Trick erzeugt das YAML, ohne es direkt anzuwenden –
so lässt sich das Manifest erst prüfen oder per GitOps einchecken. Hier wird es
direkt über eine Pipe nach kubectl apply weitergeleitet.
Hinweis: Der Private Key der CA (
rootCA-key.pem) ist sensitiv. Für produktionsähnliche Umgebungen empfiehlt sich die Verschlüsselung mit SOPS/Age vor dem Einchecken in ein Git-Repository.
Schritt 4: ClusterIssuer anlegen
Mit dem Secret im Cluster kann jetzt ein ClusterIssuer erstellt werden, der
cert-manager mitteilt, wie Zertifikate ausgestellt werden sollen:
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: mkcert-ca-issuer
spec:
ca:
secretName: mkcert-ca
EOF
Ein ClusterIssuer ist clusterübergreifend verfügbar, im Gegensatz zu einem
Issuer, der auf einen einzelnen Namespace beschränkt ist. Das Secret muss sich
dabei immer im cert-manager-Namespace befinden.
Schritt 5: Beispiel-Deployment und Service
Zum Testen deployen wir einen einfachen nginx:
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
replicas: 1
selector:
matchLabels:
app: my-service
template:
metadata:
labels:
app: my-service
spec:
containers:
- name: my-service
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-service
ports:
- port: 80
targetPort: 80
EOF
Schritt 6: Ingress mit TLS-Annotation
Der entscheidende Teil: Im Ingress wird über die Annotation
cert-manager.io/cluster-issuer mitgeteilt, welcher Issuer für das Zertifikat
zuständig ist. cert-manager erkennt die Annotation, fordert automatisch ein
Zertifikat an und legt es im angegebenen Secret ab.
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: mkcert-ca-issuer
name: my-ingress
spec:
tls:
- hosts:
- myapp.mydomain.tld
secretName: mkcert-tls
rules:
- host: myapp.mydomain.tld
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
EOF
cert-manager erstellt kurz darauf ein Certificate-Objekt und das Secret
mkcert-tls mit dem ausgestellten Zertifikat. Der Status lässt sich so prüfen:
kubectl get certificate
kubectl describe certificate mkcert-tls
Wie es funktioniert
mkcert (lokal)
└── rootCA.pem + rootCA-key.pem
│
▼
Kubernetes Secret "mkcert-ca"
(cert-manager Namespace)
│
▼
ClusterIssuer "mkcert-ca-issuer"
│
▼ (Ingress-Annotation löst aus)
cert-manager stellt Zertifikat aus
│
▼
Secret "mkcert-tls" → Ingress → HTTPS ✓
Der Browser auf dem lokalen Rechner vertraut dem Zertifikat, weil mkcert -install
die Root-CA bereits in den System-Truststore eingetragen hat.
Fazit
Mit dieser Kombination aus mkcert und cert-manager lassen sich lokale Kubernetes-Entwicklungsumgebungen mit vollständig vertrauenswürdigen TLS-Zertifikaten ausstatten, ohne Browser-Warnungen, ohne öffentliche DNS-Einträge und ohne manuelle Zertifikatsverwaltung. cert-manager übernimmt dabei die Ausstellung und Erneuerung vollautomatisch, genau wie im Produktionsbetrieb mit Let's Encrypt.