Konfiguration der OIDC-Authentifizierung für Browserless Clients

Diese Anleitung beschreibt, wie Sie die OpenID-Connect-Authentifizierung (OIDC) für einen Shoot-Cluster einrichten. Sie umfasst das Erstellen der API-Server-Authentifizierungskonfiguration, die Installation des OIDC-Login-Plugins sowie das Berechtigen von OIDC-Nutzern bzw. Clients im Cluster.

Voraussetzungen

Stellen Sie sicher, dass Ihnen folgende Zugänge und Informationen vorliegen:

  1. Project Kubeconfig: Im PSKE-Dashboard unter Members > Service Accounts verfügbar.
  2. Shoot Kubeconfig: Im Shoot-Overview des PSKE-Dashboards herunterladbar.
  3. OIDC-Zugangsdaten: Issuer-URL, Client-ID und Client-Secret Ihres OIDC-Identity-Providers.

1. Authentifizierungs-ConfigMap erstellen

Erstellen Sie eine Datei kube-apiserver-auth-config.yaml und fügen Sie den folgenden Inhalt ein. Ersetzen Sie dabei die Platzhalter für Issuer-URL und Client-ID. Weitere Informationen finden Sie in der Dokumentation zur OIDC-Konfiguration: OIDC / Structured Authentication.

# kube-apiserver-auth-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-apiserver-auth-config
data:
  config.yaml: |-
    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
      - issuer:
          url: https://example.com
          audiences:
            - example-client-id
        claimMappings:
          username:
            claim: sub
            prefix: https://example.com#

Danach wird die ConfigMap mit der Project-Kubeconfig angewendet:

export KUBECONFIG=kubeconfig-project.yaml
kubectl apply --filename=kube-apiserver-auth-config.yaml

Hinweis: Wenn Sie diese ConfigMap später ändern, kann es erforderlich sein, den Shoot-Cluster manuell neu zu reconcilieren. (s. Punkt 2).

2. ConfigMap am Shoot-Cluster hinterlegen

Bearbeiten Sie die Shoot-Ressource entweder über das PSKE-Dashboard (Ansicht von Overview auf YAML wechseln) oder per kubectl edit mit der Project-Kubeconfig. Fügen Sie die folgende Konfiguration unter spec.kubernetes.kubeAPIServer.structuredAuthentication hinzu:

apiVersion: core.gardener.cloud/v1beta1
kind: Shoot
spec:
  kubernetes:
    kubeAPIServer:
      structuredAuthentication:
        configMapName: kube-apiserver-auth-config

Hinweis: Änderungen an der structuredAuthentication des Shoots lösen automatisch ein Rollout der Authentifizierungskonfiguration aus. Wird lediglich der Inhalt der ConfigMap geändert (ohne Namensänderung), erfolgt keine automatische Reconcilierung.

3. OIDC-Login-Plugin installieren

Installieren Sie das kubectl OIDC-Login-Plugin. Die offizielle Anleitung finden Sie hier:

https://github.com/int128/kubelogin#setup

4. OIDC-Token mit Client-Credentials abrufen

Überprüfen Sie die OIDC-Konfiguration mit folgendem Befehl:

kubectl oidc-login setup --oidc-issuer-url=https://example.com --oidc-client-id=example-client-id --oidc-client-secret=example-client-secret --grant-type=client-credentials

Bei erfolgreicher Ausführung erhalten Sie eine Ausgabe mit den Token-Claims, beispielsweise:

Authentication in progress...
## Authenticated with the OpenID Connect Provider

You got the token with the following claims:

```
{
  "iss": "https://example.com",
  "sub": "example-sub",
  "aud": "example-client-id",
  "exp": 1767312000,
  "iat": 1767225600,
  "jti": "3e9a0664-fa50-46de-85a2-d2b0c060b19c",
  "at_hash": "uav9UWBowQlxgbIzgsvCpE",
}
```

Merken Sie sich insbesondere den Wert des Feldes sub, da dieser im nächsten Schritt benötigt wird.

5. Zugriff auf den Shoot freigeben

Erstellen Sie ein ClusterRoleBinding, das den OIDC-User anhand des sub-Claims an die Rolle view im Cluster bindet:

# oidc-user.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: oidc-cluster-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: https://example.com#example-sub

Anwenden:

export KUBECONFIG=kubeconfig-shoot.yaml
kubectl apply --filename=oidc-user.yaml

6. OIDC-User im kubeconfig hinterlegen

Tragen Sie den OIDC-Login als Benutzer in die kubeconfig ein, damit kubectl automatisch Tokens abrufen kann:

export KUBECONFIG=kubeconfig-shoot.yaml
kubectl config set-credentials oidc \
  --exec-api-version=client.authentication.k8s.io/v1 \
  --exec-interactive-mode=Never \
  --exec-command=kubectl \
  --exec-arg=oidc-login \
  --exec-arg=get-token \
  --exec-arg="--oidc-issuer-url=https://example.com" \
  --exec-arg="--oidc-client-id=example-client-id" \
  --exec-arg="--oidc-client-secret=example-client-secret" \
  --exec-arg="--grant-type=client-credentials"

7. Zugriff testen

Nun können Sie den OIDC-Benutzer verwenden, um kubectl-Befehle auszuführen, zum Beispiel:

kubectl --kubeconfig=kubeconfig-shoot.yaml --user=oidc get nodes

Dex als OIDC Identity Provider für browserlose Clients einrichten

Diese Anleitung ist für Entwicklungs- und Testumgebungen vorgesehen:

  • nip.io: Wird für dynamische DNS-Namen verwendet und ist nicht für produktive Umgebungen geeignet.
  • Secrets: Client-Secrets dürfen niemals direkt in Konfigurationsdateien gespeichert werden.

Voraussetzungen

  1. Tools: Helm, kubectl, kubelogin, openssl sowie eine Shell (z. B. bash)
  2. Shoot Kubeconfig: Shoot-Kubeconfig aus dem PSKE-Dashboard

1. Traefik installieren

helm repo add traefik https://traefik.github.io/charts
helm repo update traefik
helm upgrade traefik traefik/traefik --create-namespace --install --namespace=traefik

2. cert-manager installieren

helm repo add jetstack https://charts.jetstack.io
helm repo update jetstack
helm upgrade cert-manager jetstack/cert-manager --create-namespace --install --namespace=cert-manager \
    --set=crds.enabled=true

Erstellen Sie eine cluster-issuers.yaml Datei mit dem folgenden Inhalt:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-http01
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-http01
    solvers:
      - http01:
          ingress:
            ingressClassName: traefik
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-http01-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-http01-staging
    solvers:
      - http01:
          ingress:
            ingressClassName: traefik

Anwenden:

kubectl --namespace=cert-manager apply --filename=cluster-issuers.yaml

3. Dex installieren

Erstellen Sie eine Datei dex-values.yaml:

config:
  enablePasswordDB: true
  issuer: https://localhost # placeholder, overridden by helm --set
  oauth2:
    passwordConnector: local
  staticClients:
    - id: example-client-id
      secret: example-client-secret
      name: example-client-name
  staticPasswords:
    - username: admin
      email: admin@example.com
      # password: admin (echo admin | htpasswd -Bin admin | cut -d: -f2)
      hash: $2y$05$T0abwx/OUS0EZNkDZgznd.rTsUJZrH1QSlRDHirCGQEMYko7LJbgK
  storage:
    type: memory
env:
  DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT: true
image:
  # using "latest" because even the most recent release v2.45.1 does not support the required grant type "client credentials"
  tag: latest@sha256:bb4835dd1e71986cae4f6b8565e7d79c48f77460098b9e40ad5c117b4ad11465
ingress:
  enabled: true
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-http01-staging # use staging initially
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
  hosts:
    - host: localhost # placeholder, overridden by helm --set
      paths:
        - path: /
          pathType: Prefix
  tls:
    - hosts:
        - localhost # placeholder, overridden by helm --set
      secretName: dex-tls

Ermitteln Sie die LoadBalancer-IP und erzeugen Sie eine nip.io-Domain:

IP=$(kubectl --namespace=traefik get service/traefik --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "IP=${IP:?}"

# "${IP//./-}" replaces dots with dashes (e.g. 1.2.3.4 -> 1-2-3-4)
HOST=${IP//./-}.nip.io
echo "HOST=${HOST:?}"

Installation von Dex:

helm repo add dex https://charts.dexidp.io
helm repo update dex
helm upgrade dex dex/dex --create-namespace --install --namespace=dex \
    --values=dex-values.yaml \
    --set="config.issuer=https://${HOST:?}" \
    --set="ingress.hosts[0].host=${HOST:?}" \
    --set="ingress.tls[0].hosts[0]=${HOST:?}"

4. Zertifikate prüfen

Dex verwendet zunächst das Staging-Zertifikat, um Rate-Limits zu vermeiden. Prüfen Sie, ob es vom Staging-Provider ausgestellt wurde:

openssl s_client -showcerts -connect "${HOST:?}:443" </dev/null 2>/dev/null |
    openssl x509 -noout -issuer -nameopt lname | tee -a /dev/stderr |
    grep -F "organizationName=(STAGING) Let's Encrypt,"

Sobald dies verifiziert ist, aktualisieren Sie den Ingress von Dex, sodass er den Produktions-Let’s-Encrypt-Issuer verwendet:

helm upgrade dex dex/dex --create-namespace --install --namespace=dex --reuse-values \
    --set="ingress.annotations.\"cert-manager.io/cluster-issuer\"=letsencrypt-http01"

Zertifikat überprüfen:

openssl s_client -showcerts -connect "${HOST:?}:443" </dev/null 2>/dev/null |
    openssl x509 -noout -issuer -nameopt lname | tee -a /dev/stderr |
    grep -F "organizationName=Let's Encrypt,"

5. Test OIDC Authentication

kubectl oidc-login setup \
    --oidc-issuer-url="https://${HOST:?}" \
    --oidc-client-id=example-client-id \
    --oidc-client-secret=example-client-secret \
    --grant-type=client-credentials

Alternativ steht auch das Tutorial PSKE - Einrichten von OIDC/2FA auf PSKE zur Verfügung.