fix: require ENCRYPTION_KEY at startup instead of auto-generating

Auto-generating and persisting the key to data/.encryption_key co-locates
the key with the database, defeating encryption at rest if an attacker can
read the data directory. It also silently loses all encrypted secrets if the
data volume is recreated.

Replace the auto-generation fallback with a hard startup error that tells
operators exactly what to do:
- Upgraders from the JWT_SECRET-derived encryption era: set ENCRYPTION_KEY
  to their old JWT_SECRET so existing ciphertext remains readable.
- Fresh installs: generate a key with `openssl rand -hex 32`.
This commit is contained in:
jubnl
2026-04-01 08:38:02 +02:00
parent 7a314a92b1
commit 358afd2428

View File

@@ -44,24 +44,14 @@ export function updateJwtSecret(newSecret: string): void {
// old JWT_SECRET so existing encrypted values continue to decrypt correctly.
// After re-saving all credentials via the admin panel you can switch to a new
// random ENCRYPTION_KEY.
let ENCRYPTION_KEY: string = process.env.ENCRYPTION_KEY || '';
const ENCRYPTION_KEY: string = process.env.ENCRYPTION_KEY || '';
if (!ENCRYPTION_KEY) {
const keyFile = path.join(dataDir, '.encryption_key');
try {
ENCRYPTION_KEY = fs.readFileSync(keyFile, 'utf8').trim();
} catch {
ENCRYPTION_KEY = crypto.randomBytes(32).toString('hex');
try {
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
fs.writeFileSync(keyFile, ENCRYPTION_KEY, { mode: 0o600 });
console.log('Generated and saved encryption key to', keyFile);
} catch (writeErr: unknown) {
console.warn('WARNING: Could not persist encryption key to disk:', writeErr instanceof Error ? writeErr.message : writeErr);
console.warn('Encrypted secrets will be unreadable after restart. Set ENCRYPTION_KEY env var for persistent encryption.');
}
}
console.error('FATAL: ENCRYPTION_KEY is not set.');
console.error('If this occurs after an update from a version that derived encryption from JWT_SECRET,');
console.error('set ENCRYPTION_KEY to the value of your old JWT_SECRET to keep existing secrets readable.');
console.error('For a fresh install, generate a random key: openssl rand -hex 32');
process.exit(1);
}
export { ENCRYPTION_KEY };