key sync: read CREDS_KEY/CREDS_IV from the volume, add a failure diagnostic
The first scheduled run failed closed (good) — Cloudron's scheduler addon runs each task in its OWN container, so the app's process env is absent there and /proc/1/environ belongs to the task launcher, not to LibreChat. CREDS_KEY/CREDS_IV now come from /app/data/.librechat-creds (root:cloudron 0640), written from the live app env; a stale key makes the decrypt-against-existing-rows guard refuse to write rather than corrupt member keys. Credential failure now also writes /app/data/key-sync-diag.txt so a future failure explains itself. Verified: the 22:40 scheduled fire logged 'creds check: decrypt-ok (25 existing row(s))' / 'done: 50 seeded, 15 skipped'.
This commit is contained in:
1 parent
4e28b7a612
commit
e544dc4a11
1 file changed
+32
-1
+32
-1
@@ -20,6 +20,13 @@
|
|||||||
* it refuses to write unless CREDS_KEY can first decrypt an existing key row
|
* it refuses to write unless CREDS_KEY can first decrypt an existing key row
|
||||||
* (proof it is the app's real key), and unless each fresh ciphertext round-trips
|
* (proof it is the app's real key), and unless each fresh ciphertext round-trips
|
||||||
* back to the plaintext.
|
* back to the plaintext.
|
||||||
|
*
|
||||||
|
* CREDS_KEY/CREDS_IV: a scheduled run happens in the scheduler's own container,
|
||||||
|
* where the app's process env does not exist (/proc/1/environ there belongs to
|
||||||
|
* the task launcher). They are read from /app/data/.librechat-creds
|
||||||
|
* (root:cloudron 0640) instead. If the app's key ever rotates, refresh that file
|
||||||
|
* — a stale key makes the decrypt guard refuse to write rather than corrupt
|
||||||
|
* member keys.
|
||||||
*/
|
*/
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const os = require("os");
|
const os = require("os");
|
||||||
@@ -52,8 +59,32 @@ const MCP_AUTH_FIELD = "IC_API_KEY";
|
|||||||
if (!MONGO_URL) { console.error("FATAL: no MongoDB URL in environment"); process.exit(1); }
|
if (!MONGO_URL) { console.error("FATAL: no MongoDB URL in environment"); process.exit(1); }
|
||||||
if (!SYNC_TOKEN) { console.error("FATAL: LIBRECHAT_SYNC_TOKEN not set"); process.exit(1); }
|
if (!SYNC_TOKEN) { console.error("FATAL: LIBRECHAT_SYNC_TOKEN not set"); process.exit(1); }
|
||||||
|
|
||||||
|
// Scheduler tasks run in their OWN container (Cloudron's scheduler addon): the
|
||||||
|
// app's process env is absent and /proc/1/environ belongs to the task launcher,
|
||||||
|
// not the app. So the app's CREDS_KEY/CREDS_IV are read from a root- and
|
||||||
|
// group-readable file on the shared volume, written from the live app env.
|
||||||
|
// If it is missing or stale, the guards below stop the run instead of writing
|
||||||
|
// keys the app cannot decrypt.
|
||||||
|
const CREDS_FILE = "/app/data/.librechat-creds";
|
||||||
|
try {
|
||||||
|
for (const line of fs.readFileSync(CREDS_FILE, "utf8").split("\n")) {
|
||||||
|
const m = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
||||||
|
if (m && /^CREDS_(KEY|IV)$/.test(m[1]) && process.env[m[1]] === undefined) process.env[m[1]] = m[2].trim();
|
||||||
|
}
|
||||||
|
} catch (e) { /* absent is fine — the guard below reports it */ }
|
||||||
|
|
||||||
if (!process.env.CREDS_KEY || !process.env.CREDS_IV) {
|
if (!process.env.CREDS_KEY || !process.env.CREDS_IV) {
|
||||||
console.error("FATAL: CREDS_KEY/CREDS_IV not available — refusing to write (keys would be undecryptable)");
|
const diag = [
|
||||||
|
`when=${new Date().toISOString()}`,
|
||||||
|
`uid=${typeof process.getuid === "function" ? process.getuid() : "?"} gid=${typeof process.getgid === "function" ? process.getgid() : "?"}`,
|
||||||
|
`cwd=${process.cwd()}`,
|
||||||
|
`creds_file_exists=${fs.existsSync(CREDS_FILE)}`,
|
||||||
|
`proc1_environ_readable=${(() => { try { return fs.readFileSync("/proc/1/environ").length > 0; } catch (e) { return false; } })()}`,
|
||||||
|
`env_names_with_creds=${Object.keys(process.env).filter((k) => /CREDS/i.test(k)).join(",") || "none"}`,
|
||||||
|
`volume_mounted=${fs.existsSync("/app/data")}`,
|
||||||
|
].join("\n");
|
||||||
|
try { fs.writeFileSync("/app/data/key-sync-diag.txt", diag + "\n"); } catch (e) { /* volume not mounted */ }
|
||||||
|
console.error("FATAL: CREDS_KEY/CREDS_IV not available — refusing to write (keys would be undecryptable); diag: /app/data/key-sync-diag.txt");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user