feat: add enrollment event detection from NetBird audit events

This commit is contained in:
Prox 2026-03-04 00:17:17 +02:00
parent 376e0b5a9d
commit 05440ea740
2 changed files with 118 additions and 0 deletions

78
src/poller/poller.test.ts Normal file
View File

@ -0,0 +1,78 @@
import { assertEquals } from "@std/assert";
import type { NbEvent } from "../netbird/types.ts";
import { processEnrollmentEvents } from "./poller.ts";
/** Helper: build an NbEvent with sensible defaults, overridable per-field. */
function makeEvent(overrides: Partial<NbEvent> = {}): NbEvent {
return {
id: 1,
timestamp: "2025-06-01T12:00:00Z",
activity: "peer setup key added",
activity_code: "peer.setupkey.add",
initiator_id: "init-1",
initiator_name: "admin",
target_id: "peer-1",
meta: { setup_key: "drone-key", name: "drone-01" },
...overrides,
};
}
Deno.test("processEnrollmentEvents detects peer.setupkey.add", () => {
const events: NbEvent[] = [
makeEvent(), // enrollment — should match
makeEvent({
id: 2,
activity_code: "peer.login",
target_id: "peer-2",
}), // not an enrollment
makeEvent({
id: 3,
activity_code: "group.add",
target_id: "group-1",
}), // unrelated event
];
const known = new Set(["drone-key"]);
const result = processEnrollmentEvents(events, known, null);
assertEquals(result.length, 1);
assertEquals(result[0].setupKeyName, "drone-key");
assertEquals(result[0].peerId, "peer-1");
assertEquals(result[0].peerHostname, "drone-01");
assertEquals(result[0].timestamp, "2025-06-01T12:00:00Z");
});
Deno.test("processEnrollmentEvents filters by lastTimestamp", () => {
const events: NbEvent[] = [
makeEvent({ id: 1, timestamp: "2025-06-01T10:00:00Z", target_id: "p1" }),
makeEvent({ id: 2, timestamp: "2025-06-01T12:00:00Z", target_id: "p2" }),
makeEvent({ id: 3, timestamp: "2025-06-01T14:00:00Z", target_id: "p3" }),
];
const known = new Set(["drone-key"]);
// Only events strictly after the watermark should pass.
const result = processEnrollmentEvents(
events,
known,
"2025-06-01T12:00:00Z",
);
assertEquals(result.length, 1);
assertEquals(result[0].peerId, "p3");
assertEquals(result[0].timestamp, "2025-06-01T14:00:00Z");
});
Deno.test("processEnrollmentEvents ignores unknown keys", () => {
const events: NbEvent[] = [
makeEvent({
meta: { setup_key: "rogue-key", name: "rogue-host" },
target_id: "peer-x",
}),
];
const known = new Set(["drone-key", "gcs-key"]);
const result = processEnrollmentEvents(events, known, null);
assertEquals(result.length, 0);
});

40
src/poller/poller.ts Normal file
View File

@ -0,0 +1,40 @@
import type { NbEvent } from "../netbird/types.ts";
export interface EnrollmentDetection {
setupKeyName: string;
peerId: string;
peerHostname: string;
timestamp: string;
}
/**
* Filters enrollment events from the full event list.
* Returns enrollments for peers that enrolled using a known setup key
* and whose timestamp is after lastTimestamp (if provided).
*/
export function processEnrollmentEvents(
events: NbEvent[],
knownKeyNames: Set<string>,
lastTimestamp: string | null,
): EnrollmentDetection[] {
return events
.filter((e) => {
if (e.activity_code !== "peer.setupkey.add") return false;
if (lastTimestamp && e.timestamp <= lastTimestamp) return false;
if (!knownKeyNames.has(e.meta.setup_key)) {
console.log(JSON.stringify({
msg: "unknown_enrollment",
setup_key: e.meta.setup_key,
peer_id: e.target_id,
}));
return false;
}
return true;
})
.map((e) => ({
setupKeyName: e.meta.setup_key,
peerId: e.target_id,
peerHostname: e.meta.name,
timestamp: e.timestamp,
}));
}