fix: update Keycloak configuration and Docker healthcheck improvements
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Successful in 6m48s
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Successful in 6m40s
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Successful in 1m44s
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Successful in 1m31s
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Successful in 6m48s
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Successful in 6m40s
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Successful in 1m44s
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Successful in 1m31s
- Enabled `directAccessGrants` for `frontend-client` in `meldestelle-realm.json` to support ROPC login flow. - Strengthened admin credentials in realm configuration to meet password policy requirements. - Upgraded Keycloak to `26.5.5` with updated Docker healthcheck logic: - Replaced `curl` with bash `/dev/tcp` for compatibility with `ubi9-micro` image. - Switched health endpoint from `/ready` to `/live` for single-node use. - Adjusted healthcheck timings (`start_period`, `timeout`, `interval`) for smoother startup. - Removed deprecated v1 hostname parameter `KC_HOSTNAME_STRICT_HTTPS`. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
@@ -49,7 +49,7 @@ SPRING_DATA_VALKEY_PORT=6379
|
|||||||
SPRING_DATA_VALKEY_PASSWORD=valkey-password
|
SPRING_DATA_VALKEY_PASSWORD=valkey-password
|
||||||
|
|
||||||
# --- KEYCLOAK ---
|
# --- KEYCLOAK ---
|
||||||
KEYCLOAK_IMAGE_TAG=26.4
|
KEYCLOAK_IMAGE_TAG=26.5.5
|
||||||
KC_HEAP_MIN=512M
|
KC_HEAP_MIN=512M
|
||||||
KC_HEAP_MAX=1024M
|
KC_HEAP_MAX=1024M
|
||||||
# Lokale Entwicklung: start-dev (kein Pre-Build nötig, kein --optimized)
|
# Lokale Entwicklung: start-dev (kein Pre-Build nötig, kein --optimized)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
||||||
# === CENTRALIZED BUILD ARGUMENTS ===
|
# === CENTRALIZED BUILD ARGUMENTS ===
|
||||||
ARG GRADLE_VERSION
|
ARG GRADLE_VERSION=9.3.1
|
||||||
ARG JAVA_VERSION
|
ARG JAVA_VERSION=25
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
||||||
# === CENTRALIZED BUILD ARGUMENTS ===
|
# === CENTRALIZED BUILD ARGUMENTS ===
|
||||||
ARG GRADLE_VERSION
|
ARG GRADLE_VERSION=9.3.1
|
||||||
ARG JAVA_VERSION
|
ARG JAVA_VERSION=25
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
# ===================================================================
|
# ===================================================================
|
||||||
# Production-Ready Keycloak Dockerfile
|
# Production-Ready Keycloak Dockerfile
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# Based on: quay.io/keycloak/keycloak:26.4
|
# Based on: quay.io/keycloak/keycloak:26.5.5
|
||||||
# Features:
|
# Features:
|
||||||
# - Pre-built optimized image (faster startup)
|
# - Pre-built optimized image (faster startup)
|
||||||
# - Security hardening
|
# - Security hardening
|
||||||
# - Health monitoring
|
# - Health monitoring
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
ARG KEYCLOAK_IMAGE_TAG
|
ARG KEYCLOAK_IMAGE_TAG=26.5.5
|
||||||
|
|
||||||
FROM quay.io/keycloak/keycloak:${KEYCLOAK_IMAGE_TAG}
|
FROM quay.io/keycloak/keycloak:${KEYCLOAK_IMAGE_TAG}
|
||||||
|
|
||||||
|
ARG KEYCLOAK_IMAGE_TAG=26.5.5
|
||||||
|
|
||||||
LABEL maintainer="Meldestelle Development Team"
|
LABEL maintainer="Meldestelle Development Team"
|
||||||
LABEL description="Production-ready Keycloak for Meldestelle authentication"
|
LABEL description="Production-ready Keycloak for Meldestelle authentication"
|
||||||
LABEL version="${KEYCLOAK_IMAGE_TAG}"
|
LABEL version="${KEYCLOAK_IMAGE_TAG}"
|
||||||
|
|||||||
@@ -239,7 +239,7 @@
|
|||||||
"protocol": "openid-connect",
|
"protocol": "openid-connect",
|
||||||
"standardFlowEnabled": true,
|
"standardFlowEnabled": true,
|
||||||
"implicitFlowEnabled": false,
|
"implicitFlowEnabled": false,
|
||||||
"directAccessGrantsEnabled": false,
|
"directAccessGrantsEnabled": true,
|
||||||
"serviceAccountsEnabled": false,
|
"serviceAccountsEnabled": false,
|
||||||
"authorizationServicesEnabled": false,
|
"authorizationServicesEnabled": false,
|
||||||
"attributes": {
|
"attributes": {
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
"credentials": [
|
"credentials": [
|
||||||
{
|
{
|
||||||
"type": "password",
|
"type": "password",
|
||||||
"value": "password",
|
"value": "Admin#1234",
|
||||||
"temporary": false
|
"temporary": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
+12
-8
@@ -76,8 +76,8 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: config/docker/keycloak/Dockerfile
|
dockerfile: config/docker/keycloak/Dockerfile
|
||||||
args:
|
args:
|
||||||
KEYCLOAK_IMAGE_TAG: "${KEYCLOAK_IMAGE_TAG:-26.4}"
|
KEYCLOAK_IMAGE_TAG: "${KEYCLOAK_IMAGE_TAG:-26.5.5}"
|
||||||
image: "${DOCKER_REGISTRY:-git.mo-code.at/mocode-software}/keycloak:${KEYCLOAK_IMAGE_TAG:-26.4}"
|
image: "${DOCKER_REGISTRY:-git.mo-code.at/mocode-software}/keycloak:${KEYCLOAK_IMAGE_TAG:-26.5.5}"
|
||||||
container_name: "${PROJECT_NAME:-meldestelle}-keycloak"
|
container_name: "${PROJECT_NAME:-meldestelle}-keycloak"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
profiles: [ "infra", "all" ]
|
profiles: [ "infra", "all" ]
|
||||||
@@ -93,8 +93,8 @@ services:
|
|||||||
KC_HOSTNAME: "${KC_HOSTNAME:-localhost}"
|
KC_HOSTNAME: "${KC_HOSTNAME:-localhost}"
|
||||||
# WICHTIG: false erlaubt Zugriff über beliebige Hostnamen (nötig für Server-Betrieb ohne TLS)
|
# WICHTIG: false erlaubt Zugriff über beliebige Hostnamen (nötig für Server-Betrieb ohne TLS)
|
||||||
KC_HOSTNAME_STRICT: "${KC_HOSTNAME_STRICT:-false}"
|
KC_HOSTNAME_STRICT: "${KC_HOSTNAME_STRICT:-false}"
|
||||||
# WICHTIG: false erlaubt HTTP (kein HTTPS-Zwang) – für Entwicklung und HTTP-only Server
|
# KC_HOSTNAME_STRICT_HTTPS wurde entfernt — deprecated v1-Option in Keycloak 26.x (hostname v2).
|
||||||
KC_HOSTNAME_STRICT_HTTPS: "${KC_HOSTNAME_STRICT_HTTPS:-false}"
|
# HTTP-Zugriff wird ausschließlich über KC_HTTP_ENABLED gesteuert.
|
||||||
KC_HTTP_ENABLED: "true"
|
KC_HTTP_ENABLED: "true"
|
||||||
# Admin-Interface explizit auf allen Interfaces binden (0.0.0.0)
|
# Admin-Interface explizit auf allen Interfaces binden (0.0.0.0)
|
||||||
KC_HTTP_MANAGEMENT_PORT: "9000"
|
KC_HTTP_MANAGEMENT_PORT: "9000"
|
||||||
@@ -109,11 +109,15 @@ services:
|
|||||||
postgres:
|
postgres:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD-SHELL", "curl -sf http://localhost:9000/health/ready || exit 1" ]
|
# Keycloak basiert auf ubi9-micro — curl/wget sind NICHT im Image enthalten!
|
||||||
|
# Lösung: Bash /dev/tcp — kein externes Tool nötig, funktioniert auf jedem bash-Image.
|
||||||
|
# Management-Port 9000: Health-Endpoints (/health/live, /health/ready) laufen hier.
|
||||||
|
# /health/live: prüft nur Prozess-Liveness — kein Warten auf JGroups-Cluster-Formation.
|
||||||
|
test: [ "CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && printf 'GET /health/live HTTP/1.0\\r\\nHost: localhost\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '\"UP\"'" ]
|
||||||
interval: "15s"
|
interval: "15s"
|
||||||
timeout: "5s"
|
timeout: "10s"
|
||||||
retries: "10"
|
retries: 5
|
||||||
start_period: "60s"
|
start_period: "90s"
|
||||||
volumes:
|
volumes:
|
||||||
- "./config/docker/keycloak:/opt/keycloak/data/import:Z"
|
- "./config/docker/keycloak:/opt/keycloak/data/import:Z"
|
||||||
# start --optimized nutzt das pre-built Image (kc.sh build im Dockerfile)
|
# start --optimized nutzt das pre-built Image (kc.sh build im Dockerfile)
|
||||||
|
|||||||
@@ -41,6 +41,31 @@ Sicherheitseinstellungen auf einen produktionsreifen Stand gebracht.
|
|||||||
- **`frontend-client` vs. `web-app`:** Klare Trennung: `web-app` für Browser-basierte Web-App, `frontend-client` für
|
- **`frontend-client` vs. `web-app`:** Klare Trennung: `web-app` für Browser-basierte Web-App, `frontend-client` für
|
||||||
native KMP Desktop/Mobile-App.
|
native KMP Desktop/Mobile-App.
|
||||||
|
|
||||||
|
## 🐛 Bugfix: Keycloak Import-Fehler `invalidPasswordMinSpecialCharsMessage` (2026-03-09)
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
Beim Keycloak Härtungs-Step wurde die Password-Policy auf
|
||||||
|
`length(10) and digits(1) and upperCase(1) and specialChars(1) and notUsername(undefined)` gestärkt.
|
||||||
|
Der Test-User `admin` in `meldestelle-realm.json` hatte jedoch noch das Klartext-Passwort `"password"`,
|
||||||
|
das diese Policy verletzt (keine Ziffer, kein Großbuchstabe, kein Sonderzeichen).
|
||||||
|
|
||||||
|
Keycloak validiert beim `--import-realm` Plain-Text-Credentials gegen die im Realm definierte Policy →
|
||||||
|
Server-Start schlägt fehl mit `invalidPasswordMinSpecialCharsMessage`.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- **`config/docker/keycloak/meldestelle-realm.json`**: User `admin` Credential `"password"` → `"Admin1234!"`
|
||||||
|
(erfüllt alle Policy-Regeln: ≥10 Zeichen, Ziffer, Großbuchstabe, Sonderzeichen, nicht gleich Username).
|
||||||
|
|
||||||
|
### Gelerntes
|
||||||
|
|
||||||
|
Keycloak validiert **Plain-Text-Credentials** im Realm-JSON beim Import gegen die Realm-Policy.
|
||||||
|
Wird die Policy nachträglich gestärkt, müssen alle bestehenden User-Credentials im JSON ebenfalls
|
||||||
|
aktualisiert werden — sonst startet der Server nicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ✅ Micrometer Upgrade (2026-03-09, gleiche Session)
|
## ✅ Micrometer Upgrade (2026-03-09, gleiche Session)
|
||||||
|
|
||||||
Verifiziert: `micrometer = "1.16.1"` und `micrometerTracing = "1.6.1"` waren bereits korrekt in
|
Verifiziert: `micrometer = "1.16.1"` und `micrometerTracing = "1.6.1"` waren bereits korrekt in
|
||||||
@@ -171,6 +196,51 @@ gemounteter Konfigs.
|
|||||||
- **Consul Healthcheck:** curl auf `/v1/status/leader` ✅
|
- **Consul Healthcheck:** curl auf `/v1/status/leader` ✅
|
||||||
- **Keycloak Healthcheck:** curl auf `localhost:9000/health/ready` ✅
|
- **Keycloak Healthcheck:** curl auf `localhost:9000/health/ready` ✅
|
||||||
|
|
||||||
|
## 🐛 Bugfix: Keycloak Healthcheck schlägt fehl obwohl Keycloak läuft (2026-03-09)
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
|
||||||
|
`docker compose --profile infra up -d` — Keycloak startet korrekt (Port 8080 + 9000 gebunden,
|
||||||
|
Realm importiert), wird von Docker aber nie als `healthy` markiert. Dieses Problem existierte
|
||||||
|
bereits früher und führte damals zur Entfernung des Healthchecks.
|
||||||
|
|
||||||
|
### Root Cause 1: `/health/ready` vs. `/health/live`
|
||||||
|
|
||||||
|
Keycloak 26.x führt beim Start den JGroups-Cluster-Join-Prozess aus. Bei Single-Node-Betrieb
|
||||||
|
(kein anderer Knoten im Netz) versucht Keycloak 10× einem Ghost-Node beizutreten — jeder Versuch
|
||||||
|
dauert 2 Sekunden = **20 Sekunden Verzögerung** nach dem eigentlichen Startup.
|
||||||
|
|
||||||
|
- **`/health/ready`** wartet auf vollständige Cluster-Formation → gibt während der JOIN-Phase `503`
|
||||||
|
zurück.
|
||||||
|
- **`/health/live`** prüft nur ob der Prozess läuft — unabhängig vom Cluster-Status.
|
||||||
|
|
||||||
|
Mit `start_period: 60s` und `interval: 15s` feuerte der erste Check in genau diesem 20s-Fenster
|
||||||
|
→ false-negative → nach 10 Retries → `unhealthy`.
|
||||||
|
|
||||||
|
### Root Cause 2: `KC_HOSTNAME_STRICT_HTTPS` — deprecated v1-Option
|
||||||
|
|
||||||
|
`WARNING: Hostname v1 options [hostname-strict-https] are still in use` erscheint in jedem
|
||||||
|
Startup-Log. In Keycloak 26.x wurde das Hostname-System auf v2 umgestellt. `KC_HOSTNAME_STRICT_HTTPS`
|
||||||
|
ist ein v1-Parameter der ignoriert wird — HTTP-Zugriff wird ausschließlich über `KC_HTTP_ENABLED`
|
||||||
|
gesteuert.
|
||||||
|
|
||||||
|
### Fixes in `dc-infra.yaml`
|
||||||
|
|
||||||
|
| Parameter | Vorher | Nachher | Begründung |
|
||||||
|
|----------------------------|------------------|-----------------|------------------------------------------------------|
|
||||||
|
| `test` | `…/health/ready` | `…/health/live` | Kein false-negative während JGroups-JOIN |
|
||||||
|
| `start_period` | `"60s"` | `"120s"` | Mehr Puffer für langsamen First-Start (Realm-Import) |
|
||||||
|
| `interval` | `"15s"` | `"20s"` | Weniger Stress während Startup-Phase |
|
||||||
|
| `timeout` | `"5s"` | `"10s"` | Mehr Zeit für Endpoint-Antwort |
|
||||||
|
| `retries` | `"10"` (String) | `10` (Integer) | Korrekter YAML-Typ |
|
||||||
|
| `KC_HOSTNAME_STRICT_HTTPS` | gesetzt | **entfernt** | Deprecated v1-Option, erzeugt Warning |
|
||||||
|
|
||||||
|
### Gelerntes
|
||||||
|
|
||||||
|
- **`/health/live` ist für Single-Node-Keycloak die richtige Wahl** — `ready` ist für Cluster-Szenarien
|
||||||
|
konzipiert wo mehrere Nodes koordiniert hochfahren müssen.
|
||||||
|
- `retries` in Docker Compose Healthcheck sollte als Integer, nicht als String angegeben werden.
|
||||||
|
|
||||||
## ✅ Ping Service — Tracer Bullet Analyse & Fix (2026-03-09, gleiche Session)
|
## ✅ Ping Service — Tracer Bullet Analyse & Fix (2026-03-09, gleiche Session)
|
||||||
|
|
||||||
Vollständige Analyse des Ping Service Stacks (Backend → Gateway → Frontend). Ergebnis: Die gesamte
|
Vollständige Analyse des Ping Service Stacks (Backend → Gateway → Frontend). Ergebnis: Die gesamte
|
||||||
@@ -226,7 +296,202 @@ fachliche Implementierung war bereits vorhanden. Ein einziger kritischer Bug gef
|
|||||||
| `.gitea/workflows/docker-publish.yaml` | Push `main` | Build & Push aller Docker-Images |
|
| `.gitea/workflows/docker-publish.yaml` | Push `main` | Build & Push aller Docker-Images |
|
||||||
| `.gitea/workflows/pr-guard.yaml` | Pull Request | Prüft hartcodierte Versionen in Gradle-Dateien |
|
| `.gitea/workflows/pr-guard.yaml` | Pull Request | Prüft hartcodierte Versionen in Gradle-Dateien |
|
||||||
|
|
||||||
|
## Bugfix: Keycloak Healthcheck — Finale Korrektur (Port 8080 → 9000)
|
||||||
|
|
||||||
|
**Problem:** Keycloak 26.5.5 startet sauber (~15s), wird aber dauerhaft nicht `healthy`.
|
||||||
|
|
||||||
|
**Root Cause (final):** Der vorherige Fix hatte Port 9000 → 8080 geändert — das war **falsch**.
|
||||||
|
|
||||||
|
| Port | Zweck | Health-Endpoint |
|
||||||
|
|----------|-----------------------------------------|-------------------------------------|
|
||||||
|
| **8080** | Haupt-HTTP (Login, Admin-Console, APIs) | ❌ kennt `/health/*` **nicht** → 404 |
|
||||||
|
| **9000** | Management (Actuator, Health, Metrics) | ✅ `/health/live`, `/health/ready` |
|
||||||
|
|
||||||
|
`curl http://localhost:8080/health/live` → **404** → `curl -f` schlägt fehl → `unhealthy`.
|
||||||
|
|
||||||
|
**Fix `dc-infra.yaml`:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
test: ["CMD-SHELL", "curl -sf --max-time 5 http://localhost:9000/health/live || exit 1"]
|
||||||
|
interval: "15s"
|
||||||
|
timeout: "10s"
|
||||||
|
retries: 5
|
||||||
|
start_period: "90s"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum `/health/live` statt `/health/ready`?**
|
||||||
|
`/health/ready` wartet auf JGroups-Cluster-Formation (Single-Node: 10×2s JOIN-Timeout = 20s Verzögerung). `/health/live`
|
||||||
|
prüft nur Prozess-Liveness — sofort `UP` sobald Quarkus läuft.
|
||||||
|
|
||||||
|
**`start_period: 90s`** — Puffer für First-Run mit DB-Schema-Init (Liquibase-Migrations ~15–20s).
|
||||||
|
|
||||||
## 🔜 Nächste Schritte
|
## 🔜 Nächste Schritte
|
||||||
|
|
||||||
- **TLS/HTTPS** — Langfristig: `KC_HOSTNAME_STRICT_HTTPS=true` setzen, sobald TLS eingerichtet ist.
|
- **TLS/HTTPS** — Langfristig: `KC_HOSTNAME_STRICT_HTTPS=true` setzen, sobald TLS eingerichtet ist.
|
||||||
- **Entries Service** — Beginn der Implementierung des ersten echten Fach-Services ("Nennungen").
|
- **Entries Service** — Beginn der Implementierung des ersten echten Fach-Services ("Nennungen").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bugfix: Keycloak Healthcheck — Echter Root Cause (curl nicht vorhanden)
|
||||||
|
|
||||||
|
**Zeitpunkt:** 2026-03-09 ~15:15
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
Das Keycloak-Image `quay.io/keycloak/keycloak:26.5.5` basiert auf **`ubi9-micro`** — einem minimalen Red Hat Image *
|
||||||
|
*ohne `curl` / `wget` / Package Manager**.
|
||||||
|
Alle bisherigen Port-Fixes (8080 → 9000) waren korrekt, aber der `curl`-Befehl scheiterte im Container mit
|
||||||
|
`command not found`.
|
||||||
|
Keycloak selbst lief die ganze Zeit einwandfrei — nur der Healthcheck-Befehl fehlte das nötige Tool.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
| Datei | Änderung |
|
||||||
|
|-----------------|---------------------------------------------|
|
||||||
|
| `dc-infra.yaml` | Healthcheck: `curl` → Bash `/dev/tcp`-Trick |
|
||||||
|
|
||||||
|
**Neuer Healthcheck (kein externes Tool nötig):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && printf 'GET /health/live HTTP/1.0\r\nHost: localhost\r\n\r\n' >&3 && cat <&3 | grep -q '"status":"UP"'"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warum `/dev/tcp` funktioniert
|
||||||
|
|
||||||
|
- Bash hat `/dev/tcp` eingebaut — verfügbar ohne Installation
|
||||||
|
- `exec 3<>/dev/tcp/localhost/9000` öffnet TCP-Verbindung auf Port 9000
|
||||||
|
- `printf ... >&3` sendet einen minimalen HTTP/1.0 GET-Request
|
||||||
|
- `cat <&3 | grep -q '"status":"UP"'` liest Antwort und prüft Health-Status
|
||||||
|
|
||||||
|
### Gelernt
|
||||||
|
|
||||||
|
> Vor jedem `curl`-basierten Healthcheck prüfen: Ist `curl` im Container-Image vorhanden?
|
||||||
|
> Minimale Images (`ubi9-micro`, `distroless`, `alpine` ohne curl-Paket) haben es **nicht**.
|
||||||
|
> Sicherer Standard: `/dev/tcp` (bash built-in) statt `curl` — funktioniert immer wenn bash vorhanden ist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bugfix: Keycloak Healthcheck — grep-Pattern Leerzeichen
|
||||||
|
|
||||||
|
**Zeitpunkt:** 2026-03-09 ~15:27
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
Nach dem `/dev/tcp`-Fix war der Healthcheck immer noch `unhealthy` (ExitCode=1).
|
||||||
|
Der grep-Pattern lautete `'"status":"UP"'` (kein Leerzeichen nach `:`), die tatsächliche
|
||||||
|
JSON-Antwort von Keycloak enthält jedoch `"status": "UP"` (mit Leerzeichen nach dem Doppelpunkt).
|
||||||
|
|
||||||
|
**Beweis via `docker exec`:**
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.0 200 OK
|
||||||
|
{
|
||||||
|
"status": "UP",
|
||||||
|
"checks": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
grep nach `"status":"UP"` → kein Match → ExitCode 1 → unhealthy.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
| Datei | Änderung |
|
||||||
|
|-----------------|--------------------------------------------------|
|
||||||
|
| `dc-infra.yaml` | grep-Pattern: `"status":"UP"` → `"status": "UP"` |
|
||||||
|
|
||||||
|
**Vorher:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
test: ["CMD-SHELL", "... | grep -q '\"status\":\"UP\"'"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nachher:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
test: ["CMD-SHELL", "... | grep -q '\"status\": \"UP\"'"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ergebnis
|
||||||
|
|
||||||
|
Nach `docker compose up -d keycloak`: Status **healthy**, 2x ExitCode=0 bestätigt.
|
||||||
|
|
||||||
|
### Gelernt
|
||||||
|
|
||||||
|
> Den Healthcheck-Befehl immer manuell via `docker exec` testen und die **exakte** API-Antwort
|
||||||
|
> prüfen — JSON-Formatierung (Leerzeichen, Einrückung) kann je nach Keycloak-Version variieren.
|
||||||
|
> Sicherer wäre `grep -q '"UP"'` ohne den Key, um Formatierungsunterschiede zu vermeiden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Optimierung: Healthcheck grep-Pattern vereinfacht
|
||||||
|
|
||||||
|
**Zeitpunkt:** 2026-03-09 ~15:30
|
||||||
|
|
||||||
|
### Maßnahme
|
||||||
|
|
||||||
|
Das grep-Pattern wurde weiter vereinfacht auf `grep -q '"UP"'`, um robust gegen
|
||||||
|
JSON-Formatierungsänderungen (Leerzeichen, Einrückung) in zukünftigen Keycloak-Versionen zu sein.
|
||||||
|
|
||||||
|
| Datei | Vorher | Nachher |
|
||||||
|
|-----------------|----------------------------|------------------|
|
||||||
|
| `dc-infra.yaml` | `grep -q '"status": "UP"'` | `grep -q '"UP"'` |
|
||||||
|
|
||||||
|
### Ergebnis
|
||||||
|
|
||||||
|
Status **healthy**, ExitCode=0 bestätigt. Pattern ist nun versionsunabhängig.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bugfix: Login fehlgeschlagen — `directAccessGrantsEnabled` für `frontend-client`
|
||||||
|
|
||||||
|
**Zeitpunkt:** 2026-03-09 ~15:41
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
|
||||||
|
Web-App (`localhost:4000`) zeigte beim direkten Username/Passwort-Login:
|
||||||
|
|
||||||
|
```
|
||||||
|
Login fehlgeschlagen: HTTP 400 - {"error":"unauthorized_client","error_description":"Client not allowed for direct access grants"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
`AuthApiClient.kt` verwendet `AppConstants.KEYCLOAK_CLIENT_ID = "frontend-client"` mit
|
||||||
|
`grant_type=password` (Resource Owner Password Credentials / ROPC-Flow).
|
||||||
|
Der `frontend-client` hatte in `meldestelle-realm.json` jedoch `directAccessGrantsEnabled: false` —
|
||||||
|
bewusst deaktiviert als reiner PKCE-Client, dabei aber vergessen, dass die Web-App ROPC nutzt.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
| Datei | Änderung |
|
||||||
|
|--------------------------|----------------------------------------------------------------|
|
||||||
|
| `meldestelle-realm.json` | `frontend-client`: `directAccessGrantsEnabled: false` → `true` |
|
||||||
|
|
||||||
|
Live-Update via Admin REST API (kein Neustart nötig):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Token holen
|
||||||
|
TOKEN=$(curl -s -X POST "http://localhost:8180/realms/master/protocol/openid-connect/token" \
|
||||||
|
-d "username=kc-admin&password=kc-password&grant_type=password&client_id=admin-cli" ...)
|
||||||
|
|
||||||
|
# Client-UUID ermitteln & PUT mit directAccessGrantsEnabled:true
|
||||||
|
curl -X PUT "http://localhost:8180/admin/realms/meldestelle/clients/$UUID" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" -d "$UPDATED_CLIENT_JSON"
|
||||||
|
# → HTTP 204
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST ".../realms/meldestelle/protocol/openid-connect/token" \
|
||||||
|
-d "username=admin&password=Admin%231234&grant_type=password&client_id=frontend-client"
|
||||||
|
# → "token_type":"Bearer" ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gelernt
|
||||||
|
|
||||||
|
> `--import-realm` in Keycloak importiert Realms **nur beim ersten Start** (wenn Realm noch nicht existiert).
|
||||||
|
> Änderungen an der realm.json nach dem ersten Import müssen entweder via Admin REST API oder
|
||||||
|
> durch Löschen des Volumes (Datenverlust!) neu eingespielt werden.
|
||||||
|
> Für Dev-Umgebungen: `KC_COMMAND=start --optimized --import-realm` mit separatem Reset-Script
|
||||||
|
> oder Admin-API-Skript für Konfigurationsänderungen vorbereiten.
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Reference in New Issue
Block a user