fix(deploy): Backend startet wieder — JWT-Secret kam leer im Container an
Startblocker aus d9fecb6: die Compose-Dateien lesen seither
${NACHSUCHE_JWT_SECRET} (bzw. DROHNENFUEHRER_/STOEBERHUNDE_), die .env-Dateien
definieren aber weiterhin JWT_SECRET. Compose ersetzte die unbekannte Variable
still durch einen Leerstring, config/env.js beendete den Prozess daraufhin mit
Exit-Code 1 — mit restart: unless-stopped ein Crash-Loop. Nachgewiesen mit
`docker compose config` (JWT_SECRET: "") und einem Startversuch.
- Compose nutzt jetzt ${..._JWT_SECRET:?...}: fehlt der Wert, bricht compose
mit einer Meldung ab, statt ihn stillschweigend zu leeren.
- Die .env-Dateien (nicht versioniert) tragen die korrekten Namen und je App
ein EIGENES 32-Byte-Secret. Vorher stand in allen drei derselbe 16-Zeichen-
Wert, wodurch die App-Trennung aus d9fecb6 wirkungslos war.
FOLGE: alle laufenden Admin-Sitzungen und Fuehrer-Tokens sind ungueltig,
alle muessen sich einmal neu anmelden.
- Fuehrer-Tokens tragen jetzt app: config.appName, und handlerAuth.js sowie
die umbenannten Varianten pruefen den Claim. Vorher trugen sie nur
{ id, role } — die app-Pruefung in middleware/auth.js lief bei ihnen ins
Leere, weil sie an `decoded.app &&` haengt.
Geprueft: Fuehrer-Token aus App A gegen /me von App B -> 403, gegen die
eigene -> 200.
- APP_URL steht in Compose wieder auf ${APP_URL:-}. Der Default aus der
letzten Runde hat die Warnung aus config/env.js unterdrueckt, die auf eine
fehlende Konfiguration hinweisen sollte.
- .env.example dokumentiert die neuen Namen, den openssl-Aufruf und dass
CORS_ORIGIN/APP_URL produktiv nicht auf localhost stehen duerfen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
dd39a7aff2
commit
d0e48f3385
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -6,13 +6,17 @@ NODE_ENV=development
|
|||
MONGO_URI=mongodb://127.0.0.1:27017/drohnenfuehrer
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
||||
# Der Name MUSS zu docker-compose.yml passen. Je App ein EIGENES Secret:
|
||||
# openssl rand -hex 32
|
||||
DROHNENFUEHRER_JWT_SECRET=
|
||||
JWT_EXPIRES_IN=24h
|
||||
|
||||
# Admin Initial Password (used by seed.js if admin doesn't exist)
|
||||
# ADMIN_INITIAL_PASSWORD=secure-password-here
|
||||
|
||||
# CORS Configuration (comma-separated for multiple origins)
|
||||
# Produktiv die echte oeffentliche Herkunft eintragen, nicht localhost —
|
||||
# APP_URL wird daraus abgeleitet, wenn es nicht gesetzt ist.
|
||||
CORS_ORIGIN=http://localhost:3000
|
||||
|
||||
# Geocoding Configuration (OpenStreetMap Nominatim)
|
||||
|
|
@ -23,7 +27,9 @@ GEOCODE_MIN_DELAY_MS=1100
|
|||
# Basis-URL der App fuer Links in E-Mails (Passwort-Reset).
|
||||
# MUSS den Unterpfad enthalten, unter dem die App ausgeliefert wird.
|
||||
# Ohne diesen Wert wird er aus CORS_ORIGIN + "/drohnenfuehrer" zusammengesetzt.
|
||||
APP_URL=http://localhost:8081/drohnenfuehrer
|
||||
# Produktiv die echte oeffentliche URL inkl. Unterpfad eintragen.
|
||||
# Leer lassen -> wird aus CORS_ORIGIN + "/drohnenfuehrer" gebildet (mit Warnung).
|
||||
APP_URL=
|
||||
|
||||
# SMTP fuer Passwort-Reset-Mails (optional).
|
||||
# Fehlt die Konfiguration, wird der Reset-Link nur ins Log geschrieben.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const drohnenfuehrerLogin = async (req, res) => {
|
|||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user._id.toString(), role: 'drohnenfuehrer' },
|
||||
{ id: user._id.toString(), role: 'drohnenfuehrer', app: config.appName },
|
||||
config.jwtSecret,
|
||||
{ expiresIn: '12h' }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ const authenticateDrohnenfuehrer = (req, res, next) => {
|
|||
if (decoded.role !== 'drohnenfuehrer') {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
// Token einer anderen App ablehnen. Greift auch dann, wenn versehentlich
|
||||
// wieder ein gemeinsames JWT-Secret konfiguriert wird.
|
||||
if (decoded.app && decoded.app !== config.appName) {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
req.drohnenfuehrerUser = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ services:
|
|||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://drohnenfuehrer:${MONGO_PASSWORD}@mongo:27017/drohnenfuehrer?authSource=admin
|
||||
- JWT_SECRET=${DROHNENFUEHRER_JWT_SECRET}
|
||||
# :? statt stiller Leerersetzung — sonst startet das Backend mit
|
||||
# leerem Secret und beendet sich sofort wieder (Crash-Loop).
|
||||
- JWT_SECRET=${DROHNENFUEHRER_JWT_SECRET:?DROHNENFUEHRER_JWT_SECRET muss in .env gesetzt sein}
|
||||
- JWT_EXPIRES_IN=24h
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:8081}
|
||||
- ADMIN_THORSTEN_PASSWORD=${ADMIN_THORSTEN_PASSWORD}
|
||||
|
|
@ -46,7 +48,9 @@ services:
|
|||
# - SMTP_PASS=${SMTP_PASS}
|
||||
# - SMTP_FROM=drohnenfuehrer@example.com
|
||||
# Basis fuer Links in Passwort-Reset-Mails. MUSS den Unterpfad enthalten.
|
||||
- APP_URL=${APP_URL:-http://localhost:8081/drohnenfuehrer}
|
||||
# Leer lassen, wenn nicht konfiguriert: config/env.js baut den Wert
|
||||
# dann aus CORS_ORIGIN + Unterpfad und warnt sichtbar darueber.
|
||||
- APP_URL=${APP_URL:-}
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
|
|
@ -6,13 +6,17 @@ NODE_ENV=development
|
|||
MONGO_URI=mongodb://127.0.0.1:27017/tracking-leaders
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
||||
# Der Name MUSS zu docker-compose.yml passen. Je App ein EIGENES Secret:
|
||||
# openssl rand -hex 32
|
||||
NACHSUCHE_JWT_SECRET=
|
||||
JWT_EXPIRES_IN=24h
|
||||
|
||||
# Admin Initial Password (used by seed.js if admin doesn't exist)
|
||||
# ADMIN_INITIAL_PASSWORD=secure-password-here
|
||||
|
||||
# CORS Configuration (comma-separated for multiple origins)
|
||||
# Produktiv die echte oeffentliche Herkunft eintragen, nicht localhost —
|
||||
# APP_URL wird daraus abgeleitet, wenn es nicht gesetzt ist.
|
||||
CORS_ORIGIN=http://localhost:3000
|
||||
|
||||
# Geocoding Configuration (OpenStreetMap Nominatim)
|
||||
|
|
@ -23,7 +27,9 @@ GEOCODE_MIN_DELAY_MS=1100
|
|||
# Basis-URL der App fuer Links in E-Mails (Passwort-Reset).
|
||||
# MUSS den Unterpfad enthalten, unter dem die App ausgeliefert wird.
|
||||
# Ohne diesen Wert wird er aus CORS_ORIGIN + "/nachsuche" zusammengesetzt.
|
||||
APP_URL=http://localhost:8080/nachsuche
|
||||
# Produktiv die echte oeffentliche URL inkl. Unterpfad eintragen.
|
||||
# Leer lassen -> wird aus CORS_ORIGIN + "/nachsuche" gebildet (mit Warnung).
|
||||
APP_URL=
|
||||
|
||||
# SMTP fuer Passwort-Reset-Mails (optional).
|
||||
# Fehlt die Konfiguration, wird der Reset-Link nur ins Log geschrieben.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const handlerLogin = async (req, res) => {
|
|||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user._id.toString(), role: 'handler' },
|
||||
{ id: user._id.toString(), role: 'handler', app: config.appName },
|
||||
config.jwtSecret,
|
||||
{ expiresIn: '12h' }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ const authenticateHandler = (req, res, next) => {
|
|||
if (decoded.role !== 'handler') {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
// Token einer anderen App ablehnen. Greift auch dann, wenn versehentlich
|
||||
// wieder ein gemeinsames JWT-Secret konfiguriert wird.
|
||||
if (decoded.app && decoded.app !== config.appName) {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
req.handlerUser = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ services:
|
|||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://nachsuche:${MONGO_PASSWORD}@mongo:27017/nachsuche?authSource=admin
|
||||
- JWT_SECRET=${NACHSUCHE_JWT_SECRET}
|
||||
# :? statt stiller Leerersetzung — sonst startet das Backend mit
|
||||
# leerem Secret und beendet sich sofort wieder (Crash-Loop).
|
||||
- JWT_SECRET=${NACHSUCHE_JWT_SECRET:?NACHSUCHE_JWT_SECRET muss in .env gesetzt sein}
|
||||
- JWT_EXPIRES_IN=24h
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:8080}
|
||||
- ADMIN_THORSTEN_PASSWORD=${ADMIN_THORSTEN_PASSWORD}
|
||||
|
|
@ -48,7 +50,9 @@ services:
|
|||
# - SMTP_PASS=${SMTP_PASS}
|
||||
# - SMTP_FROM=nachsuche@example.com
|
||||
# Basis fuer Links in Passwort-Reset-Mails. MUSS den Unterpfad enthalten.
|
||||
- APP_URL=${APP_URL:-http://localhost:8080/nachsuche}
|
||||
# Leer lassen, wenn nicht konfiguriert: config/env.js baut den Wert
|
||||
# dann aus CORS_ORIGIN + Unterpfad und warnt sichtbar darueber.
|
||||
- APP_URL=${APP_URL:-}
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
|
|
@ -6,13 +6,17 @@ NODE_ENV=development
|
|||
MONGO_URI=mongodb://127.0.0.1:27017/stoeberhunde
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
||||
# Der Name MUSS zu docker-compose.yml passen. Je App ein EIGENES Secret:
|
||||
# openssl rand -hex 32
|
||||
STOEBERHUNDE_JWT_SECRET=
|
||||
JWT_EXPIRES_IN=24h
|
||||
|
||||
# Admin Initial Password (used by seed.js if admin doesn't exist)
|
||||
# ADMIN_INITIAL_PASSWORD=secure-password-here
|
||||
|
||||
# CORS Configuration (comma-separated for multiple origins)
|
||||
# Produktiv die echte oeffentliche Herkunft eintragen, nicht localhost —
|
||||
# APP_URL wird daraus abgeleitet, wenn es nicht gesetzt ist.
|
||||
CORS_ORIGIN=http://localhost:3000
|
||||
|
||||
# Geocoding Configuration (OpenStreetMap Nominatim)
|
||||
|
|
@ -23,7 +27,9 @@ GEOCODE_MIN_DELAY_MS=1100
|
|||
# Basis-URL der App fuer Links in E-Mails (Passwort-Reset).
|
||||
# MUSS den Unterpfad enthalten, unter dem die App ausgeliefert wird.
|
||||
# Ohne diesen Wert wird er aus CORS_ORIGIN + "/stoeberhunde" zusammengesetzt.
|
||||
APP_URL=http://localhost:8082/stoeberhunde
|
||||
# Produktiv die echte oeffentliche URL inkl. Unterpfad eintragen.
|
||||
# Leer lassen -> wird aus CORS_ORIGIN + "/stoeberhunde" gebildet (mit Warnung).
|
||||
APP_URL=
|
||||
|
||||
# SMTP fuer Passwort-Reset-Mails (optional).
|
||||
# Fehlt die Konfiguration, wird der Reset-Link nur ins Log geschrieben.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const stoeberhundefuehrerLogin = async (req, res) => {
|
|||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user._id.toString(), role: 'stoeberhundefuehrer' },
|
||||
{ id: user._id.toString(), role: 'stoeberhundefuehrer', app: config.appName },
|
||||
config.jwtSecret,
|
||||
{ expiresIn: '12h' }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ const authenticateStoeberhundefuehrer = (req, res, next) => {
|
|||
if (decoded.role !== 'stoeberhundefuehrer') {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
// Token einer anderen App ablehnen. Greift auch dann, wenn versehentlich
|
||||
// wieder ein gemeinsames JWT-Secret konfiguriert wird.
|
||||
if (decoded.app && decoded.app !== config.appName) {
|
||||
return res.status(403).json({ success: false, message: 'Zugriff verweigert' });
|
||||
}
|
||||
req.stoeberhundefuehrerUser = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ services:
|
|||
environment:
|
||||
- NODE_ENV=production
|
||||
- MONGO_URI=mongodb://stoeberhunde:${MONGO_PASSWORD}@mongo:27017/stoeberhunde?authSource=admin
|
||||
- JWT_SECRET=${STOEBERHUNDE_JWT_SECRET}
|
||||
# :? statt stiller Leerersetzung — sonst startet das Backend mit
|
||||
# leerem Secret und beendet sich sofort wieder (Crash-Loop).
|
||||
- JWT_SECRET=${STOEBERHUNDE_JWT_SECRET:?STOEBERHUNDE_JWT_SECRET muss in .env gesetzt sein}
|
||||
- JWT_EXPIRES_IN=24h
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:8082}
|
||||
- ADMIN_THORSTEN_PASSWORD=${ADMIN_THORSTEN_PASSWORD}
|
||||
|
|
@ -46,7 +48,9 @@ services:
|
|||
# - SMTP_PASS=${SMTP_PASS}
|
||||
# - SMTP_FROM=stoeberhunde@example.com
|
||||
# Basis fuer Links in Passwort-Reset-Mails. MUSS den Unterpfad enthalten.
|
||||
- APP_URL=${APP_URL:-http://localhost:8082/stoeberhunde}
|
||||
# Leer lassen, wenn nicht konfiguriert: config/env.js baut den Wert
|
||||
# dann aus CORS_ORIGIN + Unterpfad und warnt sichtbar darueber.
|
||||
- APP_URL=${APP_URL:-}
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
Loading…
Reference in New Issue