feat: add enrollment event detection from NetBird audit events
This commit is contained in:
parent
376e0b5a9d
commit
05440ea740
78
src/poller/poller.test.ts
Normal file
78
src/poller/poller.test.ts
Normal 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
40
src/poller/poller.ts
Normal 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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user