@glassmkr/crucible 0.1.0 → 0.3.1
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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- package/.github/ISSUE_TEMPLATE/no_data.md +26 -0
- package/README.md +48 -52
- package/dist/alerts/rules.js +120 -0
- package/dist/alerts/rules.js.map +1 -1
- package/dist/collect/cpu.js +57 -16
- package/dist/collect/cpu.js.map +1 -1
- package/dist/collect/disks.js +48 -2
- package/dist/collect/disks.js.map +1 -1
- package/dist/collect/ipmi.js +160 -2
- package/dist/collect/ipmi.js.map +1 -1
- package/dist/collect/security.d.ts +39 -0
- package/dist/collect/security.js +176 -0
- package/dist/collect/security.js.map +1 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +48 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +64 -0
- package/dist/lib/version-check.d.ts +7 -0
- package/dist/lib/version-check.js +39 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/metrics-server.d.ts +3 -0
- package/dist/metrics-server.js +113 -0
- package/dist/metrics-server.js.map +1 -0
- package/dist/notify/telegram.js +27 -9
- package/dist/notify/telegram.js.map +1 -1
- package/dist/push/forge.d.ts +1 -0
- package/dist/push/forge.js +80 -4
- package/dist/push/forge.js.map +1 -1
- package/package.json +1 -1
- package/scripts/sign-release.sh +29 -0
- package/src/alerts/rules.ts +99 -0
- package/src/collect/cpu.ts +64 -16
- package/src/collect/disks.ts +57 -2
- package/src/collect/ipmi.ts +147 -3
- package/src/collect/security.ts +238 -0
- package/src/config.ts +5 -0
- package/src/index.ts +49 -3
- package/src/lib/types.ts +44 -0
- package/src/lib/version-check.ts +38 -0
- package/src/metrics-server.ts +123 -0
- package/src/notify/telegram.ts +29 -9
- package/src/push/forge.ts +89 -5
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -10,6 +10,39 @@ export interface Snapshot {
|
|
|
10
10
|
raid: RaidInfo[];
|
|
11
11
|
ipmi: IpmiInfo;
|
|
12
12
|
os_alerts: OsAlerts;
|
|
13
|
+
security?: SecurityData;
|
|
14
|
+
}
|
|
15
|
+
export interface SecurityData {
|
|
16
|
+
ssh: {
|
|
17
|
+
permitRootLogin: string;
|
|
18
|
+
passwordAuthentication: string;
|
|
19
|
+
rootPasswordExposed: boolean;
|
|
20
|
+
} | null;
|
|
21
|
+
firewall: {
|
|
22
|
+
active: boolean;
|
|
23
|
+
source: string;
|
|
24
|
+
details: string;
|
|
25
|
+
};
|
|
26
|
+
pending_updates: {
|
|
27
|
+
distro: string;
|
|
28
|
+
pendingCount: number;
|
|
29
|
+
available: boolean;
|
|
30
|
+
} | null;
|
|
31
|
+
kernel_vulns: Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
status: string;
|
|
34
|
+
mitigated: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
kernel_reboot: {
|
|
37
|
+
running: string;
|
|
38
|
+
installed: string;
|
|
39
|
+
needsReboot: boolean;
|
|
40
|
+
} | null;
|
|
41
|
+
auto_updates: {
|
|
42
|
+
configured: boolean;
|
|
43
|
+
mechanism: string;
|
|
44
|
+
details: string;
|
|
45
|
+
};
|
|
13
46
|
}
|
|
14
47
|
export interface SystemInfo {
|
|
15
48
|
hostname: string;
|
|
@@ -18,6 +51,15 @@ export interface SystemInfo {
|
|
|
18
51
|
kernel: string;
|
|
19
52
|
uptime_seconds: number;
|
|
20
53
|
}
|
|
54
|
+
export interface CpuCoreInfo {
|
|
55
|
+
core: number;
|
|
56
|
+
user_percent: number;
|
|
57
|
+
system_percent: number;
|
|
58
|
+
iowait_percent: number;
|
|
59
|
+
idle_percent: number;
|
|
60
|
+
irq_percent: number;
|
|
61
|
+
softirq_percent: number;
|
|
62
|
+
}
|
|
21
63
|
export interface CpuInfo {
|
|
22
64
|
user_percent: number;
|
|
23
65
|
system_percent: number;
|
|
@@ -26,6 +68,7 @@ export interface CpuInfo {
|
|
|
26
68
|
load_1m: number;
|
|
27
69
|
load_5m: number;
|
|
28
70
|
load_15m: number;
|
|
71
|
+
cores?: CpuCoreInfo[];
|
|
29
72
|
}
|
|
30
73
|
export interface MemoryInfo {
|
|
31
74
|
total_mb: number;
|
|
@@ -41,6 +84,11 @@ export interface DiskInfo {
|
|
|
41
84
|
used_gb: number;
|
|
42
85
|
available_gb: number;
|
|
43
86
|
percent_used: number;
|
|
87
|
+
fstype?: string;
|
|
88
|
+
options?: string;
|
|
89
|
+
inodes_total?: number;
|
|
90
|
+
inodes_used?: number;
|
|
91
|
+
inodes_free?: number;
|
|
44
92
|
io_read_mb_s?: number;
|
|
45
93
|
io_write_mb_s?: number;
|
|
46
94
|
latency_p99_ms?: number;
|
|
@@ -73,6 +121,20 @@ export interface RaidInfo {
|
|
|
73
121
|
disks: string[];
|
|
74
122
|
failed_disks: string[];
|
|
75
123
|
}
|
|
124
|
+
export interface SelEvent {
|
|
125
|
+
id: number;
|
|
126
|
+
timestamp: string;
|
|
127
|
+
sensor: string;
|
|
128
|
+
sensor_type: string;
|
|
129
|
+
event: string;
|
|
130
|
+
direction: string;
|
|
131
|
+
severity: string;
|
|
132
|
+
}
|
|
133
|
+
export interface FanStatus {
|
|
134
|
+
name: string;
|
|
135
|
+
rpm: number;
|
|
136
|
+
status: string;
|
|
137
|
+
}
|
|
76
138
|
export interface IpmiInfo {
|
|
77
139
|
available: boolean;
|
|
78
140
|
sensors: Array<{
|
|
@@ -87,6 +149,8 @@ export interface IpmiInfo {
|
|
|
87
149
|
uncorrectable: number;
|
|
88
150
|
};
|
|
89
151
|
sel_entries_count: number;
|
|
152
|
+
sel_events_recent: SelEvent[];
|
|
153
|
+
fans: FanStatus[];
|
|
90
154
|
}
|
|
91
155
|
export interface OsAlerts {
|
|
92
156
|
oom_kills_recent: number;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const CURRENT_VERSION = "0.1.0";
|
|
2
|
+
let lastCheckTime = 0;
|
|
3
|
+
let lastResult = null;
|
|
4
|
+
const CHECK_INTERVAL = 6 * 60 * 60 * 1000; // check every 6 hours
|
|
5
|
+
export function getCurrentVersion() {
|
|
6
|
+
return CURRENT_VERSION;
|
|
7
|
+
}
|
|
8
|
+
export async function checkForUpdates(forgeUrl) {
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
if (now - lastCheckTime < CHECK_INTERVAL)
|
|
11
|
+
return;
|
|
12
|
+
lastCheckTime = now;
|
|
13
|
+
const url = forgeUrl || "https://forge.glassmkr.com";
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch(`${url}/api/v1/version`, { signal: AbortSignal.timeout(5000) });
|
|
16
|
+
if (!res.ok)
|
|
17
|
+
return;
|
|
18
|
+
const data = await res.json();
|
|
19
|
+
const latest = data.crucible?.latest;
|
|
20
|
+
if (!latest)
|
|
21
|
+
return;
|
|
22
|
+
if (latest !== CURRENT_VERSION) {
|
|
23
|
+
console.log(`[update] New Crucible version available: ${latest} (current: ${CURRENT_VERSION})`);
|
|
24
|
+
console.log(`[update] Changelog: ${data.crucible?.changelog_url || "https://github.com/glassmkr/crucible/releases"}`);
|
|
25
|
+
console.log(`[update] Run: npm update -g @glassmkr/crucible && sudo systemctl restart glassmkr-collector`);
|
|
26
|
+
lastResult = { updateAvailable: true, latest, changelog: data.crucible?.changelog_url || "" };
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
lastResult = { updateAvailable: false, latest, changelog: "" };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Version check is non-critical, fail silently
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function getUpdateStatus() {
|
|
37
|
+
return lastResult;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=version-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/lib/version-check.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,OAAO,CAAC;AAChC,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,IAAI,UAAU,GAA2E,IAAI,CAAC;AAC9F,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,sBAAsB;AAEjE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAiB;IACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,GAAG,GAAG,aAAa,GAAG,cAAc;QAAE,OAAO;IACjD,aAAa,GAAG,GAAG,CAAC;IAEpB,MAAM,GAAG,GAAG,QAAQ,IAAI,4BAA4B,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,iBAAiB,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO;QACpB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAwF,CAAC;QACpH,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC;QACrC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,IAAI,MAAM,KAAK,eAAe,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,4CAA4C,MAAM,cAAc,eAAe,GAAG,CAAC,CAAC;YAChG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,CAAC,QAAQ,EAAE,aAAa,IAAI,+CAA+C,EAAE,CAAC,CAAC;YACtH,OAAO,CAAC,GAAG,CAAC,6FAA6F,CAAC,CAAC;YAC3G,UAAU,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,IAAI,EAAE,EAAE,CAAC;QAChG,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;QACjE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
let latestSnapshot = null;
|
|
3
|
+
export function updateMetrics(snapshot) {
|
|
4
|
+
latestSnapshot = snapshot;
|
|
5
|
+
}
|
|
6
|
+
export function startMetricsServer(port) {
|
|
7
|
+
const server = createServer((req, res) => {
|
|
8
|
+
if (req.url === "/metrics" && req.method === "GET") {
|
|
9
|
+
if (!latestSnapshot) {
|
|
10
|
+
res.writeHead(503);
|
|
11
|
+
res.end("# No data collected yet\n");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
res.writeHead(200, { "Content-Type": "text/plain; version=0.0.4" });
|
|
15
|
+
res.end(formatPrometheus(latestSnapshot));
|
|
16
|
+
}
|
|
17
|
+
else if (req.url === "/health") {
|
|
18
|
+
res.writeHead(200);
|
|
19
|
+
res.end("ok\n");
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
res.writeHead(404);
|
|
23
|
+
res.end("Not found\n");
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
server.listen(port, "0.0.0.0", () => {
|
|
27
|
+
console.log(`[metrics] Prometheus endpoint listening on :${port}/metrics`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function formatPrometheus(snap) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
// CPU
|
|
33
|
+
lines.push("# HELP glassmkr_cpu_user_percent CPU user utilization");
|
|
34
|
+
lines.push("# TYPE glassmkr_cpu_user_percent gauge");
|
|
35
|
+
lines.push(`glassmkr_cpu_user_percent ${snap.cpu.user_percent}`);
|
|
36
|
+
lines.push(`glassmkr_cpu_system_percent ${snap.cpu.system_percent}`);
|
|
37
|
+
lines.push(`glassmkr_cpu_iowait_percent ${snap.cpu.iowait_percent}`);
|
|
38
|
+
lines.push(`glassmkr_cpu_idle_percent ${snap.cpu.idle_percent}`);
|
|
39
|
+
lines.push(`glassmkr_load_1m ${snap.cpu.load_1m}`);
|
|
40
|
+
lines.push(`glassmkr_load_5m ${snap.cpu.load_5m}`);
|
|
41
|
+
lines.push(`glassmkr_load_15m ${snap.cpu.load_15m}`);
|
|
42
|
+
// Memory
|
|
43
|
+
lines.push("# HELP glassmkr_memory_used_mb Memory used in MB");
|
|
44
|
+
lines.push("# TYPE glassmkr_memory_used_mb gauge");
|
|
45
|
+
lines.push(`glassmkr_memory_used_mb ${snap.memory.used_mb}`);
|
|
46
|
+
lines.push(`glassmkr_memory_total_mb ${snap.memory.total_mb}`);
|
|
47
|
+
lines.push(`glassmkr_memory_available_mb ${snap.memory.available_mb}`);
|
|
48
|
+
lines.push(`glassmkr_swap_used_mb ${snap.memory.swap_used_mb}`);
|
|
49
|
+
// Disks
|
|
50
|
+
lines.push("# HELP glassmkr_disk_used_percent Disk usage percentage");
|
|
51
|
+
lines.push("# TYPE glassmkr_disk_used_percent gauge");
|
|
52
|
+
for (const disk of snap.disks) {
|
|
53
|
+
const labels = `mount="${disk.mount}",device="${disk.device}"`;
|
|
54
|
+
lines.push(`glassmkr_disk_used_percent{${labels}} ${disk.percent_used}`);
|
|
55
|
+
lines.push(`glassmkr_disk_total_gb{${labels}} ${disk.total_gb}`);
|
|
56
|
+
lines.push(`glassmkr_disk_used_gb{${labels}} ${disk.used_gb}`);
|
|
57
|
+
}
|
|
58
|
+
// Network
|
|
59
|
+
lines.push("# HELP glassmkr_net_rx_bytes_sec Network receive bytes per second");
|
|
60
|
+
lines.push("# TYPE glassmkr_net_rx_bytes_sec gauge");
|
|
61
|
+
for (const iface of snap.network) {
|
|
62
|
+
const labels = `interface="${iface.interface}"`;
|
|
63
|
+
lines.push(`glassmkr_net_rx_bytes_sec{${labels}} ${iface.rx_bytes_sec}`);
|
|
64
|
+
lines.push(`glassmkr_net_tx_bytes_sec{${labels}} ${iface.tx_bytes_sec}`);
|
|
65
|
+
lines.push(`glassmkr_net_rx_errors{${labels}} ${iface.rx_errors}`);
|
|
66
|
+
lines.push(`glassmkr_net_tx_errors{${labels}} ${iface.tx_errors}`);
|
|
67
|
+
lines.push(`glassmkr_net_speed_mbps{${labels}} ${iface.speed_mbps}`);
|
|
68
|
+
}
|
|
69
|
+
// SMART
|
|
70
|
+
for (const drive of snap.smart) {
|
|
71
|
+
const labels = `device="${drive.device}",model="${drive.model}"`;
|
|
72
|
+
if (drive.temperature_c != null)
|
|
73
|
+
lines.push(`glassmkr_smart_temperature_c{${labels}} ${drive.temperature_c}`);
|
|
74
|
+
if (drive.percentage_used != null)
|
|
75
|
+
lines.push(`glassmkr_smart_percentage_used{${labels}} ${drive.percentage_used}`);
|
|
76
|
+
if (drive.reallocated_sectors != null)
|
|
77
|
+
lines.push(`glassmkr_smart_reallocated_sectors{${labels}} ${drive.reallocated_sectors}`);
|
|
78
|
+
}
|
|
79
|
+
// IPMI
|
|
80
|
+
if (snap.ipmi?.available) {
|
|
81
|
+
for (const sensor of snap.ipmi.sensors) {
|
|
82
|
+
if (typeof sensor.value === "number") {
|
|
83
|
+
const sensorName = sensor.name.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
|
|
84
|
+
lines.push(`glassmkr_ipmi_sensor{sensor="${sensor.name}",unit="${sensor.unit}"} ${sensor.value}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
lines.push(`glassmkr_ipmi_ecc_correctable ${snap.ipmi.ecc_errors.correctable}`);
|
|
88
|
+
lines.push(`glassmkr_ipmi_ecc_uncorrectable ${snap.ipmi.ecc_errors.uncorrectable}`);
|
|
89
|
+
// Fans
|
|
90
|
+
if (snap.ipmi.fans) {
|
|
91
|
+
for (const fan of snap.ipmi.fans) {
|
|
92
|
+
lines.push(`glassmkr_ipmi_fan_rpm{fan="${fan.name}",status="${fan.status}"} ${fan.rpm}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// OS alerts
|
|
97
|
+
lines.push(`glassmkr_oom_kills_recent ${snap.os_alerts.oom_kills_recent}`);
|
|
98
|
+
lines.push(`glassmkr_zombie_processes ${snap.os_alerts.zombie_processes}`);
|
|
99
|
+
// Security
|
|
100
|
+
if (snap.security) {
|
|
101
|
+
lines.push(`glassmkr_ssh_root_password_exposed ${snap.security.ssh?.rootPasswordExposed ? 1 : 0}`);
|
|
102
|
+
lines.push(`glassmkr_firewall_active ${snap.security.firewall.active ? 1 : 0}`);
|
|
103
|
+
if (snap.security.pending_updates?.available) {
|
|
104
|
+
lines.push(`glassmkr_pending_security_updates ${snap.security.pending_updates.pendingCount}`);
|
|
105
|
+
}
|
|
106
|
+
const unmitigated = snap.security.kernel_vulns.filter(v => !v.mitigated).length;
|
|
107
|
+
lines.push(`glassmkr_kernel_vulns_unmitigated ${unmitigated}`);
|
|
108
|
+
lines.push(`glassmkr_kernel_needs_reboot ${snap.security.kernel_reboot?.needsReboot ? 1 : 0}`);
|
|
109
|
+
lines.push(`glassmkr_auto_updates_configured ${snap.security.auto_updates.configured ? 1 : 0}`);
|
|
110
|
+
}
|
|
111
|
+
return lines.join("\n") + "\n";
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=metrics-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-server.js","sourceRoot":"","sources":["../src/metrics-server.ts"],"names":[],"mappings":"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"}
|
package/dist/notify/telegram.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
+
const PRIORITY_MAP = {
|
|
2
|
+
raid_degraded: "P1", smart_failing: "P1", ecc_errors: "P1", psu_redundancy_loss: "P1", ipmi_fan_failure: "P1",
|
|
3
|
+
oom_kills: "P2", ram_high: "P2", disk_space_high: "P2", ipmi_sel_critical: "P2", disk_io_errors: "P2", zfs_pool_unhealthy: "P2",
|
|
4
|
+
cpu_iowait_high: "P3", nvme_wear_high: "P3", disk_latency_high: "P3", cpu_temperature_high: "P3",
|
|
5
|
+
ssh_root_password: "P3", pending_security_updates: "P3", kernel_vulnerabilities: "P3", zfs_scrub_errors: "P3",
|
|
6
|
+
swap_active: "P4", no_firewall: "P4", kernel_needs_reboot: "P4", unattended_upgrades_disabled: "P4",
|
|
7
|
+
interface_errors: "P4", link_speed_mismatch: "P4", interface_saturation: "P4",
|
|
8
|
+
};
|
|
9
|
+
const PRIORITY_LABELS = {
|
|
10
|
+
P1: "\u{1F534} P1 Urgent", P2: "\u{1F7E0} P2 High", P3: "\u{1F7E1} P3 Medium", P4: "\u{1F535} P4 Low",
|
|
11
|
+
};
|
|
12
|
+
function getPriority(alertType) {
|
|
13
|
+
return PRIORITY_MAP[alertType] || "P3";
|
|
14
|
+
}
|
|
1
15
|
export async function sendTelegram(botToken, chatId, newAlerts, resolvedAlerts, serverName) {
|
|
2
16
|
const parts = [];
|
|
3
17
|
if (newAlerts.length > 0) {
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
// Group by priority
|
|
19
|
+
const byPriority = {};
|
|
20
|
+
for (const a of newAlerts) {
|
|
21
|
+
const p = getPriority(a.type);
|
|
22
|
+
if (!byPriority[p])
|
|
23
|
+
byPriority[p] = [];
|
|
24
|
+
byPriority[p].push(a);
|
|
10
25
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
for (const p of ["P1", "P2", "P3", "P4"]) {
|
|
27
|
+
const alerts = byPriority[p];
|
|
28
|
+
if (!alerts?.length)
|
|
29
|
+
continue;
|
|
30
|
+
parts.push(`${PRIORITY_LABELS[p]} on <b>${serverName}</b>:\n`);
|
|
31
|
+
for (const a of alerts)
|
|
14
32
|
parts.push(` \u2022 <b>${a.title}</b>\n ${a.recommendation}\n`);
|
|
15
33
|
}
|
|
16
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/notify/telegram.ts"],"names":[],"mappings":"AAEA,MAAM,
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/notify/telegram.ts"],"names":[],"mappings":"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"}
|
package/dist/push/forge.d.ts
CHANGED
package/dist/push/forge.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
|
+
import https from "https";
|
|
2
|
+
import tls from "tls";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
let agent;
|
|
5
|
+
export function initForgeAgent(tlsPin) {
|
|
6
|
+
if (!tlsPin) {
|
|
7
|
+
agent = undefined; // Use default (Node built-in fetch)
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
agent = new https.Agent({
|
|
11
|
+
rejectUnauthorized: true,
|
|
12
|
+
checkServerIdentity: (hostname, cert) => {
|
|
13
|
+
const err = tls.checkServerIdentity(hostname, cert);
|
|
14
|
+
if (err)
|
|
15
|
+
return err;
|
|
16
|
+
const pubkey = cert.pubkey;
|
|
17
|
+
if (!pubkey)
|
|
18
|
+
return new Error("Certificate has no public key");
|
|
19
|
+
const hash = crypto.createHash("sha256").update(pubkey).digest("base64");
|
|
20
|
+
if (hash !== tlsPin) {
|
|
21
|
+
return new Error(`TLS pin mismatch for ${hostname}. ` +
|
|
22
|
+
`Expected: ${tlsPin}, Got: ${hash}. ` +
|
|
23
|
+
`If the server certificate was rotated with a new key, update tls_pin in collector.yaml.`);
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
1
29
|
export async function pushToForge(url, apiKey, snapshot) {
|
|
30
|
+
// If TLS pinning is enabled, use https.request (fetch doesn't support custom agents)
|
|
31
|
+
if (agent) {
|
|
32
|
+
return pushWithAgent(url, apiKey, snapshot);
|
|
33
|
+
}
|
|
34
|
+
// Default: use fetch (no pinning)
|
|
2
35
|
try {
|
|
3
36
|
const response = await fetch(`${url}/api/v1/ingest`, {
|
|
4
37
|
method: "POST",
|
|
5
|
-
headers: {
|
|
6
|
-
Authorization: `Bearer ${apiKey}`,
|
|
7
|
-
"Content-Type": "application/json",
|
|
8
|
-
},
|
|
38
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
9
39
|
body: JSON.stringify(snapshot),
|
|
10
40
|
signal: AbortSignal.timeout(10000),
|
|
11
41
|
});
|
|
@@ -23,4 +53,50 @@ export async function pushToForge(url, apiKey, snapshot) {
|
|
|
23
53
|
return false;
|
|
24
54
|
}
|
|
25
55
|
}
|
|
56
|
+
function pushWithAgent(url, apiKey, snapshot) {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const parsed = new URL(`${url}/api/v1/ingest`);
|
|
59
|
+
const body = JSON.stringify(snapshot);
|
|
60
|
+
const req = https.request({
|
|
61
|
+
hostname: parsed.hostname,
|
|
62
|
+
port: parsed.port ? parseInt(parsed.port) : 443,
|
|
63
|
+
path: parsed.pathname,
|
|
64
|
+
method: "POST",
|
|
65
|
+
agent,
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Bearer ${apiKey}`,
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"Content-Length": Buffer.byteLength(body),
|
|
70
|
+
},
|
|
71
|
+
timeout: 10000,
|
|
72
|
+
}, (res) => {
|
|
73
|
+
let data = "";
|
|
74
|
+
res.on("data", (chunk) => data += chunk);
|
|
75
|
+
res.on("end", () => {
|
|
76
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(data);
|
|
79
|
+
console.log(`[forge] Push successful (pinned). Active alerts: ${parsed.active_alerts ?? 0}`);
|
|
80
|
+
}
|
|
81
|
+
catch { /* ignore parse errors */ }
|
|
82
|
+
resolve(true);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error(`[forge] Push failed (pinned): ${res.statusCode}`);
|
|
86
|
+
resolve(false);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
req.on("error", (err) => {
|
|
91
|
+
console.error(`[forge] Push failed (pinned): ${err.message}`);
|
|
92
|
+
resolve(false);
|
|
93
|
+
});
|
|
94
|
+
req.on("timeout", () => {
|
|
95
|
+
req.destroy(new Error("Request timed out"));
|
|
96
|
+
resolve(false);
|
|
97
|
+
});
|
|
98
|
+
req.write(body);
|
|
99
|
+
req.end();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
26
102
|
//# sourceMappingURL=forge.js.map
|
package/dist/push/forge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forge.js","sourceRoot":"","sources":["../../src/push/forge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"forge.js","sourceRoot":"","sources":["../../src/push/forge.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Sign a Crucible release
|
|
3
|
+
# Usage: ./scripts/sign-release.sh <version>
|
|
4
|
+
|
|
5
|
+
VERSION=$1
|
|
6
|
+
DIST_DIR="dist"
|
|
7
|
+
|
|
8
|
+
if [ -z "$VERSION" ]; then
|
|
9
|
+
echo "Usage: ./scripts/sign-release.sh <version>"
|
|
10
|
+
echo "Example: ./scripts/sign-release.sh v0.2.0"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "Signing Crucible $VERSION"
|
|
15
|
+
|
|
16
|
+
# Generate checksums
|
|
17
|
+
cd "$DIST_DIR" || exit 1
|
|
18
|
+
sha256sum *.tar.gz *.deb 2>/dev/null > SHA256SUMS || sha256sum *.js > SHA256SUMS
|
|
19
|
+
|
|
20
|
+
# Sign the checksums file
|
|
21
|
+
gpg --armor --detach-sign --local-user security@glassmkr.com SHA256SUMS
|
|
22
|
+
|
|
23
|
+
echo ""
|
|
24
|
+
echo "Release artifacts:"
|
|
25
|
+
ls -la SHA256SUMS SHA256SUMS.asc
|
|
26
|
+
echo ""
|
|
27
|
+
echo "Verify with:"
|
|
28
|
+
echo " gpg --verify SHA256SUMS.asc SHA256SUMS"
|
|
29
|
+
echo " sha256sum -c SHA256SUMS"
|
package/src/alerts/rules.ts
CHANGED
|
@@ -181,4 +181,103 @@ export const allRules: AlertRule[] = [
|
|
|
181
181
|
evidence: { failed: failed.map(p => ({ name: p.name, status: p.status })) },
|
|
182
182
|
recommendation: "Replace failed PSU. Check power connections." }];
|
|
183
183
|
}},
|
|
184
|
+
// 19. IPMI SEL critical events
|
|
185
|
+
{ type: "ipmi_sel_critical", evaluate(snap) {
|
|
186
|
+
if (!snap.ipmi?.available || !snap.ipmi.sel_events_recent?.length) return [];
|
|
187
|
+
const critical = snap.ipmi.sel_events_recent.filter(e => e.severity === "critical" && e.direction === "Asserted");
|
|
188
|
+
if (critical.length === 0) return [];
|
|
189
|
+
const byType: Record<string, typeof critical> = {};
|
|
190
|
+
for (const e of critical) { if (!byType[e.sensor_type]) byType[e.sensor_type] = []; byType[e.sensor_type].push(e); }
|
|
191
|
+
const details = Object.entries(byType).map(([t, evts]) => `${t}: ${evts.map(e => `${e.sensor}: ${e.event}`).join(", ")}`).join("; ");
|
|
192
|
+
const recs: string[] = [];
|
|
193
|
+
if (byType.memory) recs.push("Memory errors: identify slot with `ipmitool sel elist | grep -i memory`. Schedule DIMM replacement.");
|
|
194
|
+
if (byType.power) recs.push("PSU event: check physical PSU and connections. Verify redundancy: `ipmitool chassis status`.");
|
|
195
|
+
if (byType.watchdog) recs.push("Watchdog reset: OS or BMC became unresponsive. Check dmesg for root cause.");
|
|
196
|
+
if (byType.processor) recs.push("CPU event: check for thermal throttling or MCE. Run `dmesg | grep -i mce`.");
|
|
197
|
+
if (recs.length === 0) recs.push("Review full SEL: `ipmitool sel elist`.");
|
|
198
|
+
return [{ type: "ipmi_sel_critical", severity: "critical",
|
|
199
|
+
title: `IPMI: ${critical.length} critical hardware event(s)`,
|
|
200
|
+
message: `BMC System Event Log: ${critical.length} critical event(s). ${details}`,
|
|
201
|
+
evidence: { critical_events: critical, sensor_types: Object.keys(byType) },
|
|
202
|
+
recommendation: recs.join(" ") }];
|
|
203
|
+
}},
|
|
204
|
+
// 20. Fan failure
|
|
205
|
+
{ type: "ipmi_fan_failure", evaluate(snap) {
|
|
206
|
+
if (!snap.ipmi?.available || !snap.ipmi.fans?.length) return [];
|
|
207
|
+
const failed = snap.ipmi.fans.filter(f => f.status === "critical" || (f.rpm === 0 && f.status !== "absent"));
|
|
208
|
+
if (failed.length === 0) return [];
|
|
209
|
+
const total = snap.ipmi.fans.filter(f => f.status !== "absent").length;
|
|
210
|
+
const names = failed.map(f => `${f.name} (${f.rpm} RPM)`).join(", ");
|
|
211
|
+
return [{ type: "ipmi_fan_failure", severity: "critical",
|
|
212
|
+
title: `Fan failure: ${failed.length} of ${total} fans`,
|
|
213
|
+
message: `${failed.length} fan(s) stopped or critically slow: ${names}. Reduced cooling capacity.`,
|
|
214
|
+
evidence: { failed_fans: failed, total_fans: total, all_fans: snap.ipmi.fans.filter(f => f.status !== "absent") },
|
|
215
|
+
recommendation: "Check physical fans. Monitor temps: `ipmitool sdr type Temperature`. Replace failed fan module." }];
|
|
216
|
+
}},
|
|
217
|
+
// === Security (6) ===
|
|
218
|
+
// 21. SSH root password login
|
|
219
|
+
{ type: "ssh_root_password", evaluate(snap) {
|
|
220
|
+
if (!snap.security?.ssh?.rootPasswordExposed) return [];
|
|
221
|
+
return [{ type: "ssh_root_password", severity: "warning",
|
|
222
|
+
title: "SSH root login with password enabled",
|
|
223
|
+
message: `PermitRootLogin is "${snap.security.ssh.permitRootLogin}" and PasswordAuthentication is "${snap.security.ssh.passwordAuthentication}". Root can be brute-forced over SSH.`,
|
|
224
|
+
evidence: { permitRootLogin: snap.security.ssh.permitRootLogin, passwordAuthentication: snap.security.ssh.passwordAuthentication },
|
|
225
|
+
recommendation: 'Set "PermitRootLogin prohibit-password" in /etc/ssh/sshd_config and restart sshd. Key-based root login still works.' }];
|
|
226
|
+
}},
|
|
227
|
+
// 22. No firewall
|
|
228
|
+
{ type: "no_firewall", evaluate(snap) {
|
|
229
|
+
if (!snap.security || snap.security.firewall.active) return [];
|
|
230
|
+
return [{ type: "no_firewall", severity: "warning" as const,
|
|
231
|
+
title: "No firewall active",
|
|
232
|
+
message: "No active firewall rules detected (checked UFW, firewalld, nftables, iptables). All ports are exposed unless protected by network-level ACLs.",
|
|
233
|
+
evidence: { source: snap.security.firewall.source },
|
|
234
|
+
recommendation: 'Enable a firewall: "sudo ufw enable" (Debian/Ubuntu) or "sudo systemctl start firewalld" (RHEL/Rocky).' }];
|
|
235
|
+
}},
|
|
236
|
+
// 23. Pending security updates
|
|
237
|
+
{ type: "pending_security_updates", evaluate(snap, t) {
|
|
238
|
+
if (!snap.security?.pending_updates?.available) return [];
|
|
239
|
+
const maxPending = 10;
|
|
240
|
+
if (snap.security.pending_updates.pendingCount <= maxPending) return [];
|
|
241
|
+
const d = snap.security.pending_updates;
|
|
242
|
+
return [{ type: "pending_security_updates", severity: "warning",
|
|
243
|
+
title: `${d.pendingCount} security updates pending`,
|
|
244
|
+
message: `${d.pendingCount} security updates pending on this ${d.distro} server.`,
|
|
245
|
+
evidence: { pendingCount: d.pendingCount, distro: d.distro },
|
|
246
|
+
recommendation: d.distro === "ubuntu" || d.distro === "debian" ? 'Apply with: "sudo apt-get upgrade"' : 'Apply with: "sudo dnf update --security"' }];
|
|
247
|
+
}},
|
|
248
|
+
// 24. Kernel vulnerabilities
|
|
249
|
+
{ type: "kernel_vulnerabilities", evaluate(snap) {
|
|
250
|
+
if (!snap.security?.kernel_vulns?.length) return [];
|
|
251
|
+
const unmitigated = snap.security.kernel_vulns.filter(v => !v.mitigated);
|
|
252
|
+
if (unmitigated.length === 0) return [];
|
|
253
|
+
const details = unmitigated.map(v => `${v.name}: ${v.status}`).join("; ");
|
|
254
|
+
return [{ type: "kernel_vulnerabilities", severity: "warning",
|
|
255
|
+
title: `${unmitigated.length} CPU vulnerability mitigations missing`,
|
|
256
|
+
message: `Unmitigated: ${details}. Update the kernel and CPU microcode to apply mitigations.`,
|
|
257
|
+
evidence: { unmitigated, total: snap.security.kernel_vulns.length },
|
|
258
|
+
recommendation: 'Check: "grep . /sys/devices/system/cpu/vulnerabilities/*". Update kernel and microcode packages.' }];
|
|
259
|
+
}},
|
|
260
|
+
// 25. Kernel needs reboot
|
|
261
|
+
{ type: "kernel_needs_reboot", evaluate(snap) {
|
|
262
|
+
if (!snap.security?.kernel_reboot?.needsReboot) return [];
|
|
263
|
+
const k = snap.security.kernel_reboot;
|
|
264
|
+
return [{ type: "kernel_needs_reboot", severity: "warning" as const,
|
|
265
|
+
title: "Reboot required for kernel update",
|
|
266
|
+
message: `Running kernel: ${k.running}. Installed kernel: ${k.installed}. A reboot is needed to apply the newer kernel.`,
|
|
267
|
+
evidence: { running: k.running, installed: k.installed },
|
|
268
|
+
recommendation: "Schedule a reboot to apply the newer kernel. Security patches may not be active until then." }];
|
|
269
|
+
}},
|
|
270
|
+
// 26. Unattended upgrades disabled
|
|
271
|
+
{ type: "unattended_upgrades_disabled", evaluate(snap) {
|
|
272
|
+
if (!snap.security || snap.security.auto_updates.configured) return [];
|
|
273
|
+
const a = snap.security.auto_updates;
|
|
274
|
+
const hint = a.mechanism === "unattended-upgrades" ? 'Enable: "sudo dpkg-reconfigure -plow unattended-upgrades"'
|
|
275
|
+
: a.mechanism === "dnf-automatic" ? 'Enable: "sudo systemctl enable --now dnf-automatic-install.timer"'
|
|
276
|
+
: 'Install: "sudo apt install unattended-upgrades" (Debian/Ubuntu) or "sudo dnf install dnf-automatic" (RHEL/Rocky)';
|
|
277
|
+
return [{ type: "unattended_upgrades_disabled", severity: "warning" as const,
|
|
278
|
+
title: "Automatic security updates not configured",
|
|
279
|
+
message: `${a.details}. Without automatic updates, security patches must be applied manually.`,
|
|
280
|
+
evidence: { mechanism: a.mechanism, details: a.details },
|
|
281
|
+
recommendation: hint }];
|
|
282
|
+
}},
|
|
184
283
|
];
|