Installation mit Kubernetes¶
Die Kubernetes-Manifeste befinden sich im Repository unter dist/kubernetes/.
Dateien im Überblick¶
Datei |
Inhalt |
|---|---|
|
Passwörter und Zugangsdaten (Zertifikats-Passwort, Admin-Passwort, DB-Connection-Strings, Mail) |
|
Alle nicht-sensiblen Einstellungen sowie auskommentierte Migrationsbeispiele |
|
PersistentVolumeClaim für die Datenhaltung (Datenbank, Signing-Keys, Data-Protection-Keys) |
|
Deployment mit SecurityContext, Liveness-/Readiness-Probes und Resource-Limits |
|
ClusterIP-Service, der den Container-Port 8080 intern auf Port 80 exponiert |
|
Ingress-Vorlage (nginx, optional TLS via cert-manager) |
Schnellstart¶
git clone https://github.com/jugstalt/identityserver.net.git
cd identityserver.net/dist/kubernetes
# 1. secret.yaml und configmap.yaml an die eigene Umgebung anpassen
# 2. Alle Manifeste anwenden
kubectl apply -f .
Der OIDC-Discovery-Endpunkt ist danach erreichbar unter:
http://<ingress-host>/.well-known/openid-configuration
Namespace¶
Alle Manifeste verwenden standardmäßig namespace: default. Für einen eigenen
Namespace diesen zuerst anlegen und anschließend das Feld namespace: in jeder
Datei anpassen:
kubectl create namespace identity
# dann namespace: identity in allen .yaml Files setzen
Alternativ kubectl apply -n identity -f . verwenden und das Namespace-Feld in den
Dateien weglassen — für Produktionsumgebungen ist die explizite Angabe in den Dateien
aber vorzuziehen.
Secret (secret.yaml)¶
Sensible Werte werden in einem Kubernetes Secret gespeichert. Die Datei verwendet
stringData, sodass Werte im Klartext eingetragen werden können — Kubernetes
kodiert sie automatisch in Base64:
apiVersion: v1
kind: Secret
metadata:
name: identityserver-secret
namespace: default
type: Opaque
stringData:
IDENTITYSERVER__SIGNINGCREDENTIAL__CERTPASSWORD: "Change-This-Password!"
IDENTITYSERVER__MIGRATIONS__ADMINPASSWORD: "Admin1234!"
# Datenbank — genau eine auskommentieren:
# IDENTITYSERVER__CONNECTIONSTRINGS__SQLSERVER: "Server=db;..."
# IDENTITYSERVER__CONNECTIONSTRINGS__POSTGRES: "Host=db;..."
# IDENTITYSERVER__CONNECTIONSTRINGS__MONGODB: "mongodb://mongo:27017"
# Mail-Sender — gewünschten auskommentieren:
# IDENTITYSERVER__MAIL__SMTP__PASSWORD: "smtp-password"
# IDENTITYSERVER__MAIL__SENDGRID__APIKEY: "SG.xxxxxxxxxxxx"
# Externer Identity Provider:
# IDENTITYSERVER__EXTERNAL__MICROSOFTIDENTITYWEB__CLIENTSECRET: "..."
Warnung
secret.yaml mit echten Zugangsdaten niemals in ein öffentliches Repository
einchecken. In Produktionsumgebungen einen Secrets-Manager verwenden
(Sealed Secrets, External Secrets Operator, Vault, …).
ConfigMap (configmap.yaml)¶
Nicht-sensible Einstellungen stehen in der ConfigMap. Die Variablennamen verwenden
__ als Trennzeichen für hierarchische ASP.NET Core-Konfigurationswerte — identisch
mit der docker-compose-Installation.
Die wichtigsten Werte vor dem ersten Deployment anpassen:
# Öffentliche URL — muss mit der URL übereinstimmen, unter der Clients den Server erreichen
IDENTITYSERVER__PUBLICORIGIN: "https://auth.example.com"
IDENTITYSERVER__APPLICATIONTITLE: "IdentityServer.NET"
# LiteDB (Standard) — Pfad innerhalb des PVC, das bei /home/app eingehängt ist
IDENTITYSERVER__CONNECTIONSTRINGS__LITEDB: "/home/app/identityserver-net/is_identityserver-net.db"
# Hinter einem Ingress-Controller, der TLS terminiert:
IDENTITYSERVER__CONFIGURE__USEHTTPSREDIRECTION: "false"
IDENTITYSERVER__CONFIGURE__ADDXFORWARDEDPROTOMIDDLEWARE: "true"
Vollständige Liste aller verfügbaren Konfigurationsvariablen: siehe configmap.yaml
im Repository.
PersistentVolumeClaim (pvc.yaml)¶
1 Gi ist für die meisten Deployments mit LiteDB ausreichend. Die Storage-Class an den eigenen Cluster anpassen:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: identityserver-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# storageClassName: standard
Deployment (deployment.yaml)¶
Das Deployment hängt das PVC ein, setzt den SecurityContext und konfiguriert Health-Probes.
Volume und Berechtigungen¶
Das PVC wird bei /home/app eingehängt — nicht bei
/home/app/identityserver-net. Ein Einhängen eine Ebene tiefer würde dazu führen,
dass Kubernetes das Verzeichnis als root anlegt, was der nicht-privilegierte
app-Benutzer (UID 1654) nicht beschreiben kann. Das Einhängen bei /home/app
übernimmt stattdessen die Dateirechte aus dem Image.
fsGroup: 1654 stellt sicher, dass das eingehängte Volume dem app-Benutzer
gehört:
spec:
securityContext:
fsGroup: 1654
runAsNonRoot: true
Health-Probes¶
Beide Probes rufen den OIDC-Discovery-Endpunkt auf, der verfügbar ist, sobald der Server vollständig initialisiert ist:
livenessProbe:
httpGet:
path: /.well-known/openid-configuration
port: 8080
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /.well-known/openid-configuration
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
Service (service.yaml)¶
Ein ClusterIP-Service mappt Port 80 auf den Container-Port 8080:
spec:
selector:
app: identityserver
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP
type auf NodePort oder LoadBalancer ändern, wenn kein Ingress-Controller
verwendet wird.
Ingress / TLS (ingress.yaml)¶
Die Vorlage richtet sich an den nginx Ingress Controller. auth.example.com durch
die eigene Domain ersetzen:
spec:
rules:
- host: auth.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: identityserver
port:
name: http
TLS mit cert-manager (Let’s Encrypt)¶
Den tls-Block und die cert-manager-Annotation einkommentieren:
metadata:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- auth.example.com
secretName: identityserver-tls
Anschließend IDENTITYSERVER__PUBLICORIGIN in der configmap.yaml setzen:
IDENTITYSERVER__PUBLICORIGIN: "https://auth.example.com"
Und die aktualisierte ConfigMap einpielen sowie einen Rollout-Neustart auslösen:
kubectl apply -f configmap.yaml
kubectl rollout restart deployment/identityserver
Migrationen (Seeding)¶
Bemerkung
Migrationen werden nur ausgeführt, wenn ASPNETCORE_ENVIRONMENT=Development
gesetzt ist. Sie legen fehlende Einträge beim ersten Start an und überschreiben
niemals vorhandene Daten. Danach kann die Variable entfernt oder belassen werden —
nachfolgende Starts verändern nichts Bestehendes.
Das Seeding wird in der configmap.yaml konfiguriert. Alle Migrations-Werte sind
nicht-sensibel — Ausnahme: Passwörter und Secrets gehören in die secret.yaml.
Vollständiges Beispiel für ein typisches Setup:
configmap.yaml
ASPNETCORE_ENVIRONMENT: "Development"
# Identity Resources
IDENTITYSERVER__MIGRATIONS__IDENTITYRESOURCES__0__NAME: "openid"
IDENTITYSERVER__MIGRATIONS__IDENTITYRESOURCES__1__NAME: "profile"
IDENTITYSERVER__MIGRATIONS__IDENTITYRESOURCES__2__NAME: "role"
# API Resource
IDENTITYSERVER__MIGRATIONS__APIRESOURCES__0__NAME: "my-api"
IDENTITYSERVER__MIGRATIONS__APIRESOURCES__0__SCOPES__0__NAME: "read"
IDENTITYSERVER__MIGRATIONS__APIRESOURCES__0__SCOPES__1__NAME: "write"
# Rollen
IDENTITYSERVER__MIGRATIONS__ROLES__0__NAME: "admin"
IDENTITYSERVER__MIGRATIONS__ROLES__1__NAME: "user"
# Zusätzlicher Benutzer (Passwort in secret.yaml)
IDENTITYSERVER__MIGRATIONS__USERS__0__NAME: "alice@example.com"
IDENTITYSERVER__MIGRATIONS__USERS__0__ROLES__0: "admin"
# Web Application Client
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__CLIENTTYPE: "WebApplication"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__CLIENTID: "my-webapp"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__CLIENTURL: "https://myapp.example.com"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__SCOPES__0: "openid"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__SCOPES__1: "profile"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__SCOPES__2: "role"
# API Client (Client Credentials)
IDENTITYSERVER__MIGRATIONS__CLIENTS__1__CLIENTTYPE: "ApiClient"
IDENTITYSERVER__MIGRATIONS__CLIENTS__1__CLIENTID: "my-service"
IDENTITYSERVER__MIGRATIONS__CLIENTS__1__SCOPES__0: "my-api"
IDENTITYSERVER__MIGRATIONS__CLIENTS__1__SCOPES__1: "my-api.read"
secret.yaml
IDENTITYSERVER__MIGRATIONS__ADMINPASSWORD: "Admin1234!"
IDENTITYSERVER__MIGRATIONS__APIRESOURCES__0__APISECRET: "apisecret"
IDENTITYSERVER__MIGRATIONS__USERS__0__PASSWORD: "Alice1234!"
IDENTITYSERVER__MIGRATIONS__CLIENTS__0__CLIENTSECRET: "secret"
IDENTITYSERVER__MIGRATIONS__CLIENTS__1__CLIENTSECRET: "secret"
Passkey (WebAuthn / FIDO2)¶
Passkeys ermöglichen die passwortlose Anmeldung per Biometrie oder Hardware-Schlüssel.
Folgende Werte in der configmap.yaml setzen:
Variable |
Beschreibung |
|---|---|
|
|
|
|
|
WebAuthn Relying Party ID, z. B. |
|
Anzeigename im Authenticator während der Passkey-Registrierung |
Erstes Login¶
Nach dem ersten Start die Pod-Logs auf die erzeugten Admin-Zugangsdaten prüfen:
kubectl logs deployment/identityserver
Dort erscheint ein Block ähnlich wie:
################# Setup ##################
User admin@identityserver.net created
Password: Admin1234!
#########################################
Die Standard-Admin-E-Mail-Adresse ist admin@identityserver.net.
Wurde IDENTITYSERVER__MIGRATIONS__ADMINPASSWORD nicht gesetzt, generiert der
Server ein zufälliges 16-Zeichen-Passwort und gibt es in den Logs aus.
Update¶
Das neueste Image laden und einen Rolling-Restart auslösen:
kubectl rollout restart deployment/identityserver
kubectl rollout status deployment/identityserver
Um eine bestimmte Version festzuhalten, image: in der deployment.yaml anpassen:
image: gstalt/identityserver-net:5.25.0000
Konfiguration ändern¶
Nach Änderungen an configmap.yaml oder secret.yaml anwenden und neu starten:
kubectl apply -f configmap.yaml # bzw. secret.yaml
kubectl rollout restart deployment/identityserver
Horizontale Skalierung¶
Bemerkung
Mit LiteDB (Standard) ist nur replicas: 1 möglich, da LiteDB keine
gleichzeitigen Schreibzugriffe aus mehreren Pods unterstützt.
Für mehrere Replikas auf eine der folgenden Datenbanken wechseln
(Connection-String in secret.yaml):
SQL Server (
IDENTITYSERVER__CONNECTIONSTRINGS__SQLSERVER)PostgreSQL (
IDENTITYSERVER__CONNECTIONSTRINGS__POSTGRES)MongoDB (
IDENTITYSERVER__CONNECTIONSTRINGS__MONGODB)
Außerdem muss das PVC accessModes: ReadWriteMany unterstützen oder der Speicher
für Signing-Keys und Data-Protection-Keys extern abgelegt werden.
Fehlerbehebung¶
Pod bleibt in Pending
kubectl describe pod -l app=identityserver
Im Abschnitt Events nachsehen — meistens ein PVC das nicht gebunden werden kann
oder unzureichende Node-Ressourcen.
Pod stürzt beim Start ab ( CrashLoopBackOff )
kubectl logs deployment/identityserver --previous
Häufige Ursachen:
UnauthorizedAccessException: Access to the path '/home/app/...' is denied— das Volume ist am falschen Pfad eingehängt oderfsGroupfehlt. Prüfen, ob das Mount bei/home/appliegt undsecurityContext.fsGroup: 1654gesetzt ist.IDENTITYSERVER__PUBLICORIGINnicht gesetzt — der Server kann ohne einen gültigen Public Origin nicht starten.
Pod ist Running, aber die Site ist nicht erreichbar
kubectl get ingress
kubectl describe ingress identityserver
Sicherstellen, dass der Ingress-Controller installiert ist und der Host mit der verwendeten Domain übereinstimmt.
Shell-Zugriff zum Debuggen
kubectl exec -it deployment/identityserver -- /bin/sh