@glassmkr/crucible 0.13.14 → 0.13.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +3 -3
  2. package/dist/alerts/__tests__/rules.test.js +34 -0
  3. package/dist/alerts/__tests__/rules.test.js.map +1 -1
  4. package/dist/alerts/rules.js +19 -2
  5. package/dist/alerts/rules.js.map +1 -1
  6. package/dist/collect/__tests__/smart.test.js +44 -0
  7. package/dist/collect/__tests__/smart.test.js.map +1 -1
  8. package/dist/collect/ethtool.js +2 -1
  9. package/dist/collect/ethtool.js.map +1 -1
  10. package/dist/collect/hardware-raid.js +6 -5
  11. package/dist/collect/hardware-raid.js.map +1 -1
  12. package/dist/collect/io-errors.js +2 -2
  13. package/dist/collect/io-errors.js.map +1 -1
  14. package/dist/collect/ipmi.js +6 -6
  15. package/dist/collect/ipmi.js.map +1 -1
  16. package/dist/collect/lvm.js +2 -7
  17. package/dist/collect/lvm.js.map +1 -1
  18. package/dist/collect/os-alerts.js +2 -1
  19. package/dist/collect/os-alerts.js.map +1 -1
  20. package/dist/collect/reboot-evidence.js +2 -2
  21. package/dist/collect/reboot-evidence.js.map +1 -1
  22. package/dist/collect/security.d.ts +3 -0
  23. package/dist/collect/security.js +87 -10
  24. package/dist/collect/security.js.map +1 -1
  25. package/dist/collect/smart.d.ts +1 -0
  26. package/dist/collect/smart.js +31 -2
  27. package/dist/collect/smart.js.map +1 -1
  28. package/dist/collect/zfs.js +3 -2
  29. package/dist/collect/zfs.js.map +1 -1
  30. package/dist/init.d.ts +16 -1
  31. package/dist/init.js +91 -3
  32. package/dist/init.js.map +1 -1
  33. package/dist/lib/__tests__/capability.test.js +46 -34
  34. package/dist/lib/__tests__/capability.test.js.map +1 -1
  35. package/dist/lib/__tests__/privileged.test.d.ts +1 -0
  36. package/dist/lib/__tests__/privileged.test.js +110 -0
  37. package/dist/lib/__tests__/privileged.test.js.map +1 -0
  38. package/dist/lib/capability.d.ts +19 -4
  39. package/dist/lib/capability.js +48 -46
  40. package/dist/lib/capability.js.map +1 -1
  41. package/dist/lib/privileged.d.ts +22 -0
  42. package/dist/lib/privileged.js +117 -0
  43. package/dist/lib/privileged.js.map +1 -0
  44. package/dist/lib/types.d.ts +4 -1
  45. package/dist/metrics-server.js +1 -0
  46. package/dist/metrics-server.js.map +1 -1
  47. package/dist/notify/telegram.js +1 -1
  48. package/dist/notify/telegram.js.map +1 -1
  49. package/package.json +1 -1
  50. package/rule-ids.json +1 -0
package/dist/init.js CHANGED
@@ -24,6 +24,8 @@ import * as fsDefault from "node:fs";
24
24
  import { execFileSync as execFileSyncDefault } from "node:child_process";
25
25
  import * as osDefault from "node:os";
26
26
  import * as pathDefault from "node:path";
27
+ import { SERVICE_USER, WRAPPER_PATH, WRAPPER_SCRIPT, SUDOERS_PATH, SUDOERS_CONTENT, } from "./lib/privileged.js";
28
+ export const STATE_DIRS = ["/var/lib/glassmkr", "/var/lib/crucible"];
27
29
  export const DEFAULT_INGEST_URL = "https://app.glassmkr.com/api/v1/ingest";
28
30
  export const DEFAULT_CONFIG_PATH = "/etc/glassmkr/crucible.yaml";
29
31
  export const LEGACY_CONFIG_PATH = "/etc/glassmkr/collector.yaml";
@@ -58,7 +60,7 @@ export function buildCollectorYaml(serverName, ingestUrl, apiKey) {
58
60
  function escapeDoubleQuoted(s) {
59
61
  return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
60
62
  }
61
- export function buildSystemdUnit(binPath, configPath) {
63
+ export function buildSystemdUnit(binPath, configPath, user = "root") {
62
64
  return [
63
65
  `[Unit]`,
64
66
  `Description=Glassmkr Crucible - Bare Metal Monitoring`,
@@ -66,7 +68,10 @@ export function buildSystemdUnit(binPath, configPath) {
66
68
  ``,
67
69
  `[Service]`,
68
70
  `Type=simple`,
69
- `User=root`,
71
+ // §2.1: run unprivileged when privilege separation is in place; the
72
+ // agent escalates only through the sudo wrapper. Falls back to root
73
+ // when setup didn't complete, so collection never silently breaks.
74
+ `User=${user}`,
70
75
  `ExecStart=${binPath} ${configPath}`,
71
76
  `Restart=always`,
72
77
  `RestartSec=10`,
@@ -76,6 +81,83 @@ export function buildSystemdUnit(binPath, configPath) {
76
81
  ``,
77
82
  ].join("\n");
78
83
  }
84
+ /**
85
+ * Establish the §2.1 privilege boundary: an unprivileged `glassmkr` system
86
+ * user that escalates only through the sudo wrapper. Idempotent; safe to run
87
+ * on every install/upgrade. Returns true iff the box is fully ready to run
88
+ * the service as `glassmkr` (user + wrapper + valid sudoers + writable state).
89
+ * On ANY hard failure it returns false and the caller keeps the unit on
90
+ * User=root, so an install where this can't complete never loses collection.
91
+ *
92
+ * Ordering matters for the root->unprivileged migration: the wrapper and
93
+ * sudoers are installed BEFORE the unit flips to User=glassmkr (done by the
94
+ * caller), so there is never a window where the agent is unprivileged but the
95
+ * escalation path is absent.
96
+ */
97
+ export function setupPrivilegeSeparation(deps, configPath) {
98
+ // 1. Service user (system, no login, no home).
99
+ const idr = deps.exec("id", ["-u", SERVICE_USER]);
100
+ if (idr.status !== 0) {
101
+ const ur = deps.exec("useradd", [
102
+ "--system", "--no-create-home", "--shell", "/usr/sbin/nologin", SERVICE_USER,
103
+ ]);
104
+ if (ur.status !== 0) {
105
+ deps.warn(`[init] could not create '${SERVICE_USER}' user (status=${ur.status}); staying on User=root.`);
106
+ return false;
107
+ }
108
+ deps.log(`[init] created system user '${SERVICE_USER}'.`);
109
+ }
110
+ // 2. The collection wrapper (root:root 0755 — must NOT be writable by the
111
+ // service user, or the sudo grant could be hijacked).
112
+ try {
113
+ deps.fs.writeFileSync(WRAPPER_PATH, WRAPPER_SCRIPT, { mode: 0o755 });
114
+ deps.fs.chmodSync(WRAPPER_PATH, 0o755);
115
+ }
116
+ catch (err) {
117
+ deps.warn(`[init] could not install wrapper ${WRAPPER_PATH}: ${err?.message ?? err}; staying on User=root.`);
118
+ return false;
119
+ }
120
+ // 3. Sudoers drop-in, validated with `visudo -cf` on a temp file BEFORE it
121
+ // goes live (a malformed sudoers file breaks sudo host-wide).
122
+ const sudoersTmp = `${SUDOERS_PATH}.tmp`;
123
+ try {
124
+ deps.fs.writeFileSync(sudoersTmp, SUDOERS_CONTENT, { mode: 0o440 });
125
+ const chk = deps.exec("visudo", ["-cf", sudoersTmp]);
126
+ if (chk.status !== 0) {
127
+ deps.warn(`[init] visudo rejected the sudoers drop-in (status=${chk.status}); staying on User=root.`);
128
+ return false;
129
+ }
130
+ deps.fs.renameSync(sudoersTmp, SUDOERS_PATH);
131
+ deps.fs.chmodSync(SUDOERS_PATH, 0o440);
132
+ }
133
+ catch (err) {
134
+ deps.warn(`[init] could not install sudoers ${SUDOERS_PATH}: ${err?.message ?? err}; staying on User=root.`);
135
+ return false;
136
+ }
137
+ // 4. State dirs the agent writes directly (alert-state, reboot-marker) +
138
+ // the config it reads: hand ownership to the service user.
139
+ for (const dir of STATE_DIRS) {
140
+ try {
141
+ deps.fs.mkdirSync(dir, { recursive: true, mode: 0o750 });
142
+ }
143
+ catch { /* exists */ }
144
+ const ch = deps.exec("chown", ["-R", `${SERVICE_USER}:${SERVICE_USER}`, dir]);
145
+ if (ch.status !== 0) {
146
+ deps.warn(`[init] could not chown ${dir} to ${SERVICE_USER} (status=${ch.status}); staying on User=root.`);
147
+ return false;
148
+ }
149
+ }
150
+ const chc = deps.exec("chown", [`${SERVICE_USER}:${SERVICE_USER}`, configPath]);
151
+ if (chc.status !== 0) {
152
+ deps.warn(`[init] could not chown ${configPath} to ${SERVICE_USER}; staying on User=root.`);
153
+ return false;
154
+ }
155
+ // 5. adm group: several collectors read log-adjacent paths directly. Best
156
+ // effort; not fatal (the wrapper covers the binary paths regardless).
157
+ deps.exec("usermod", ["-aG", "adm", SERVICE_USER]);
158
+ deps.log(`[init] privilege separation ready: service will run as '${SERVICE_USER}' via ${WRAPPER_PATH}.`);
159
+ return true;
160
+ }
79
161
  /**
80
162
  * Run the init flow with caller-provided IO. Returns the exit code
81
163
  * (0 = success). Pure orchestration; no side effects on `process`.
@@ -216,8 +298,14 @@ export async function runInit(opts, deps) {
216
298
  deps.error(`[init] could not locate the glassmkr-crucible binary on PATH. Make sure 'npm install -g @glassmkr/crucible' completed before running init.`);
217
299
  return 7;
218
300
  }
301
+ // §2.1: establish the unprivileged-user + sudo-wrapper boundary. Runs
302
+ // AFTER the config is written (so it can hand ownership to the service
303
+ // user) and BEFORE the unit is written (so the escalation path exists
304
+ // before the unit flips to User=glassmkr). Fail-safe: on any hard failure
305
+ // this returns false and the unit stays on User=root.
306
+ const serviceUser = setupPrivilegeSeparation(deps, configPath) ? SERVICE_USER : "root";
219
307
  // Write systemd unit (0644).
220
- const unit = buildSystemdUnit(binPath, configPath);
308
+ const unit = buildSystemdUnit(binPath, configPath, serviceUser);
221
309
  try {
222
310
  deps.fs.writeFileSync(SYSTEMD_UNIT_PATH, unit, { mode: 0o644 });
223
311
  deps.fs.chmodSync(SYSTEMD_UNIT_PATH, 0o644);
package/dist/init.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -1,56 +1,68 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { detectIpmiCapability, formatCapabilityLine } from "../capability.js";
2
+ import { detectIpmiCapability, formatCapabilityLine, isIpmitoolVersionVulnerable, } from "../capability.js";
3
+ describe("isIpmitoolVersionVulnerable (CVE-2020-5208, fixed in 1.8.19)", () => {
4
+ it("flags versions strictly below 1.8.19", () => {
5
+ expect(isIpmitoolVersionVulnerable("1.8.18")).toBe(true);
6
+ expect(isIpmitoolVersionVulnerable("1.8.11")).toBe(true);
7
+ expect(isIpmitoolVersionVulnerable("1.7.99")).toBe(true);
8
+ });
9
+ it("clears 1.8.19 and above", () => {
10
+ expect(isIpmitoolVersionVulnerable("1.8.19")).toBe(false);
11
+ expect(isIpmitoolVersionVulnerable("1.8.20")).toBe(false);
12
+ expect(isIpmitoolVersionVulnerable("1.9.0")).toBe(false);
13
+ expect(isIpmitoolVersionVulnerable("2.0.0")).toBe(false);
14
+ });
15
+ it("does not gate an unknown/unparseable version (fail-open on identification)", () => {
16
+ expect(isIpmitoolVersionVulnerable(null)).toBe(false);
17
+ expect(isIpmitoolVersionVulnerable("")).toBe(false);
18
+ expect(isIpmitoolVersionVulnerable("unknown")).toBe(false);
19
+ });
20
+ it("tolerates distro version suffixes", () => {
21
+ expect(isIpmitoolVersionVulnerable("1.8.18-11ubuntu2")).toBe(true);
22
+ expect(isIpmitoolVersionVulnerable("1.8.19-4")).toBe(false);
23
+ });
24
+ });
3
25
  describe("detectIpmiCapability", () => {
4
26
  it("returns no_ipmitool_binary when neither device nor binary exist (Pi)", async () => {
5
27
  const cap = await detectIpmiCapability({
6
- statDevice: async () => "enoent",
7
28
  runIpmitool: async () => { const e = new Error("not found"); e.code = "ENOENT"; throw e; },
29
+ probeSensor: async () => null,
8
30
  });
9
31
  expect(cap).toEqual({ available: false, reason: "no_ipmitool_binary" });
10
32
  });
11
- it("returns available when /dev/ipmi0 exists and ipmitool runs", async () => {
33
+ it("available when the WRAPPED sensor probe returns BMC data (CVE-safe version)", async () => {
34
+ // §2.1: availability comes from the wrapper (runs as root), not a direct
35
+ // /dev/ipmi0 stat the unprivileged service user can't do.
12
36
  const cap = await detectIpmiCapability({
13
- statDevice: async (p) => p === "/dev/ipmi0" ? "ok" : "enoent",
14
- runIpmitool: async () => ({ stdout: "ipmitool version 1.8.18\n", stderr: "" }),
37
+ runIpmitool: async () => ({ stdout: "ipmitool version 1.8.19\n", stderr: "" }),
38
+ probeSensor: async () => "CPU Temp | 39.000 | degrees C | ok",
15
39
  });
16
- expect(cap).toEqual({ available: true, method: "ipmitool_in_band", ipmitool_version: "1.8.18" });
40
+ expect(cap).toEqual({ available: true, method: "ipmitool_in_band", ipmitool_version: "1.8.19" });
17
41
  });
18
- it("returns no_bmc_device when ipmitool exists but device is missing AND sensor probe fails", async () => {
42
+ it("disables IPMI when ipmitool is below 1.8.19 even if the BMC answers (CVE-2020-5208)", async () => {
19
43
  const cap = await detectIpmiCapability({
20
- statDevice: async () => "enoent",
21
- runIpmitool: async (args) => {
22
- if (args[0] === "-V")
23
- return { stdout: "ipmitool version 1.8.18", stderr: "" };
24
- const e = new Error("Could not open device");
25
- e.stderr = "Could not open device at /dev/ipmi0: No such file or directory";
26
- throw e;
27
- },
44
+ runIpmitool: async () => ({ stdout: "ipmitool version 1.8.18\n", stderr: "" }),
45
+ probeSensor: async () => "CPU Temp | 39.000 | degrees C | ok",
28
46
  });
29
47
  expect(cap.available).toBe(false);
30
- if (!cap.available)
31
- expect(cap.reason).toBe("no_bmc_device");
48
+ if (!cap.available) {
49
+ expect(cap.reason).toBe("ipmitool_cve_2020_5208");
50
+ expect(cap.detail).toContain("1.8.18");
51
+ }
32
52
  });
33
- it("returns permission_denied on EACCES", async () => {
53
+ it("no_bmc_device when the wrapped sensor probe returns nothing (no BMC / VM)", async () => {
34
54
  const cap = await detectIpmiCapability({
35
- statDevice: async () => "eacces",
36
- runIpmitool: async () => ({ stdout: "ipmitool version 1.8.18", stderr: "" }),
55
+ runIpmitool: async () => ({ stdout: "ipmitool version 1.8.19", stderr: "" }),
56
+ probeSensor: async () => null,
37
57
  });
38
58
  expect(cap.available).toBe(false);
39
- if (!cap.available) {
40
- expect(cap.reason).toBe("permission_denied");
41
- expect(cap.detail).toContain("ipmi");
42
- }
59
+ if (!cap.available)
60
+ expect(cap.reason).toBe("no_bmc_device");
43
61
  });
44
- it("VM with ipmitool installed but no virtual BMC: no_bmc_device", async () => {
62
+ it("no_bmc_device on empty (whitespace-only) sensor output", async () => {
45
63
  const cap = await detectIpmiCapability({
46
- statDevice: async () => "enoent",
47
- runIpmitool: async (args) => {
48
- if (args[0] === "-V")
49
- return { stdout: "ipmitool version 1.8.19", stderr: "" };
50
- const e = new Error("could not open");
51
- e.stderr = "Could not open device";
52
- throw e;
53
- },
64
+ runIpmitool: async () => ({ stdout: "ipmitool version 1.8.19", stderr: "" }),
65
+ probeSensor: async () => " \n ",
54
66
  });
55
67
  expect(cap.available).toBe(false);
56
68
  if (!cap.available)
@@ -58,8 +70,8 @@ describe("detectIpmiCapability", () => {
58
70
  });
59
71
  it("captures ipmitool version when present", async () => {
60
72
  const cap = await detectIpmiCapability({
61
- statDevice: async (p) => p === "/dev/ipmi0" ? "ok" : "enoent",
62
73
  runIpmitool: async () => ({ stdout: "ipmitool version 1.8.21-rc1\n", stderr: "" }),
74
+ probeSensor: async () => "CPU Temp | 39.000 | degrees C | ok",
63
75
  });
64
76
  expect(cap.available).toBe(true);
65
77
  if (cap.available)
@@ -1 +1 @@
1
- {"version":3,"file":"capability.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/capability.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capability.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/capability.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isAllowedSmartDevice, isAllowedIface, WRAPPER_SCRIPT, SUDOERS_CONTENT, WRAPPER_PATH, SUDOERS_PATH, SERVICE_USER, } from "../privileged.js";
3
+ import { setupPrivilegeSeparation } from "../../init.js";
4
+ function mockDeps(execImpl) {
5
+ const files = new Map();
6
+ const warns = [];
7
+ const deps = {
8
+ fs: {
9
+ existsSync: (p) => files.has(p),
10
+ mkdirSync: () => { },
11
+ writeFileSync: (p, data, o) => { files.set(p, { data, mode: o?.mode ?? 0o644 }); },
12
+ chmodSync: (p, mode) => { const f = files.get(p); if (f)
13
+ f.mode = mode; },
14
+ renameSync: (from, to) => {
15
+ const f = files.get(from);
16
+ if (!f)
17
+ throw new Error(`ENOENT: ${from}`);
18
+ files.set(to, f);
19
+ files.delete(from);
20
+ },
21
+ },
22
+ exec: execImpl,
23
+ hostname: () => "h", log: () => { }, warn: (m) => warns.push(m), error: () => { },
24
+ fetch: async () => ({ status: 200 }), readStdin: async () => "",
25
+ };
26
+ return { deps, files, warns };
27
+ }
28
+ // A fresh box: `id glassmkr` fails (user absent), everything else succeeds.
29
+ const okExec = (cmd) => cmd === "id"
30
+ ? { stdout: "", status: 1 }
31
+ : { stdout: "", status: 0 };
32
+ describe("setupPrivilegeSeparation", () => {
33
+ it("succeeds on a clean box: creates user, installs wrapper 0755 + sudoers 0440", () => {
34
+ const { deps, files } = mockDeps(okExec);
35
+ expect(setupPrivilegeSeparation(deps, "/etc/glassmkr/crucible.yaml")).toBe(true);
36
+ expect(files.get(WRAPPER_PATH)?.mode).toBe(0o755);
37
+ expect(files.get(SUDOERS_PATH)?.mode).toBe(0o440);
38
+ expect(files.has(`${SUDOERS_PATH}.tmp`)).toBe(false); // renamed, not left behind
39
+ });
40
+ it("fail-safe: visudo rejection returns false and never installs the sudoers file", () => {
41
+ const { deps, files } = mockDeps((cmd) => cmd === "id" ? { stdout: "", status: 1 } :
42
+ cmd === "visudo" ? { stdout: "", status: 1 } :
43
+ { stdout: "", status: 0 });
44
+ expect(setupPrivilegeSeparation(deps, "/etc/glassmkr/crucible.yaml")).toBe(false);
45
+ expect(files.has(SUDOERS_PATH)).toBe(false);
46
+ });
47
+ it("fail-safe: useradd failure returns false (caller keeps User=root)", () => {
48
+ const { deps } = mockDeps((cmd) => cmd === "id" ? { stdout: "", status: 1 } :
49
+ cmd === "useradd" ? { stdout: "", status: 1 } :
50
+ { stdout: "", status: 0 });
51
+ expect(setupPrivilegeSeparation(deps, "/etc/glassmkr/crucible.yaml")).toBe(false);
52
+ });
53
+ });
54
+ describe("isAllowedSmartDevice", () => {
55
+ it("accepts real block/nvme device nodes", () => {
56
+ for (const d of ["/dev/sda", "/dev/sdb", "/dev/sdaa", "/dev/hda", "/dev/nvme0n1", "/dev/nvme12n3", "/dev/nvme0", "/dev/bus/0"]) {
57
+ expect(isAllowedSmartDevice(d)).toBe(true);
58
+ }
59
+ });
60
+ it("rejects traversal, injection, and non-device paths", () => {
61
+ for (const d of ["/etc/shadow", "/dev/../etc/shadow", "/dev/sda; rm -rf /", "/dev/sda foo", "", "sda", "/dev/sda\n/dev/sdb", "/dev/mapper/x"]) {
62
+ expect(isAllowedSmartDevice(d)).toBe(false);
63
+ }
64
+ });
65
+ });
66
+ describe("isAllowedIface", () => {
67
+ it("accepts real interface names", () => {
68
+ for (const i of ["eth0", "enp1s0", "eno1", "bond0", "bond0.100", "br-lan", "en_p0"]) {
69
+ expect(isAllowedIface(i)).toBe(true);
70
+ }
71
+ });
72
+ it("rejects empty, over-long, spaced, and injection-y names", () => {
73
+ for (const i of ["", "a".repeat(16), "eth0 x", "eth0;rm", "../eth0", "eth0|cat", "$(id)"]) {
74
+ expect(isAllowedIface(i)).toBe(false);
75
+ }
76
+ });
77
+ });
78
+ describe("WRAPPER_SCRIPT (the sudo facade)", () => {
79
+ it("is POSIX sh with strict mode", () => {
80
+ expect(WRAPPER_SCRIPT.startsWith("#!/bin/sh")).toBe(true);
81
+ expect(WRAPPER_SCRIPT).toContain("set -eu");
82
+ });
83
+ it("has no argument passthrough sink ($@ / $* into a command)", () => {
84
+ // The only $-expansions should be the validated $dev / $ifc / $action /
85
+ // ${#1}. A bare exec of "$@" or "$*" would be a passthrough hole.
86
+ expect(WRAPPER_SCRIPT).not.toMatch(/exec[^\n]*"\$@"/);
87
+ expect(WRAPPER_SCRIPT).not.toMatch(/exec[^\n]*\$\*/);
88
+ });
89
+ it("validates the two parameterised actions before exec", () => {
90
+ expect(WRAPPER_SCRIPT).toContain("valid_device");
91
+ expect(WRAPPER_SCRIPT).toContain("valid_iface");
92
+ });
93
+ it("hard-codes the expected privileged commands", () => {
94
+ expect(WRAPPER_SCRIPT).toContain("exec ipmitool sensor");
95
+ expect(WRAPPER_SCRIPT).toContain("exec smartctl --json --all");
96
+ expect(WRAPPER_SCRIPT).toContain("exec zpool status");
97
+ expect(WRAPPER_SCRIPT).toContain("exec iptables -L -n");
98
+ });
99
+ });
100
+ describe("SUDOERS_CONTENT", () => {
101
+ it("grants only the wrapper, NOPASSWD, to the service user", () => {
102
+ expect(SUDOERS_CONTENT).toContain(`${SERVICE_USER} ALL=(root) NOPASSWD: ${WRAPPER_PATH}`);
103
+ });
104
+ it("contains no wildcard and resets the environment", () => {
105
+ expect(SUDOERS_CONTENT).not.toContain("*");
106
+ expect(SUDOERS_CONTENT).toContain("env_reset");
107
+ expect(SUDOERS_CONTENT).toContain("secure_path");
108
+ });
109
+ });
110
+ //# sourceMappingURL=privileged.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privileged.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/privileged.test.ts"],"names":[],"mappings":"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"}
@@ -4,17 +4,32 @@ export type IpmiCapability = {
4
4
  ipmitool_version: string | null;
5
5
  } | {
6
6
  available: false;
7
- reason: "no_ipmitool_binary" | "no_bmc_device" | "execution_failed" | "permission_denied";
7
+ reason: "no_ipmitool_binary" | "no_bmc_device" | "execution_failed" | "permission_denied" | "ipmitool_cve_2020_5208";
8
8
  detail?: string;
9
9
  };
10
+ export declare const MIN_SAFE_IPMITOOL_VERSION = "1.8.19";
11
+ /**
12
+ * True iff `version` is a parseable ipmitool version strictly below
13
+ * MIN_SAFE_IPMITOOL_VERSION. Unknown/unparseable versions return false
14
+ * (we do not disable a capability we cannot positively identify as
15
+ * vulnerable). Pure; unit-tested.
16
+ */
17
+ export declare function isIpmitoolVersionVulnerable(version: string | null): boolean;
10
18
  interface DetectDeps {
11
- /** Override for tests. Returns "ok" | "enoent" | "eacces" per path. */
12
- statDevice?: (path: string) => Promise<"ok" | "enoent" | "eacces">;
13
- /** Override for tests. Returns stdout or throws with err.code. */
19
+ /** Override for tests. Runs `ipmitool -V` (direct; works unprivileged).
20
+ * Returns stdout or throws with err.code. Used only for the version /
21
+ * CVE gate. */
14
22
  runIpmitool?: (args: string[]) => Promise<{
15
23
  stdout: string;
16
24
  stderr: string;
17
25
  }>;
26
+ /** Override for tests. The WRAPPED sensor probe (`sudo crucible-collect
27
+ * ipmi-sensor`). Returns stdout, or null on any failure. This is the
28
+ * availability signal: under the §2.1 unprivileged model the agent user
29
+ * cannot stat /dev/ipmi0 or run ipmitool directly, so a direct
30
+ * device-node probe is meaningless; the wrapper (which runs as root) is
31
+ * the real reachability test. */
32
+ probeSensor?: () => Promise<string | null>;
18
33
  }
19
34
  export declare function detectIpmiCapability(deps?: DetectDeps): Promise<IpmiCapability>;
20
35
  export declare function formatCapabilityLine(cap: IpmiCapability): string;
@@ -8,50 +8,49 @@
8
8
  //
9
9
  // This module probes once at startup and caches the result. collectIpmi()
10
10
  // reads the cached capability and short-circuits when unavailable.
11
- import { promises as fs } from "node:fs";
12
11
  import { execFile } from "node:child_process";
13
12
  import { promisify } from "node:util";
13
+ import { runPrivileged } from "./privileged.js";
14
14
  const execFileAsync = promisify(execFile);
15
- const DEVICE_CANDIDATES = ["/dev/ipmi0", "/dev/ipmi/0", "/dev/ipmidev/0"];
16
- async function defaultStatDevice(path) {
17
- try {
18
- await fs.access(path, fs.constants.R_OK);
19
- return "ok";
20
- }
21
- catch (err) {
22
- if (err.code === "EACCES" || err.code === "EPERM")
23
- return "eacces";
24
- return "enoent";
15
+ // CVE-2020-5208 (GHSA-g659-9qxw-p7cp): heap overflow in ipmitool's
16
+ // read_fru_area_section and related parsers when handling data received
17
+ // from a remote LAN party (a malicious or compromised BMC). Fixed in
18
+ // ipmitool 1.8.19. The agent runs ipmitool in-band against the host BMC,
19
+ // so a compromised BMC parsing path is a real local-blast-radius primitive
20
+ // (audit §2.1 / catalog T-202). Below the fix version we mark IPMI
21
+ // unavailable rather than feed BMC output to a vulnerable parser.
22
+ export const MIN_SAFE_IPMITOOL_VERSION = "1.8.19";
23
+ /**
24
+ * True iff `version` is a parseable ipmitool version strictly below
25
+ * MIN_SAFE_IPMITOOL_VERSION. Unknown/unparseable versions return false
26
+ * (we do not disable a capability we cannot positively identify as
27
+ * vulnerable). Pure; unit-tested.
28
+ */
29
+ export function isIpmitoolVersionVulnerable(version) {
30
+ if (!version)
31
+ return false;
32
+ const min = [1, 8, 19];
33
+ const parts = version.split(/[.\-+]/).map((p) => parseInt(p, 10));
34
+ if (parts.length === 0 || Number.isNaN(parts[0]))
35
+ return false; // unparseable
36
+ for (let i = 0; i < min.length; i++) {
37
+ const p = Number.isNaN(parts[i]) ? 0 : (parts[i] ?? 0);
38
+ if (p < min[i])
39
+ return true;
40
+ if (p > min[i])
41
+ return false;
25
42
  }
43
+ return false; // equal or greater
26
44
  }
27
45
  async function defaultRunIpmitool(args) {
28
46
  const { stdout, stderr } = await execFileAsync("ipmitool", args, { timeout: 2000 });
29
47
  return { stdout, stderr };
30
48
  }
31
49
  export async function detectIpmiCapability(deps = {}) {
32
- const statDevice = deps.statDevice ?? defaultStatDevice;
33
50
  const runIpmitool = deps.runIpmitool ?? defaultRunIpmitool;
34
- // Step 1: probe /dev/ipmi* device nodes.
35
- let deviceFound = false;
36
- let permissionDenied = false;
37
- for (const path of DEVICE_CANDIDATES) {
38
- const result = await statDevice(path);
39
- if (result === "ok") {
40
- deviceFound = true;
41
- break;
42
- }
43
- if (result === "eacces") {
44
- permissionDenied = true;
45
- }
46
- }
47
- if (permissionDenied && !deviceFound) {
48
- return {
49
- available: false,
50
- reason: "permission_denied",
51
- detail: "/dev/ipmi0 exists but is not readable; run as root or add user to ipmi group",
52
- };
53
- }
54
- // Step 2: probe ipmitool binary.
51
+ const probeSensor = deps.probeSensor ?? (() => runPrivileged("ipmi-sensor"));
52
+ // Step 1: probe the ipmitool binary + version. `ipmitool -V` needs no BMC
53
+ // access, so it works even as the unprivileged service user.
55
54
  let ipmitoolVersion = null;
56
55
  try {
57
56
  const { stdout } = await runIpmitool(["-V"]);
@@ -68,26 +67,28 @@ export async function detectIpmiCapability(deps = {}) {
68
67
  detail: String(err?.stderr ?? err?.message ?? err).split("\n")[0]?.slice(0, 200),
69
68
  };
70
69
  }
71
- // Step 3: device + binary present assume capable.
72
- if (deviceFound) {
73
- return { available: true, method: "ipmitool_in_band", ipmitool_version: ipmitoolVersion };
70
+ // Step 2: version gate (CVE-2020-5208): refuse to feed BMC output to a
71
+ // vulnerable ipmitool parser. Fail-closed for the IPMI capability only;
72
+ // every other collector is unaffected. Skipped when the version could
73
+ // not be parsed (we do not disable a capability we cannot positively
74
+ // identify as vulnerable).
75
+ if (isIpmitoolVersionVulnerable(ipmitoolVersion)) {
76
+ return {
77
+ available: false,
78
+ reason: "ipmitool_cve_2020_5208",
79
+ detail: `ipmitool ${ipmitoolVersion} < ${MIN_SAFE_IPMITOOL_VERSION}; upgrade to close CVE-2020-5208`,
80
+ };
74
81
  }
75
- // Step 4: binary present but no device node try one sensor probe to
76
- // disambiguate (some kernels expose IPMI through unconventional paths).
82
+ // Step 3: reachability via the WRAPPED sensor probe. Non-empty output means
83
+ // the BMC answered; empty/failure means no usable BMC on this host.
77
84
  try {
78
- const { stdout, stderr } = await runIpmitool(["sensor"]);
79
- const out = stdout || "";
80
- const errOut = (stderr || "").toLowerCase();
81
- if (out.trim().length > 0 && !errOut.includes("could not open")) {
85
+ const out = await probeSensor();
86
+ if (out && out.trim().length > 0) {
82
87
  return { available: true, method: "ipmitool_in_band", ipmitool_version: ipmitoolVersion };
83
88
  }
84
89
  return { available: false, reason: "no_bmc_device" };
85
90
  }
86
91
  catch (err) {
87
- const stderr = String(err?.stderr ?? "").toLowerCase();
88
- if (stderr.includes("could not open")) {
89
- return { available: false, reason: "no_bmc_device" };
90
- }
91
92
  return {
92
93
  available: false,
93
94
  reason: "execution_failed",
@@ -105,6 +106,7 @@ export function formatCapabilityLine(cap) {
105
106
  case "no_bmc_device": return "IPMI: not available (no /dev/ipmi*, BMC not detected)";
106
107
  case "permission_denied": return `IPMI: not available (${cap.detail ?? "permission denied"})`;
107
108
  case "execution_failed": return `IPMI: not available (execution failed${cap.detail ? `: ${cap.detail}` : ""})`;
109
+ case "ipmitool_cve_2020_5208": return `IPMI: disabled (${cap.detail ?? "ipmitool too old (CVE-2020-5208)"})`;
108
110
  }
109
111
  }
110
112
  //# sourceMappingURL=capability.js.map