6.7 KiB
05 — cert-manager
Datum: 2026-03-17 Version: cert-manager v1.20.0 (Helm Chart v1.20.0) Namespace: cert-manager
Übersicht
cert-manager automatisiert die Ausstellung und Erneuerung von TLS-Zertifikaten in Kubernetes. Es integriert sich mit Let's Encrypt via ACME und nutzt Traefik als Ingress-Controller für die HTTP-01-Challenge.
Installation
1. Jetstack Helm Repository hinzufügen
helm repo add jetstack https://charts.jetstack.io
helm repo update
Ausgabe:
"jetstack" has been added to your repositories
...Successfully got an update from the "jetstack" chart repository
2. Namespace erstellen
kubectl create namespace cert-manager
3. cert-manager per Helm installieren (mit CRDs)
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.20.0 \
--set crds.enabled=true \
--wait \
--timeout 5m
Ausgabe:
NAME: cert-manager
LAST DEPLOYED: Tue Mar 17 09:38:11 2026
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
cert-manager v1.20.0 has been deployed successfully!
Pod-Status
kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-cainjector-68c64dbb9b-xvbcr 1/1 Running 0 ...
cert-manager-f5cd6c77c-lfpg7 1/1 Running 0 ...
cert-manager-webhook-54d5d87669-2pgsj 1/1 Running 0 ...
Alle 3 Pods laufen stabil.
DNS-Problem: ISP-Wildcard-Interception
Symptom
Die ClusterIssuers zeigten nach der Erstellung dauerhaft Ready: False:
Failed to register ACME account: Get "https://acme-v02.api.letsencrypt.org/directory":
remote error: tls: unrecognized name
Ursache
In der Netplan-Konfiguration aller Nodes war die Search-Domain int.befast.at
statisch eingetragen. Der kubelet kopiert diese aus dem Host-/etc/resolv.conf
direkt in alle Pods:
# /etc/resolv.conf auf dem Host (systemd-resolved) — vorher
nameserver 127.0.0.53
search int.befast.at
Pods erhielten dadurch folgendes /etc/resolv.conf:
search cert-manager.svc.cluster.local svc.cluster.local cluster.local int.befast.at
nameserver 10.43.0.10
options ndots:5
Mit ndots:5 wird acme-v02.api.letsencrypt.org (3 Punkte) zuerst als
acme-v02.api.letsencrypt.org.int.befast.at aufgelöst — und der ISP-DNS
(84.191.81.126) antwortete mit einem Wildcard-Eintrag, dessen TLS-Zertifikat
den SNI-Namen acme-v02.api.letsencrypt.org nicht enthält.
Lösung: Search-Domain aus Netplan entfernen (alle 3 Nodes)
Die search:-Sektion wurde aus /etc/netplan/99-br0.yaml auf allen Nodes
entfernt. Backups liegen als /etc/netplan/99-br0.yaml.bak auf jedem Node.
rnk-cp01 (192.168.11.170):
sudo cp /etc/netplan/99-br0.yaml /etc/netplan/99-br0.yaml.bak
# search: / - int.befast.at Zeilen entfernt
sudo netplan apply
rnk-wrk01 + rnk-wrk02 (via SSH):
for IP in 192.168.11.171 192.168.11.172; do
ssh mtkadmin@$IP "
sudo cp /etc/netplan/99-br0.yaml /etc/netplan/99-br0.yaml.bak
sudo sed -i '/^ search:/,/^ - int.befast.at/d' /etc/netplan/99-br0.yaml
sudo netplan apply
"
done
/etc/netplan/99-br0.yaml nach der Änderung (Beispiel rnk-cp01):
network:
version: 2
ethernets:
enx1065308999be:
dhcp4: no
dhcp6: no
bridges:
br0:
interfaces: [enx1065308999be]
addresses:
- "192.168.11.170/24"
nameservers:
addresses:
- 192.168.11.1
routes:
- to: "default"
via: "192.168.11.1"
parameters:
stp: false
forward-delay: 0
Ergebnis
# /etc/resolv.conf auf allen Hosts — nachher
nameserver 127.0.0.53
search .
Pods erhalten jetzt saubere DNS-Konfiguration ohne ISP-Domain:
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.43.0.10
options ndots:5
Überprüfung DNS-Auflösung aus einem Pod:
kubectl run dns-test --image=busybox:1.28 --restart=Never -- sleep 30
kubectl exec dns-test -- nslookup acme-v02.api.letsencrypt.org
# Name: acme-v02.api.letsencrypt.org → 172.65.32.248 ✓
kubectl delete pod dns-test --grace-period=0
Hinweis: Zusätzlich wurde CoreDNS auf
forward . 8.8.8.8 8.8.4.4 1.1.1.1konfiguriert (stattforward . /etc/resolv.conf), damit keine zukünftigen Host-DNS-Änderungen auf Pods durchschlagen.
ClusterIssuer-Konfiguration
Manifest: /home/mtkadmin/homelab/cert-manager-issuers.yaml
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: homelab@befast.at
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
ingressClassName: traefik
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-production
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: homelab@befast.at
privateKeySecretRef:
name: letsencrypt-production-account-key
solvers:
- http01:
ingress:
ingressClassName: traefik
kubectl apply -f /home/mtkadmin/homelab/cert-manager-issuers.yaml
Status
kubectl get clusterissuer -o wide
NAME READY STATUS
letsencrypt-production True The ACME account was registered with the ACME server
letsencrypt-staging True The ACME account was registered with the ACME server
Verwendung: Zertifikat für Ingress
Staging zuerst testen (kein Rate-Limit)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: traefik
tls:
- hosts:
- my-app.example.com
secretName: my-app-tls-staging
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-svc
port:
number: 80
Auf Production wechseln
Annotation ändern auf:
cert-manager.io/cluster-issuer: letsencrypt-production
Helm Release Info
NAME NAMESPACE REVISION STATUS CHART APP VERSION
cert-manager cert-manager 1 deployed cert-manager-v1.20.0 v1.20.0
Nächste Schritte
- Ersten echten Ingress mit TLS-Zertifikat testen (Staging)
- DNS-Eintrag für öffentliche Domain auf Cluster-IP setzen
- Nach erfolgreichem Staging-Test auf Production-Issuer umstellen
- Zertifikatserneuerung überwachen (
kubectl get certificate -A)