Neuigkeiten von trion.
Immer gut informiert.

Kubernetes TLS Zertifikate

Zertifikate spielen in einem Kubernetes Cluster eine wichtige Rolle: Durch Zertifikate wird eine Vertrauensbeziehung ausgedrückt.
Vor allem bei einer statischen Beziehung zwischen Services lässt sich eine Authentifizierung durch TLS Zertifikate sehr effizient ausdrücken.

Kubernetes bringt daher auch typischerweise eine eigene Certificate Authority (CA) mit, die als Trust-Root für alle im Kubernetes Cluster verwendeten Zertifikate dienen kann.
Eine eigene CA kann, je nach verwendeten Verfahren zur Cluster Einrichtung, ebenfalls verwendet werden, oder es können intermediate-CA Zertifikate eingesetzt werden. Standard ist jedoch dass jeder Kubernetes Cluster über eine eigene CA verfügt.

Wie man mit dieser CA interagieren kann wird in diesem Beitrag erklärt.

Kubernetes TLS-Zertifikat-Anfragen erstellen

Um in Kubernetes TLS-Zertifikate auszustellen braucht es nicht viel: Ein Certificate Signing Request (CSR) und ein wenig Interaktion mit der Kubernetes API.

Als Beispiel zum Umgang mit den Zertifikaten wird für eine Docker Registry im Kubernetes Cluster ein passendes Zertifiakt erzeugt. Da die Docker Registry unter unterschiedlichen Namen verfügbar ist, benötigt das TLS-Zertifikat neben dem Common Name auch weitere Subject Alternative Name (SAN) Einträge.

TLS erlaubt sowohl IP-Adressen, als auch DNS-Namen als alternative Subjects, für die ein Zertifikat gültigt ist. Damit kann ein Zugriff sowohl über einen voll qualifizierten Namen, als auch über kurze Namen innerhalb eines Kubernetes Namespace erfolgen.

Zunächst wird ein privater Schlüssel für das Zertifikat benötigt. Mit dem Schlüssel lässt sich dann ein Certificate Signing Request (CSR) erstellen, der an Kubernetes übermittelt wird, um anschließend signiert zu werden.

Um einen neuen Schlüssel und CSR zu erstellen gibt es diverse Werkzeuge. Aktuelle Versionen von OpenSSL erlauben ohne zusätzliche Konfigurationsdateien entsprechende Zertifikatsanfragen zu generieren.

Erstellung eines neuen privaten TLS Schlüssels und Certificate Signing Request (CSR)
$ openssl genrsa -out server.key 2048 # (1)
Generating RSA private key, 2048 bit long modulus (2 primes)
............................................................+++++
.......+++++
e is 65537 (0x010001)
$ openssl req -new -key server.key -out server.csr \ #(2)
  -subj "/CN=localhost" \ #(3)
  -addext \ #(4)
  "subjectAltName=DNS:registry.docker-registry,DNS:registry.docker-registry.svc.cluster.local"
  1. Generierung eines privaten 2048 bit RSA Schlüssels

  2. Erstellung eines Certificate Signing Requests

  3. Für den Hostnamen localhost

  4. zusätzlich soll das Zertifikat für registry.docker-registry und registry.docker-registry.svc.cluster.local gültig sein

Mit dieser Zertifikatsanfrage kann Kubernetes angesprochen werden.

TLS Zertifikatanfrage in Kubernetes

Eine so erstellte Zertifikatanfrage kann an Kubernetes übermittelt werden. Dazu muss die Anfrage als base64 gekapselte Zeichenkette in dem API Aufruf eingebettet werden.

Ein Beispiel einer solchen Anfrage kann so erstellt werden:

Zertifikatanfrage an Kubernetes API
$ cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: registry.docker-registry
spec:
  groups:
  - system:authenticated
  request: $(cat server.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF
certificatesigningrequest.certificates.k8s.io/registry.docker-registry created

Ein entsprechend berechtigter Nutzer kann diese Zertifikatanfrage bestätigen und damit die Signatur der Anfrage auslösen. Kubernetes erstellt daraufhin ein von der Kubernetes Root-CA signiertes Zertifikat, das über die Kubernetes API abgerufen werden kann.

Alle ausstehenden Zertifikatanfragen können mit kubectl get csr abgerufen werden. Vergleichbar mit anderen Cluster-Resourcen gibt es keine Trennung in Namespaces für die TLS Zertifikate

Status aller CSR
$ kubectl get csr
registry.docker-registry   18s   kubernetes-admin   Pending
$ kubectl describe  csr registry.docker-registry
Name:               registry.docker-registry
Labels:             <none>
Annotations:        <none>
CreationTimestamp:  Thu, 29 Jan 2019 14:42:13 +0100
Requesting User:    kubernetes-admin
Status:             Pending
Subject:
  Common Name:    localhost
  Serial Number:
Subject Alternative Names:
         DNS Names:  registry.docker-registry
                     registry.docker-registry.svc.cluster.local
Events:  <none>

Mit kubectl certificate approve registry.docker-registry kann das Zertifikat nun ausgestellt werden. Abgerufen wird das Zertifikat wieder durch kubectl get csr - der Inhalt des Feldes status.certificate enthält das signierte TLS Zertifikat. Auch hier wird ein Base64 Encoding verwendet.

Anschließend kann das CSR Objekt durch kubectl delete csr wieder gelöscht werden.

Ausstellung des Zertifikats und Abruf
$ kubectl certificate approve registry.docker-registry
certificatesigningrequest.certificates.k8s.io/registry.docker-registry approved
$ kubectl get csr
NAME                       AGE     REQUESTOR          CONDITION
registry.docker-registry   5m33s   kubernetes-admin   Approved,Issued
$ kubectl get csr registry.docker-registry -o jsonpath='{.status.certificate}' | base64 --decode > server.crt
$ kubectl delete csr registry.docker-registry
certificatesigningrequest.certificates.k8s.io "registry.docker-registry" deleted

Um das ausgestellte Zertifikat anzusehen, kann OpenSSL verwendet werden.

Ausgabe der Zertifikat Details mit OpenSSL
$ openssl x509  -text -in server.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            22:65:46:3c:7d:75:3c:bb:35:fd:d8:d2:6f:31:4d:2b:8d:88:93:6f
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = kubernetes
        Validity
            Not Before: Jan 29 13:42:00 2019 GMT
            Not After : Jan 29 13:42:00 2020 GMT
        Subject: CN = localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:db:33:...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                28:42:05:D9:60:0C:BE:BE:73:B5:A1:A3:9B:E5:58:BA:0C:28:85:C0
            X509v3 Subject Alternative Name:
                DNS:registry.docker-registry, DNS:registry.docker-registry.svc.cluster.local
    Signature Algorithm: sha256WithRSAEncryption
         18:1d:...
-----BEGIN CERTIFICATE-----
MIIDX...
-----END CERTIFICATE-----

Das Zertifikat kann nun verwendet werden, um den Dienst registry.docker-registry auszuweisen. Da Credentials aus Sicherheitsgründen nicht fest in einem Container Image eingebaut werden sollten, wird einer Anwendung in Kubernetes das Zertifikat am besten als Secret zur Verfügung gestellt. Kubernetes verfügt sogar über einen speziellen Secret-Typ für TLS Zertifikate.

TLS Zertifikate als Kubernetes Secret

Um TLS Zertifikate für ein Deployment oder Pod durch Kubernetes bereitzustellen, wird das vorher erzeugte Secret analog zu anderen Secrets deklariert und für die relevanten Container bereitgestellt.
Einzig gilt es zu beachten, dass die Dateinamen dabei vorbelegt sind: Für das Zertifikat wird tls.crt und für den Secret-Key wird tls.key als Dateiname verwendet.

Zur Erzeugung des Secret wird statt des häufig anzutreffenden generic Typ für das create secret Kommando dann tls verwendet.

Erzeugung Kubernetes TLS Secret aus Zertifikat und privatem Schlüssel
$ kubectl create secret tls --cert=certs/server.crt --key=certs/server.key registry-tls

Die im Namespace vorhandenen Secrets können zur Überprüfung abgefragt werden. Bei TLS Zertifikaten ist der Typ dann als kubernetes.io/tls angegeben.
Soll ein Secret genauer inspiziert werden, kann kubectl describe secret mit dem Namen des Secrets verwendet werden.

Abfrage von TLS Secrets und Details
$ kubectl get secret
NAME                  TYPE                                  DATA   AGE
registry-tls          kubernetes.io/tls                     2      5m

$ kubectl describe secret registry-tls
Name:         registry-tls
Namespace:    docker-registry
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/tls

Data
====
tls.crt:  1229 bytes
tls.key:  1675 bytes

Um das TLS Zertifikat verwenden zu können, muss das Secret im Pod deklariert werden. Secrets werden als Volumes angegeben und bei den jeweiligen Containern als Volume-Mount eingebunden.

Verwendung von TLS Secret in einem Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
...
  template:
    metadata:
    spec:
      containers:
        image: ...
        name: ...
        volumeMounts:
        - mountPath: /tls
          name: tls
          readOnly: true
      ...
      volumes:
      - name: tls
        secret:
          defaultMode: 420
          secretName: registry-tls

Fazit

Mit den gezeigten Werkzeugen kann Kubernetes verwendet werden, um im Cluster als vertrauenswürdig eingestufte TLS Zertifikate auszustellen und für Container bereitzustellen.

Typische Anwendungsfälle für TLS Zertifikate sind die Authentifizierung im Machine-to-machine Kontext, vor allem bei Infrastrukturkomponenten. Kubernetes bringt die erforderlichen APIs mit, um darauf aufbauend Prozesse oder organisatorische Vorgaben zu implementieren.




Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels