Verschlüsseln wir SSL-Zertifikate mit dem Cert-Manager in Kubernetes



In diesem Artikel werde ich darüber sprechen, wie die Bestellung und Erneuerung von Zertifikaten von Let's Encrypt (und nicht nur) für Ingress in Kubernetes mithilfe des Add-On-Zertifikatsmanagers automatisiert werden kann. Aber ich beginne mit einer kurzen Einführung in das Wesentliche des Problems.

Ein bisschen Bildungsprogramm


Das HTTP-Protokoll, das in den frühen 90er Jahren des letzten Jahrhunderts entwickelt wurde, ist so eng in unseren Alltag eingedrungen, dass man sich kaum einen Tag ohne Verwendung vorstellen kann. An sich bietet es jedoch nicht einmal ein Mindestmaß an Sicherheit beim Austausch von Informationen zwischen einem Benutzer und einem Webserver. HTTPS („S“ - sicher) hilft dabei: Durch die Verpackung der übertragenen Daten in SSL / TLS hat sich dieses Protokoll beim Schutz von Informationen vor dem Abfangen bewährt und wird von der Industrie aktiv gefördert.

Zum Beispiel hat Google an 2014 festgehaltenPosition "HTTPS überall" und senkt sogar die Priorität von Websites ohne diese in den Suchergebnissen. Diese „Propaganda“ umgeht auch nicht die normalen Verbraucher: Moderne Browser warnen ihre Benutzer vor dem Vorhandensein und der Richtigkeit von SSL-Zertifikaten für besuchte Websites.





Die Kosten für ein Zertifikat für eine persönliche Website beginnen bei mehreren zehn Dollar. Es ist nicht immer gerechtfertigt und angemessen, es zu kaufen. Glücklicherweise ist seit Ende 2015 eine kostenlose Alternative in Form von Let's Encrypt (LE) -Zertifikaten verfügbar . Dieses gemeinnützige Projekt wurde von Mozilla-Enthusiasten erstellt, um die meisten Websites verschlüsselt abzudecken.

Die Zertifizierungsstelle stellt domänenvalidierte Zertifikate aus(das einfachste unter den auf dem Markt erhältlichen) mit einer Gültigkeitsdauer von 90 Tagen, und seit vielen Jahren ist es möglich, das sogenannte Wildcard-Zertifikat für mehrere Subdomains auszustellen.

Um das Zertifikat zu erhalten, verwendet die Site die Algorithmen, die im ACME- Protokoll ( Automated Certificate Management Environment ) beschrieben sind, das speziell für Let's Encrypt erstellt wurde. Bei Verwendung erfolgt die Bestätigung des Domänenbesitzes durch Anforderungen durch Platzierung eines bestimmten HTTP-Codes ( HTTP-01 ) oder Installation von DNS-Einträgen (DNS-01). Weitere Informationen hierzu finden Sie weiter unten.

Zertifizierungsmanager


Cert-Manager ist ein spezielles Projekt für Kubernetes, bei dem es sich um eine Reihe von CustomResourceDefinitions (daher die Beschränkung der unterstützten Mindestversion von K8s - v1.12) für die CA-Konfiguration (Zertifizierungsstellen) und die direkte Bestellung von Zertifikaten handelt. Die Installation von CRD in einem Cluster ist trivial und erfordert die Verwendung einer YAML-Datei:

kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml

( Es besteht auch die Möglichkeit der Installation mit Helm.)

Um den Bestellvorgang zu starten, müssen im Cluster die Ressourcen der Zertifizierungsstellen (CAs) deklariert sein: Issueroder ClusterIssuer, - die zum Signieren der CSR (Certificate Issuance Requests) verwendet werden. Der Unterschied zwischen der ersten und der zweiten Ressource liegt im Umfang:

  • Issuer kann im selben Namespace verwendet werden,
  • ClusterIssuer ist ein globales Clusterobjekt.

Übe mit dem Cert-Manager


Nr. 1. Selbstsigniertes Zertifikat


Beginnen wir mit dem einfachsten Fall - der Bestellung eines selbstsignierten Zertifikats. Diese Option ist beispielsweise in dynamischen Testumgebungen für Entwickler oder bei Verwendung eines externen Balancers, der den SSL-Verkehr beendet, weit verbreitet.

Die Ressource Issuersieht folgendermaßen aus:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

Um ein Zertifikat ausstellen zu können, muss die Ressource beschrieben werden Certificate, wo angegeben wird, wie sie ausgestellt werden soll (siehe Abschnitt issuerRefunten) und wo sich der private Schlüssel (Feld secretName) befindet. Danach muss sich Ingress auf diesen Schlüssel beziehen (siehe Abschnitt tlsc spec):

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: selfsigned
  commonName: "yet-another.website"
  dnsNames:
  - "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

Einige Sekunden nach dem Hinzufügen dieser Ressourcen zum Cluster wird das Zertifikat ausgestellt. Sie können den entsprechenden Bericht in der Ausgabe des Befehls sehen:

kubectl -n app describe  certificate selfsigned-crt
...
  Normal  GeneratedKey  5s    cert-manager  Generated a new private key
  Normal  Requested     5s    cert-manager  Created new CertificateRequest resource "selfsigned-crt-4198958557"
  Normal  Issued        5s    cert-manager  Certificate issued successfully

Wenn Sie sich die geheime Ressource selbst ansehen, enthält sie:

  • privater Schlüssel tls.key,
  • Stammzertifikat ca.crt,
  • Unser selbstsigniertes Zertifikat tls.crt.

Der Inhalt dieser Dateien kann beispielsweise mit dem Dienstprogramm opensslwie folgt angezeigt werden:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website

Es ist anzumerken, dass das damit ausgestellte Zertifikat im allgemeinen Fall von denIssuer verbundenen Clients nicht als vertrauenswürdig eingestuft wird . Der Grund ist einfach: Es gibt keine Zertifizierungsstelle (siehe Hinweis auf der Projektwebsite ) . Um dies zu vermeiden, müssen Sie im CertificatePfad zu der geheimen Datei angeben, in der sie enthalten ist ca.crt. Dies kann eine Unternehmens-CA-Organisation sein, um die für Ingress ausgestellten Zertifikate mit einem Schlüssel zu signieren, der bereits für die Anforderungen anderer Serverdienste / Informationssysteme verwendet wird.

Nr. 2. Verschlüsseln wir das Zertifikat mit der HTTP-Validierung


Wie bereits erwähnt, stehen für die Ausstellung von LE-Zertifikaten zwei Arten der Bestätigung des Domänenbesitzes zur Verfügung : HTTP-01 und DNS-01.

Der erste Ansatz (HTTP-01) besteht darin, einen kleinen Webserver mit einer separaten Bereitstellung zu starten, der über den Link <YOUR_DOMAIN > / an das Internet gesendet wird . Bekannte / acme-Challenge / <TOKEN> Einige vom Zertifizierungsserver angeforderte Daten. Daher impliziert diese Methode die Verfügbarkeit von Ingress von außen an Port 80 und die Auflösung des DNS-Eintrags der Domäne in öffentliche IP-Adressen.

Die zweite Option zum Überprüfen des ausgestellten Zertifikats (DNS-01) stammt vonvon einer API bis zum DNS-Server, auf dem die Domäneneinträge gehostet werden. Der Aussteller erstellt mit den angegebenen Token TXT-Datensätze in der Domäne, die der ACME-Server dann während der Bestätigung erhält. Zu den offiziell unterstützten DNS-Anbietern zählen CloudFlare, AWS Route53, Google CloudDNS und andere, einschließlich der eigenen Implementierung ( acme-dns ).

Hinweis : Let's Encrypt hat recht strenge Beschränkungen für Anforderungen an ACME-Server. Um nicht in ein langes Verbot zu geraten, wird empfohlen, zum Debuggen den Zertifikatstyp letsencrypt-staging zu verwenden (der Unterschied besteht nur beim ACME-Server).

Also beschreiben wir die Ressourcen:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - yet-another.website

Beachten Sie, dass die Adresse des Staging- Servers als servery acme(c Issuer) angegeben ist . Ersetzen Sie es durch eine Schlacht wird später möglich sein. Mit dieser Konfiguration verfolgen wir den gesamten Bestellpfad:



  1. Die Schöpfung Certificatebrachte eine neue Ressource hervor CertificateRequest:

    kubectl -n app describe certificate le-crt
    ...
    Created new CertificateRequest resource "le-crt-1127528680"
  2. In seiner Beschreibung - ein Zeichen auf der Schöpfung Order:

    kubectl -n app describe certificaterequests le-crt-1127528680
    Created Order resource app/le-crt-1127528680-1805948596
  3. Es Orderbeschreibt, mit welchen Parametern der Test besteht und welchen aktuellen Status er hat. Eine solche Überprüfung wird von der Ressource durchgeführt Challenge:

    kubectl -n app describe order le-crt-1127528680-1805948596
    Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
  4. Schließlich enthalten die Details dieser Ressource Informationen zum Status des Scans selbst:

    kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
    ...
      Reason:      Successfully authorized domain                                                                                                                                                                      
    ...
      Normal  Started         2m45s  cert-manager  Challenge scheduled for processing
      Normal  Presented       2m45s  cert-manager  Presented challenge using http-01 challenge mechanism
      Normal  DomainVerified  2m22s  cert-manager  Domain "yet-another.website" verified with "http-01" validation

Wenn alle Bedingungen erfüllt sind (d. H. Die Domain ist von außen zugänglich, es gibt kein Verbot der LE ...) - in weniger als einer Minute wird das Zertifikat ausgestellt. Bei Erfolg wird ein describe certificate le-crtDatensatz in der Ausgabe angezeigt Certificate issued successfully.

Jetzt können Sie die Serveradresse sicher in Combat ( https://acme-v02.api.letsencrypt.org/directory) ändern und die bereits signierten echten Zertifikate nicht neu bestellen Fake LE Intermediate X1, sondern Let's Encrypt Authority X3.

Dazu müssen Sie zunächst die Ressource löschen. CertificateAndernfalls werden keine Bestellverfahren aktiviert, da das Zertifikat bereits vorhanden und relevant ist. Das Entfernen eines Geheimnisses führt zu seiner sofortigen Rückkehr mit einer Nachricht an describe certificate:

  Normal  PrivateKeyLost  44s                   cert-manager  Lost private key for CertificateRequest "le-crt-613810377", deleting old resource

Es bleibt, das "Kampf" -Manifest für Issuerdas bereits oben beschriebene anzuwenden Certificate(es hat sich nicht geändert):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx

Nach dem Empfang der Nachricht Certificate issued successfully, describeüberprüfen Sie es:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

Nummer 3. Platzhalter LE mit DNS-Validierung


Wir werden die Aufgabe komplizieren, indem wir sofort ein Zertifikat an alle Subdomains der Site schreiben und diesmal die DNS-Prüfung (über CloudFlare) verwenden.

Rufen Sie zunächst das Token in der CloudFlare-Systemsteuerung ab, um die API zu bearbeiten:

  1. Profil → API-Token → Token erstellen.
  2. Legen Sie die Berechtigungen wie folgt fest:
    • Berechtigungen:
      • Zone - DNS - Bearbeiten
      • Zone - Zone - Lesen
    • Zonenressourcen:
      • Einschließen - Alle Zonen
  3. Kopieren Sie den nach dem Speichern erhaltenen Token (zum Beispiel :) y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M.

Erstellen Sie ein Geheimnis, in dem dieses Token gespeichert wird, und verweisen Sie im Aussteller darauf:

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
type: Opaque
stringData:
  api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - "yet-another.website"
  - "*.yet-another.website"

(Vergessen Sie nicht, Staging zu verwenden, wenn Sie experimentieren!)

Führen Sie den Vorgang zum Bestätigen des Domainbesitzes durch:

kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
  State:       pending
Events:
  Type    Reason     Age   From          Message
  ----    ------     ----  ----          -------
  Normal  Started    54s   cert-manager  Challenge scheduled for processing
  Normal  Presented  53s   cert-manager  Presented challenge using dns-01 challenge mechanism

Ein TXT-Datensatz wird im Bedienfeld angezeigt:



... und nach einer Weile ändert sich der Status in:

Domain "yet-another.website" verified with "dns-01" validation

Stellen Sie sicher, dass das Zertifikat für jede Subdomain gültig ist:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
          DNS:*.yet-another.website, DNS:yet-another.website

Die Validierung durch DNS erfolgt in der Regel nicht schnell, da die meisten DNS-Anbieter über einen Datenaktualisierungszeitraum verfügen, der angibt, wie viel Zeit von der Änderung des DNS-Eintrags bis zur tatsächlichen Aktualisierung aller DNS-Server des Anbieters vergeht. Der ACME-Standard bietet jedoch auch eine Kombination aus zwei Überprüfungsoptionen, mit denen der Empfang eines Zertifikats für die Hauptdomäne beschleunigt werden kann. In diesem Fall Issuerlautet die Beschreibung wie folgt:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - selector:
        dnsNames:
        - "*.yet-another.website"
      dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token            
    - selector:
        dnsNames:
        - "yet-another.website"
      http01:
        ingress:
          class: nginx

Wenn Sie diese Konfiguration anwenden, werden zwei Ressourcen erstellt Challenge:

kubectl -n app describe orders le-crt-613810377-1285319347
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"                 
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"

Nummer 4. Verwenden von Ingress-Spezialanmerkungen


Zusätzlich zum direkten Pfad zum Erstellen von Zertifikaten in cert-manager ist es möglich, eine Komponente namens ingress-shim zu verwenden und Ressourcen nicht explizit zu erstellen Certificate. Die Idee ist, dass mit Hilfe spezieller Ingress-Anmerkungen das Zertifikat automatisch unter Verwendung des darin angegebenen Zertifikats bestellt wird Issuer. Das Ergebnis ist ungefähr die folgende Ingress-Ressource:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

Für einen korrekten Betrieb reicht hier nur die Anwesenheit des Emittenten aus, dh es wird eine Entität weniger erstellt.

Darüber hinaus gibt es eine veraltete Annotation kube-lego - kubernetes.io/tls-acme: "true", -, die eine IssuerStandardaufgabe erfordert, wenn cert-manager mithilfe von Helm-Parametern (oder in den Startoptionen des Manager-Containers) installiert wird.

Wir im Unternehmen verwenden diese Optionen nicht und können sie aufgrund der Undurchsichtigkeit der Ansätze zur Bestellung von SSL-Zertifikaten (und gleichzeitig aufgrund verschiedener auftretender Probleme ) nicht empfehlen. Wir haben uns jedoch entschlossen, sie im Artikel zu erwähnen, um ein vollständigeres Bild zu erhalten.

Anstelle einer Schlussfolgerung


Durch einfache Manipulationen mit CRD haben wir gelernt, wie automatisch erneuerbare, selbstsignierte und kostenlose SSL-Zertifikate aus dem Let's Encrypt-Projekt für Website-Domänen geschrieben werden, die im Rahmen von Ingresss in Kubernetes-Clustern gestartet wurden.

Der Artikel enthält Beispiele zur Lösung der häufigsten Probleme in unserer Praxis. Die Cert-Manager-Funktionen sind jedoch nicht auf die oben beschriebenen Funktionen beschränkt. Auf der Utility-Website finden Sie Beispiele für die Arbeit mit anderen Diensten - beispielsweise ein Bundle mit Vault oder die Verwendung externer Ausgabezentren (Emittenten).

PS


Lesen Sie auch in unserem Blog:


All Articles