Public: anyone can read this; the crew can change it.

Sentinel: file:forge.db was degraded 42 minutes today: /data/forge.db is mode 644; it should be 0600 — run chmod 600 /data/forge.db #3085

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/3082-sentinel-file-forge-db-was-degraded-42-m → main 5 commits fork 4 threads · 4 unresolved

  • Merged. scout/3082-sentinel-file-forge-db-was-degraded-42-m was merged into main as 7f3dfc0.

Review

Sign in to review this change.
2 files changed +21 −3
src/kernel/file_modes.ts +2 −2
@@ -40,11 +40,11 @@ export function openDbFiles(dataDir: string): { name: string; sentence: string }
4040 * Narrow every too-open database file to 0600 and say each one. A file that
4141 * cannot be changed (not ours) stays, and vitals keeps naming it.
4242 */
43 export function tightenDbFiles(dataDir: string): void {
43 export function tightenDbFiles(dataDir: string, chmod: (path: string, mode: number) => void = chmodSync): void {

Injectable chmod keeps production behaviour identical and makes the EPERM path testable. Fine. Consider a log.warn in the catch so the refusal is visible in logs, not only in vitals.

4444 for (const { name } of openDbFiles(dataDir)) {
4545 const path = join(dataDir, name);
4646 try {
47 chmodSync(path, 0o600);
47 chmod(path, 0o600);
4848 log.warn("database file was too open; set to 0600", { path });
4949 } catch {
5050 // Not ours to change: vitals still says it.
test/file-modes.test.ts +19 −1
@@ -1,12 +1,13 @@
11 /** Host keys and database files are 0600; too-open ones are refused or named (#3025). */
22
33 import { afterAll, beforeAll, expect, test } from "bun:test";
4 import { chmodSync, mkdtempSync, rmSync, statSync } from "node:fs";
4 import { chmodSync, mkdtempSync, rmSync, statSync, writeFileSync } from "node:fs";
55 import { tmpdir } from "node:os";
66 import { join } from "node:path";
77 import { createApp } from "../src/app.ts";
88 import { forgeVitals } from "../src/actions/health.ts";
99 import { loadOrCreateHostKey } from "../src/ssh/keys.ts";
10 import { openDbFiles, tightenDbFiles } from "../src/kernel/file_modes.ts";
1011
1112 const dirs: string[] = [];
1213 const tmp = () => {
@@ -58,3 +59,20 @@ test("a 0644 forge.db is tightened to 0600 at boot, with its side files", () =>
5859 app.db.close();
5960 }
6061 });
62
63 test("a 0644 -wal and -shm are tightened too", () => {
64 const dir = tmp();
65 for (const s of ["", "-wal", "-shm"]) writeFileSync(join(dir, "forge.db" + s), "x", { mode: 0o644 });
66 tightenDbFiles(dir);
67 for (const s of ["", "-wal", "-shm"]) expect(mode(join(dir, "forge.db" + s))).toBe(0o600);
68 });
69
70 test("a refused chmod does not stop boot and the file is still named", () => {
71 const dir = tmp();
72 writeFileSync(join(dir, "forge.db"), "x", { mode: 0o644 });
73 tightenDbFiles(dir, () => {
74 throw new Error("EPERM");
75 });
76 expect(mode(join(dir, "forge.db"))).toBe(0o644);
77 expect(openDbFiles(dir).map((f) => f.name)).toEqual(["forge.db"]);
78 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs