From 70e51905948d07dfcd6d9034fba394d5e772279e Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 7 May 2026 15:25:13 +0200 Subject: [PATCH] docker: DATABASE_URL im entrypoint URL-encoden (Sonderzeichen-Bug auf Prod) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug auf Prod-System (frische Installation): MariaDB legte 'opencrm'-User korrekt an, aber Backend bekam "Access denied for user 'opencrm'@...". Ursache: docker-compose substituierte ${DB_PASSWORD} naiv in "mysql://${DB_USER}:${DB_PASSWORD}@db:3306/${DB_NAME}". Wenn das Passwort Sonderzeichen wie $, !, #, @, :, / enthielt, brach das die URL-Authority-Syntax → Backend connectete mit kaputtem Passwort. Fix: - docker-compose.yml: DATABASE_URL aus environment ENTFERNT. Stattdessen DB_HOST=db, DB_PORT=3306, DB_NAME, DB_USER, DB_PASSWORD als plain env-vars an den Container. - backend/docker-entrypoint.sh: baut DATABASE_URL beim Start mit encodeURIComponent für User+Passwort (via node -e, kein extra Tool wie jq nötig). Funktioniert für beliebige Sonderzeichen. Live-verifiziert: - 'secret$1!#with@special' → 'secret%241!%23with%40special' (encoded) - Backend connectet sauber, Login funktioniert - entrypoint loggt: "[entrypoint] DATABASE_URL aus DB_*-Komponenten gebaut (host=db)" Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/docker-entrypoint.sh | 17 +++++++++++++++++ docker-compose.yml | 15 ++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/backend/docker-entrypoint.sh b/backend/docker-entrypoint.sh index 0d221dd7..6b1b5dd7 100755 --- a/backend/docker-entrypoint.sh +++ b/backend/docker-entrypoint.sh @@ -3,6 +3,23 @@ # RUN_SEED=true beim ersten Start setzen, danach wieder auf false. set -e +# DATABASE_URL aus DB_*-Komponenten bauen, falls nicht explizit gesetzt. +# Wichtig: encodeURIComponent für DB_USER + DB_PASSWORD, damit Sonderzeichen +# wie $, !, #, @, :, / etc. nicht die URL-Authority-Syntax brechen. +# Wir nutzen node-eval (ist eh installiert), kein extra-Tool wie jq nötig. +if [ -z "$DATABASE_URL" ] && [ -n "$DB_USER" ] && [ -n "$DB_PASSWORD" ] && [ -n "$DB_NAME" ]; then + DATABASE_URL=$(node -e " + const u = encodeURIComponent(process.env.DB_USER); + const p = encodeURIComponent(process.env.DB_PASSWORD); + const h = process.env.DB_HOST || 'db'; + const port = process.env.DB_PORT || '3306'; + const n = process.env.DB_NAME; + process.stdout.write(\`mysql://\${u}:\${p}@\${h}:\${port}/\${n}\`); + ") + export DATABASE_URL + echo "[entrypoint] DATABASE_URL aus DB_*-Komponenten gebaut (host=${DB_HOST:-db})" +fi + echo "[entrypoint] Warte auf Datenbank…" # Prisma versucht selbst Connect; einfacher Retry-Loop um Race-Bedingungen # beim parallelen Container-Start abzufangen. diff --git a/docker-compose.yml b/docker-compose.yml index d182800c..85e00992 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,11 +48,16 @@ services: db: condition: service_healthy environment: - # Connection ins Container-Netzwerk (Service-Name = Hostname). - # Wir nutzen den App-User ${DB_USER}, der von MariaDB beim ersten Start - # automatisch mit GRANT ALL PRIVILEGES auf ${DB_NAME}.* angelegt wird - # (über MARIADB_USER/MARIADB_PASSWORD). KEIN root für die App. - DATABASE_URL: "mysql://${DB_USER}:${DB_PASSWORD}@db:3306/${DB_NAME}" + # DATABASE_URL wird vom entrypoint.sh aus den DB_*-Komponenten gebaut – + # mit encodeURIComponent für Passwörter mit Sonderzeichen ($, !, #, @, :, + # / etc.). KEIN root für die App, sondern der App-User ${DB_USER}, den + # MariaDB beim ersten Start automatisch mit GRANT ALL PRIVILEGES auf + # ${DB_NAME}.* anlegt (über MARIADB_USER/MARIADB_PASSWORD). + DB_HOST: db + DB_PORT: 3306 + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} JWT_SECRET: ${JWT_SECRET} JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d} ENCRYPTION_KEY: ${ENCRYPTION_KEY}