Gültige SSL/TLS-Zertifikate sind eine Kernanforderung der modernen Anwendungslandschaft. Leider wird die Verwaltung von Zertifikatserneuerungen (oder Zertifikatserneuerungen) bei der Bereitstellung einer Anwendung häufig vernachlässigt. Zertifikate haben eine begrenzte Lebensdauer, die von etwa 13 Monaten für Zertifikate von DigiCert bis zu 90 Tagen für Let’s Encrypt-Zertifikate reicht. Um den sicheren Zugriff aufrechtzuerhalten, müssen diese Zertifikate vor ihrem Ablauf erneuert/neu ausgestellt werden. Angesichts der erheblichen Arbeitsbelastung der meisten Ops-Teams kommt es vor, dass die Erneuerung von Zertifikaten vergessen wird. Dies führt zu Chaos, wenn das Ablaufdatum der Zertifikate näher rückt – oder, schlimmer noch, es überschritten wird.
So muss es nicht sein. Mit etwas Planung und Vorbereitung kann die Zertifikatsverwaltung automatisiert und optimiert werden. Hier sehen wir uns eine Lösung für Kubernetes an, die drei Technologien verwendet:
In diesem Blog erfahren Sie, wie Sie die Zertifikatsverwaltung vereinfachen, indem Sie Ihren Endpunkten eindeutige, automatisch erneuerte und aktualisierte Zertifikate bereitstellen.
Bevor wir in die technischen Details einsteigen, müssen wir einige Begriffe definieren. Der Begriff „TLS-Zertifikat“ bezieht sich auf zwei Komponenten, die zum Aktivieren von HTTPS-Verbindungen auf unserem Ingress-Controller erforderlich sind:
Sowohl das Zertifikat als auch der private Schlüssel werden von Let’s Encrypt ausgestellt. Eine vollständige Erklärung zur Funktionsweise von TLS-Zertifikaten finden Sie im Beitrag „So funktionieren TLS/SSL-Zertifikate“ von DigiCert.
In Kubernetes werden diese beiden Komponenten als Secrets gespeichert. Kubernetes-Workloads – wie der NGINX Ingress Controller und der Cert-Manager – können diese Secrets schreiben und lesen, die auch von Benutzern verwaltet werden können, die Zugriff auf die Kubernetes-Installation haben.
Das Cert-Manager -Projekt ist ein Zertifikatscontroller, der mit Kubernetes und OpenShift funktioniert. Bei der Bereitstellung in Kubernetes stellt der Cert-Manager automatisch die von Ingress-Controllern benötigten Zertifikate aus und stellt sicher, dass diese gültig und aktuell sind. Darüber hinaus werden Ablaufdaten von Zertifikaten verfolgt und in konfigurierten Zeitabständen eine Erneuerung versucht. Obwohl es mit zahlreichen öffentlichen und privaten Emittenten zusammenarbeitet, zeigen wir die Integration mit Let’s Encrypt.
Bei der Verwendung von Let’s Encrypt wird die gesamte Zertifikatsverwaltung automatisch durchgeführt. Dies ist zwar sehr praktisch, bringt aber auch ein Problem mit sich: Wie stellt der Dienst sicher, dass Sie der Eigentümer des betreffenden vollqualifizierten Domänennamens (FQDN) sind?
Dieses Problem wird mithilfe einer Challenge gelöst. Dabei müssen Sie eine Bestätigungsanfrage beantworten, die nur von jemandem bereitgestellt werden kann, der Zugriff auf die DNS-Einträge der jeweiligen Domäne hat. Herausforderungen können eine von zwei Formen haben:
HTTP-01 ist die einfachste Möglichkeit, ein Zertifikat zu generieren, da kein direkter Zugriff auf den DNS-Anbieter erforderlich ist. Diese Art der Abfrage wird immer über Port 80 (HTTP) durchgeführt. Beachten Sie, dass der Cert-Manager bei der Verwendung von HTTP-01-Challenges den Ingress-Controller nutzt, um das Challenge-Token bereitzustellen.
Ein Ingress-Controller ist ein spezialisierter Dienst für Kubernetes, der Datenverkehr von außerhalb des Clusters bringt, ihn auf interne Pods (eine Gruppe aus einem oder mehreren Containern) verteilt und den ausgehenden Datenverkehr verwaltet. Darüber hinaus wird der Ingress-Controller über die Kubernetes-API gesteuert und überwacht und aktualisiert die Lastausgleichskonfiguration, wenn Pods hinzugefügt oder entfernt werden oder ausfallen.
Weitere Informationen zu Ingress-Controllern finden Sie in den folgenden Blogs:
In den folgenden Beispielen verwenden wir den NGINX Ingress Controller, der von F5 NGINX entwickelt und gepflegt wird.
Diese Beispiele setzen voraus, dass Sie über eine funktionierende Kubernetes-Installation verfügen, mit der Sie Tests durchführen können, und dass die Installation eine externe IP-Adresse zuweisen kann (Kubernetes LoadBalancer-Objekt). Darüber hinaus wird davon ausgegangen, dass Sie Datenverkehr sowohl auf Port 80 als auch auf Port 443 (bei Verwendung der HTTP-01-Challenge) oder nur auf Port 443 (bei Verwendung der DNS-01-Challenge) empfangen können. Diese Beispiele werden anhand von Mac OS X veranschaulicht, können aber auch unter Linux oder WSL verwendet werden.
Sie benötigen außerdem einen DNS-Anbieter und einen FQDN, für den Sie den A-Eintrag anpassen können. Wenn Sie die HTTP-01-Challenge verwenden, müssen Sie nur einen A-Eintrag hinzufügen können (oder einen für Sie hinzufügen lassen). Wenn Sie die DNS-01-Challenge verwenden, benötigen Sie API-Zugriff auf einen unterstützten DNS-Anbieter oder einen unterstützten Webhook-Anbieter .
Am einfachsten geht das Deployment über Helm . Diese Bereitstellung ermöglicht Ihnen die Verwendung sowohl des Kubernetes Ingress als auch des NGINX Virtual Server CRD.
$ helm repo add nginx-stable https://helm.nginx.com/stable "nginx-stable" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
NAME: nginx-kic
LAST DEPLOYED: Thu Sep 1 15:58:15 2022
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.
$ kubectl get deployments --namespace nginx-ingress NAME READY UP-TO-DATE AVAILABLE AGE
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-kic-nginx-ingress LoadBalancer 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30s
Der Vorgang hängt hierbei von Ihrem DNS-Anbieter ab. Dieser DNS-Name muss von den Let‘s Encrypt-Servern auflösbar sein. Daher kann es erforderlich sein, dass Sie warten, bis der Datensatz verbreitet wurde, bevor es funktioniert. Weitere Informationen hierzu finden Sie im SiteGround-Artikel „Was ist DNS-Verbreitung und warum dauert sie so lange?“
Sobald Sie Ihren gewählten FQDN auflösen können, können Sie mit dem nächsten Schritt fortfahren.
$ host cert.example.com cert.example.com has address www.xxx.yyy.zzz
Der nächste Schritt besteht darin, die neueste Version des Cert-Managers bereitzustellen. Für unsere Bereitstellung werden wir wieder Helm verwenden.
$ helm repo add jetstack https://charts.jetstack.io "jetstack" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \
--version v1.9.1 --set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Thu Sep 1 16:01:52 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deployments --namespace cert-manager NAME READY UP-TO-DATE AVAILABLE AGE
cert-manager 1/1 1 1 4m30s
cert-manager-cainjector 1/1 1 1 4m30s
cert-manager-webhook 1/1 1 1 4m30s
Wir werden das NGINX Cafe-Beispiel verwenden, um unsere Backend-Bereitstellung und -Dienste bereitzustellen. Dies ist ein gängiges Beispiel, das in der von NGINX bereitgestellten Dokumentation verwendet wird. Wir werden Ingress in diesem Zusammenhang nicht bereitstellen.
$ git clone https://github.com/nginxinc/kubernetes-ingress.git Cloning into 'kubernetes-ingress'...
remote: Enumerating objects: 44979, done.
remote: Counting objects: 100% (172/172), done.
remote: Compressing objects: 100% (108/108), done.
remote: Total 44979 (delta 87), reused 120 (delta 63), pack-reused 44807
Receiving objects: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, done.
Resolving deltas: 100% (26508/26508), done.
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created
„kubectl
get“. Sie möchten sicherstellen, dass die Pods als BEREIT
und die Dienste als ausgeführt
angezeigt werden. Das folgende Beispiel zeigt eine repräsentative Auswahl dessen, was Sie suchen. Beachten Sie, dass der Kubernetes
-Dienst ein Systemdienst ist, der im selben Namespace (Standard) ausgeführt wird wie das NGINX Cafe-Beispiel.$ kubectl get deployments,services --namespace default NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coffee 2/2 2 2 69s
deployment.apps/tea 3/3 3 3 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coffee-svc ClusterIP 10.128.154.225 <none> 80/TCP 68s
service/kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 29m
service/tea-svc ClusterIP 10.128.96.145 <none> 80/TCP 68s
Innerhalb des Cert-Managers kann der ClusterIssuer zum Ausstellen von Zertifikaten verwendet werden. Dies ist ein Cluster-Objekt, auf das von jedem Namespace verwiesen werden kann und das von allen Zertifikatsanforderungen mit der definierten Zertifikatsausstellenden Stelle verwendet werden kann. In diesem Beispiel können alle Zertifikatsanforderungen für Let’s Encrypt-Zertifikate von diesem ClusterIssuer bearbeitet werden.
Stellen Sie den ClusterIssuer für den von Ihnen ausgewählten Challenge-Typ bereit. Obwohl es den Rahmen dieses Beitrags sprengt, gibt es erweiterte Konfigurationsoptionen , mit denen Sie mehrere Resolver (ausgewählt basierend auf Auswahlfeldern) in Ihrem ClusterIssuer angeben können.
Mithilfe des ACME-Protokolls (Automated Certificate Management Environment) wird ermittelt, ob Sie Eigentümer eines Domänennamens sind und Ihnen daher ein Let’s Encrypt-Zertifikat ausgestellt werden kann. Für diese Herausforderung müssen folgende Parameter übergeben werden:
Dieses Beispiel zeigt, wie ein ClusterIssuer eingerichtet wird, um mit der HTTP-01-Challenge den Domänenbesitz nachzuweisen und ein Zertifikat zu erhalten.
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- http01:
ingress:
class: nginx
EOF
clusterissuer.cert-manager.io/prod-issuer created
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 34s
Dieses Beispiel zeigt, wie Sie einen ClusterIssuer einrichten, um die DNS-01-Challenge zur Authentifizierung Ihres Domänenbesitzes zu verwenden. Abhängig von Ihrem DNS-Anbieter müssen Sie wahrscheinlich ein Kubernetes-Geheimnis verwenden, um Ihr Token zu speichern. In diesem Beispiel wird Cloudflare verwendet. Beachten Sie die Verwendung von Namespaces. Die Cert-Manager-Anwendung, die im Cert-Manager-Namespace bereitgestellt wird, muss Zugriff auf das Geheimnis haben.
Für dieses Beispiel benötigen Sie ein Cloudflare-API-Token , das Sie von Ihrem Konto aus erstellen können. Dies muss in die Zeile weiter unten eingefügt werden. Wenn Sie Cloudflare nicht verwenden, müssen Sie die Dokumentation Ihres Anbieters befolgen .
$ cat << EOF | kubectl apply -f apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <API Token>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
EOF
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 31m
Hier setzen wir den Schwerpunkt – wir stellen die Ingress-Ressource für unsere Anwendung bereit. So leiten wir den Datenverkehr an die zuvor bereitgestellte NGINX Cafe-Anwendung weiter.
Wenn Sie die Standard-Kubernetes-Ingress-Ressource nutzen, konfigurieren Sie den Ingress mit der folgenden Bereitstellungs-YAML und fordern Sie ein Zertifikat an.
apiVersion: networking.k8s.io/v1 kind: Ingress
metadata:
name: cafe-ingress
annotations:
cert-manager.io/cluster-issuer: prod-issuer
acme.cert-manager.io/http01-edit-in-place: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- cert.example.com
secretName: cafe-secret
rules:
- host: cert.example.com
http:
paths:
- path: /tea
pathType: Prefix
backend:
service:
name: tea-svc
port:
number: 80
- path: /coffee
pathType: Prefix
backend:
service:
name: coffee-svc
port:
number: 80
Es lohnt sich, einige wichtige Teile des Manifests noch einmal durchzugehen:
metadata.annotations
, wo wir acme.cert-manager.io/http01-edit-in-place
auf „true“ setzen. Dieser Wert ist erforderlich und passt die Art und Weise an, in der die Herausforderung bedient wird. Weitere Informationen finden Sie im Dokument „Unterstützte Anmerkungen“ . Dies kann auch durch die Verwendung eines Master/Minion-Setups bewältigt werden.spec.ingressClassName
bezieht sich auf den NGINX-Ingress-Controller, den wir installiert haben und verwenden werden.spec.tls.secret
speichert den Schlüssel des digitalen Zertifikats, den Sie erhalten, wenn Let’s Encrypt das Zertifikat ausstellt. cert.example.com
ist für spec.tls.hosts
und spec.rules.host
angegeben. Dies ist der Hostname, für den unser ClusterIssuer das Zertifikat ausgestellt hat.spec.rules.http
definiert die Pfade und die Backend-Dienste, die Anfragen auf diesen Pfaden verarbeiten. Beispielsweise wird der Datenverkehr zu /tea
an Port 80 des tea-svc
umgeleitet.spec.rules.host
und spec.tls.hosts
, Sie sollten jedoch alle Parameter in der Konfiguration überprüfen. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe created
$ kubectl get certificates NAME READY SECRET AGE
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
Wenn Sie die NGINX-CRDs verwenden, müssen Sie zum Konfigurieren Ihres Ingress das folgende Bereitstellungs-YAML verwenden.
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: cafe
spec:
host: cert.example.com
tls:
secret: cafe-secret
cert-manager:
cluster-issuer: prod-issuer
upstreams:
- name: tea
service: tea-svc
port: 80
- name: coffee
service: coffee-svc
port: 80
routes:
- path: /tea
action:
pass: tea
- path: /coffee
action:
pass: coffee
Es lohnt sich, einige wichtige Teile des Manifests noch einmal durchzugehen:
spec.tls.secret
speichert den Schlüssel des digitalen Zertifikats, den Sie erhalten, wenn Let’s Encrypt das Zertifikat ausstellt. cert.example.com
ist für spec.host
angegeben. Dies ist der Hostname, für den unser ClusterIssuer das Zertifikat ausgestellt hat.spec.upstreams
-Werte verweisen auf unsere Backend-Dienste, einschließlich der Ports.spec.routes
definieren sowohl die Route als auch die Aktion, die ausgeführt werden soll, wenn diese Routen erreicht werden.spec.host
-Werts, Sie sollten jedoch alle Parameter in der Konfiguration überprüfen. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe created
$ kubectl get VirtualServers NAME STATE HOST IP PORTS AGE
cafe Valid cert.example.com www.xxx.yyy.zzz [80,443] 51m
Sie können das Zertifikat über die Kubernetes-API anzeigen. Dort werden Ihnen Details zum Zertifikat angezeigt, einschließlich seiner Größe und des zugehörigen privaten Schlüssels.
$ kubectl describe secret cafe-secret Name: cafe-secret
Namespace: default
Labels: <none>
Annotations: cert-manager.io/alt-names: cert.example.com
cert-manager.io/certificate-name: cafe-secret
cert-manager.io/common-name: cert.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group:
cert-manager.io/issuer-kind: ClusterIssuer
cert-manager.io/issuer-name: prod-issuer
cert-manager.io/uri-sans:Type: kubernetes.io/tlsData
====
tls.crt: 5607 bytes
tls.key: 1675 bytes
Wenn Sie das tatsächliche Zertifikat und den Schlüssel sehen möchten, können Sie dies tun, indem Sie den folgenden Befehl ausführen. (Notiz: Dies veranschaulicht eine Schwäche der Kubernetes Secrets. Sie können nämlich von jedem gelesen werden, der über die erforderlichen Zugriffsberechtigungen verfügt.)
$ kubectl get secret cafe-secret -o yaml
Testen Sie die Zertifikate. Sie können hier jede gewünschte Methode verwenden. Das folgende Beispiel verwendet cURL . Der Erfolg wird durch einen Block ähnlich dem zuvor gezeigten angezeigt, der den Servernamen, die interne Adresse des Servers, das Datum, die gewählte URI (Route) (Kaffee oder Tee) und die Anforderungs-ID enthält. Fehler werden in Form von HTTP-Fehlercodes angezeigt, höchstwahrscheinlich 400 oder 301.
$ curl https://cert.example.com/tea
Server address: 10.2.0.6:8080
Server name: tea-5c457db9-l4pvq
Date: 02/Sep/2022:15:21:06 +0000
URI: /tea
Request ID: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
Server address: 10.2.2.6:8080
Server name: coffee-7c86d7d67c-kjddk
Date: 02/Sep/2022:15:21:10 +0000
URI: /coffee
Request ID: 4ea3aa1c87d2f1d80a706dde91f31d54
Wir haben zu Beginn versprochen, dass mit diesem Ansatz die Notwendigkeit entfallen würde, Zertifikatserneuerungen zu verwalten. Wir müssen jedoch noch erklären, wie das geht. Warum? Weil dies ein zentraler, integrierter Bestandteil des Cert-Managers ist. Wenn cert-manager in diesem automatischen Prozess feststellt, dass ein Zertifikat nicht vorhanden ist, abgelaufen ist, innerhalb von 15 Tagen abläuft oder wenn der Benutzer über die CLI ein neues Zertifikat anfordert, wird automatisch ein neues Zertifikat angefordert . Viel einfacher geht es nicht.
Wenn Sie ein NGINX Plus-Abonnent sind, besteht der einzige Unterschied für Sie darin, den NGINX Ingress Controller zu installieren. Anweisungen zum Ändern des oben angegebenen Helm-Befehls, um dies zu erreichen, finden Sie im Abschnitt „Installation Helm“ der NGINX-Dokumente.
Dies hängt weitgehend von Ihrem Anwendungsfall ab.
Die HTTP-01-Challenge-Methode erfordert, dass Port 80 für das Internet geöffnet ist und dass der DNS-A-Eintrag ordnungsgemäß für die IP-Adresse des Ingress-Controllers konfiguriert wurde. Dieser Ansatz erfordert außer zum Erstellen des A-Eintrags keinen Zugriff auf den DNS-Anbieter.
Die DNS-01-Challenge-Methode kann verwendet werden, wenn Sie Port 80 nicht dem Internet zugänglich machen können. Sie erfordert lediglich, dass der Zertifikatsmanager über ausgehenden Zugriff auf den DNS-Anbieter verfügt. Allerdings erfordert diese Methode, dass Sie Zugriff auf die API Ihres DNS-Anbieters haben, wobei die erforderliche Zugriffsebene je nach Anbieter unterschiedlich ist.
Aufgrund der enormen Komplexität von Kubernetes ist es schwierig, gezielte Informationen zur Fehlerbehebung bereitzustellen. Wenn Sie auf Probleme stoßen, laden wir Sie ein, uns im NGINX Community Slack zu fragen (NGINX Plus-Abonnenten können ihre normalen Supportoptionen nutzen).
Fordern Sie zunächst Ihre kostenlose 30-Tage-Testversion von NGINX Ingress Controller mit NGINX App Protect WAF und DoS an und laden Sie das stets kostenlose NGINX Service Mesh herunter .
„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."