docker: DATABASE_URL im entrypoint URL-encoden (Sonderzeichen-Bug auf Prod)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
+10
-5
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user