Transport-Layer Verschlüsselung per Secure Sockets Layer (SSL) bzw. Transport Layer Security (TLS) ist ‘state of the art’. Kaum eine Webseite kommt mehr ohne Verschlüsselung auf Transportprotokoll-Ebene aus.

Zum Glück ist es seit es Let’s Encrypt gibt, kaum mehr ein Problem, an ein gültiges TLS-Zertifikat zu kommen, dass von einer Zertifizierungstelle signiert wurde, die die meisten Browser und Betriebssysteme als vertrauenwürdig einstufen.

Mit cert-manager gibt es eine komfortable Lösung zur Verwaltung von SSL-Zertifikaten im Kubernetes-Cluster.

cert-manager

cert-manager ist eine Komponente, um das Management von TLS Zertifikaten zu automatisieren. Dazu nutzt cert-manager das Protokoll ACME (Automatic Certificate Management Environment), das vor allem im Zusammenhang mit Let’s Encrypt bekannt sein dürfte. Neben Let’s Encrypt werden aber auch viele weitere Zertifikatstellen oder sichere Ablagemöglichkeiten für Geheimnisse z.B. Hashicorp Vault unterstützt.

cert-manager prüft regelmäßig ob Zertifikate gültig und aktuell sind und erneuert sie rechtzeitig vor deren Ablauf.

Ich beschreibe im folgenden zunächst die manuelle Installation per Helm und gehe danach darauf ein, wie die Konfiguration für Flux v2 aussieht. Für Informationen zu Flux und GitOps verweise ich hier auf den Blogpost GitOps

Installation per Helm

cert-manager kann per Helm installiert werden. Die Quellen für die Zertifikate, die sogenannten Issuer (gültig innerhalb des jeweiligen Namespaces) bzw. ClusterIssuer (Clusterweit gültig), müssen per kubectl installiert werden.

# Namespace for cert-manager resources
kubectl create namespace cert-manager
# Add helm repository
helm repo add jetstack https://charts.jetstack.io
# Update helm repository
helm repo update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v1.1.0 \
  --set installCRDs=true

ClusterIssuer erzeugen

Wir erzeugen zwei ClusterIssuer, die die Let’s encrypt (Produktiv- bzw. Entwicklungs-Instanz) nutzen.

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: <your-mail-address>
    solvers:
    - selector: {}
      http01:
        ingress:
          class: nginx
    privateKeySecretRef:
      name: letsencrypt-staging
    server: https://acme-staging-v02.api.letsencrypt.org/directory
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: <your-mail-address>
    solvers:
    - selector: {}
      http01:
        ingress:
          class: nginx
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory

# Create cluster-issuer
kubectl apply -f cluster-issuer.yaml

GitOps / flux

Zunächst erzeugen wir das Verzeichnis für unseren cert-manager.

# Create directory cert-manager
mkdir infrastructure/cert-manager

Danach fügen wir im kustomizations Manifest im Verzeichnis infrastructure unter resources die Zeile - ./cert-manager ein.

Im infrastructure/sources-Verzeichnis legen wir die Datei jetstack.yaml mit dem HelmRepository-Kubernetes-Manifest an. Die Datei sollte folgenden Inhalt enthalten.

apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
  name: jetstack
spec:
  interval: 30m
  url: https://charts.jetstack.io

Nun legen wir die Dateien infrastructure/cert-manager/namespace.yaml und infrastructure/cert-manager/release.yaml an.

apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: cert-manager
spec:
  releaseName: cert-manager
  chart:
    spec:
      chart: cert-manager
      sourceRef:
        kind: HelmRepository
        name: cert-manager
        namespace: flux-system
      version: v1.1.0
  interval: 1h0m0s
  install:
    remediation:
      retries: 3
  values:
    installCRDs: true

Jetzt benötigen wir noch die Dateikustomization.yaml, die namespace.yaml und release.yaml referenziert.

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: cert-manager
resources:
- namespace.yaml
- release.yaml

Um schließlich auch die ClusterIssuer per GitOps zu erzeugen, erstellen wir noch die Datei infrastructure/cert-manager/clusterissueres.yaml und referenzieren sie in der kustomizations-Datei.

Dazu fügen wir in der Datei kustomizations.yaml unter resources die Zeile - clusterissuers.yaml ein.

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: <your mailadress>
    solvers:
    - selector: {}
      http01:
        ingress:
          class: nginx
    privateKeySecretRef:
      name: letsencrypt-staging
    server: https://acme-staging-v02.api.letsencrypt.org/directory
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: <your mailadress>
    solvers:
    - selector: {}
      http01: 
        ingress:
          class: nginx
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory

Wir sind nun in der Lage, in unserem Kubernetes-Cluster Webanwendungen bzw. Services wie diesen Blog zu betreiben. Mit einem Ingress können wir Webseiten oder APIs auch außerhalb des Kubernetes-Clusters erreichbar machen. Um Verbindungen auf Transportebene zu verschlüsseln können wir SSL/TLS nutzen und die dafür notwendigen Zertifikate automatisch von Let’s Encrypt beziehen und installieren.

Wenn wir uns nicht wie dieser Blog auf einen statischen Webseiten-Generator wie Hugo beschränken wollen, sondern z.B. Wordpress nutzen möchten, benötigen wir die Möglichkeit, Daten persistent zu speichern. Im nächsten Blogpost wird es darum um das Thema Persistenz im Kubernetes-Cluster gehen.