fix(deploy): preserve credential blocking through hosting parser
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ Die Anwendung startet ohne Schemaänderungen und ohne Demo-Seeding. Datenbankmig
|
|||||||
|
|
||||||
## Konfiguration
|
## Konfiguration
|
||||||
|
|
||||||
Werte aus `production.env.example` als Hosting-Secrets hinterlegen. Im Compose-Stack erzeugt der Dienst `migrate` automatisch ein separates Runtime-Passwort im privaten Secret-Volume, wendet die Migrationen an, vergibt die eingeschränkten DB-Rechte und bereitet die Upload-/Cache-Volumes vor. Danach startet `app` mit UID/GID 1001. Administrative Zugangsdaten werden im App-Service ausdrücklich leer überschrieben, damit eine zusätzliche globale `env_file`-Weitergabe durch das Hosting sie nicht in den App-Container einschleust. Der Start entfernt diese Variablen zusätzlich vor dem App- und Worker-Prozess. Das Eigentümerpasswort steht nur PostgreSQL und dem Initialisierungsdienst zur Verfügung. Für manuelle CLI-Befehle außerhalb von Compose bleibt `DATABASE_URL` die jeweilige administrative oder eingeschränkte URL. Keine echten Secrets im Repository speichern.
|
Werte aus `production.env.example` als Hosting-Secrets hinterlegen. Im Compose-Stack erzeugt der Dienst `migrate` automatisch ein separates Runtime-Passwort im privaten Secret-Volume, wendet die Migrationen an, vergibt die eingeschränkten DB-Rechte und bereitet die Upload-/Cache-Volumes vor. Danach startet `app` mit UID/GID 1001. Administrative Zugangsdaten werden im App-Service ausdrücklich mit dem harmlosen Platzhalter `unavailable-in-app` überschrieben (leere Werte entfernt Coolify bei der Verarbeitung), damit eine zusätzliche globale `env_file`-Weitergabe durch das Hosting sie nicht in den App-Container einschleust. Der Start entfernt diese Variablen zusätzlich vor dem App- und Worker-Prozess. Das Eigentümerpasswort steht nur PostgreSQL und dem Initialisierungsdienst zur Verfügung. Für manuelle CLI-Befehle außerhalb von Compose bleibt `DATABASE_URL` die jeweilige administrative oder eingeschränkte URL. Keine echten Secrets im Repository speichern.
|
||||||
|
|
||||||
`AUTH_URL`, `NEXTAUTH_URL` und `NEXT_PUBLIC_SITE_URL` müssen dieselbe kanonische HTTPS-Origin verwenden. Der Reverse Proxy terminiert TLS, routet intern auf App-Port 3000 und überschreibt weitergeleitete Host-/IP-Header. `TRUSTED_CLIENT_IP_HEADER` nur setzen, wenn der Proxy den gewählten Header (`x-real-ip` oder `cf-connecting-ip`) zuverlässig überschreibt. Ohne diese Einstellung gelten weiterhin gemeinsame globale und identitätsbezogene Datenbanklimits. Proxyseitig Body-Limit von höchstens 26 MiB, Timeouts und zusätzliche Request-Limits setzen.
|
`AUTH_URL`, `NEXTAUTH_URL` und `NEXT_PUBLIC_SITE_URL` müssen dieselbe kanonische HTTPS-Origin verwenden. Der Reverse Proxy terminiert TLS, routet intern auf App-Port 3000 und überschreibt weitergeleitete Host-/IP-Header. `TRUSTED_CLIENT_IP_HEADER` nur setzen, wenn der Proxy den gewählten Header (`x-real-ip` oder `cf-connecting-ip`) zuverlässig überschreibt. Ohne diese Einstellung gelten weiterhin gemeinsame globale und identitätsbezogene Datenbanklimits. Proxyseitig Body-Limit von höchstens 26 MiB, Timeouts und zusätzliche Request-Limits setzen.
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ services:
|
|||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
PORT: 3000
|
PORT: 3000
|
||||||
HOSTNAME: 0.0.0.0
|
HOSTNAME: 0.0.0.0
|
||||||
# Override host-wide env_file injection: administrative credentials belong to migrate only.
|
# A nonempty sentinel survives hosting Compose parsers and blocks global env_file credentials.
|
||||||
DATABASE_URL: ""
|
DATABASE_URL: "unavailable-in-app"
|
||||||
MIGRATION_DATABASE_URL: ""
|
MIGRATION_DATABASE_URL: "unavailable-in-app"
|
||||||
APP_DATABASE_PASSWORD: ""
|
APP_DATABASE_PASSWORD: "unavailable-in-app"
|
||||||
POSTGRES_PASSWORD: ""
|
POSTGRES_PASSWORD: "unavailable-in-app"
|
||||||
PGADMIN_DEFAULT_PASSWORD: ""
|
PGADMIN_DEFAULT_PASSWORD: "unavailable-in-app"
|
||||||
APP_DATABASE_PASSWORD_FILE: /app/runtime-secrets/database-password
|
APP_DATABASE_PASSWORD_FILE: /app/runtime-secrets/database-password
|
||||||
APP_DATABASE_NAME: ${POSTGRES_DB:-kita_planer}
|
APP_DATABASE_NAME: ${POSTGRES_DB:-kita_planer}
|
||||||
AUTH_SECRET: ${AUTH_SECRET:?}
|
AUTH_SECRET: ${AUTH_SECRET:?}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ try {
|
|||||||
const index = line.indexOf('='); return [line.slice(0, index), line.slice(index + 1)];
|
const index = line.indexOf('='); return [line.slice(0, index), line.slice(index + 1)];
|
||||||
}));
|
}));
|
||||||
for (const key of ['POSTGRES_PASSWORD', 'PGADMIN_DEFAULT_PASSWORD', 'APP_DATABASE_PASSWORD', 'MIGRATION_DATABASE_URL']) {
|
for (const key of ['POSTGRES_PASSWORD', 'PGADMIN_DEFAULT_PASSWORD', 'APP_DATABASE_PASSWORD', 'MIGRATION_DATABASE_URL']) {
|
||||||
if (process.env[key] || main[key]) throw new Error('Administrative credential reached the app.');
|
if ((process.env[key] && process.env[key] !== 'unavailable-in-app') || main[key]) throw new Error('Administrative credential reached the app.');
|
||||||
}
|
}
|
||||||
// Configured global/legacy URLs must not expose an administrative login either.
|
// Configured global/legacy URLs must not expose an administrative login either.
|
||||||
if (process.env.DATABASE_URL) throw new Error('Hosting injected a legacy database URL.');
|
if (process.env.DATABASE_URL && process.env.DATABASE_URL !== 'unavailable-in-app') throw new Error('Hosting injected a legacy database URL.');
|
||||||
if (new URL(main.DATABASE_URL).username !== 'kita_runtime') throw new Error('Wrong application database role.');
|
if (new URL(main.DATABASE_URL).username !== 'kita_runtime') throw new Error('Wrong application database role.');
|
||||||
prisma = new PrismaClient({ datasourceUrl: main.DATABASE_URL });
|
prisma = new PrismaClient({ datasourceUrl: main.DATABASE_URL });
|
||||||
const [row] = await prisma.$queryRawUnsafe('SELECT current_user');
|
const [row] = await prisma.$queryRawUnsafe('SELECT current_user');
|
||||||
|
|||||||
Reference in New Issue
Block a user