feat(fingerprint): consolidate posture checks into fingerprint table

This commit is contained in:
Varun Narravula
2026-01-20 07:00:40 -08:00
committed by Owen Schwartz
parent 1f077d7ec2
commit 3ce1afbcc9
5 changed files with 309 additions and 257 deletions

View File

@@ -3,17 +3,32 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
import { currentFingerprint, db, fingerprintSnapshots, Olm } from "@server/db";
import { desc, eq } from "drizzle-orm";
function fingerprintHash(fp: any): string {
function fingerprintSnapshotHash(fingerprint: any, postures: any): string {
const canonical = {
username: fp.username ?? null,
hostname: fp.hostname ?? null,
platform: fp.platform ?? null,
osVersion: fp.osVersion ?? null,
kernelVersion: fp.kernelVersion ?? null,
arch: fp.arch ?? null,
deviceModel: fp.deviceModel ?? null,
serialNumber: fp.serialNumber ?? null,
platformFingerprint: fp.platformFingerprint ?? null
username: fingerprint.username ?? null,
hostname: fingerprint.hostname ?? null,
platform: fingerprint.platform ?? null,
osVersion: fingerprint.osVersion ?? null,
kernelVersion: fingerprint.kernelVersion ?? null,
arch: fingerprint.arch ?? null,
deviceModel: fingerprint.deviceModel ?? null,
serialNumber: fingerprint.serialNumber ?? null,
platformFingerprint: fingerprint.platformFingerprint ?? null,
biometricsEnabled: postures.biometricsEnabled ?? false,
diskEncrypted: postures.diskEncrypted ?? false,
firewallEnabled: postures.firewallEnabled ?? false,
autoUpdatesEnabled: postures.autoUpdatesEnabled ?? false,
tpmAvailable: postures.tpmAvailable ?? false,
windowsDefenderEnabled: postures.windowsDefenderEnabled ?? false,
macosSipEnabled: postures.macosSipEnabled ?? false,
macosGatekeeperEnabled: postures.macosGatekeeperEnabled ?? false,
macosFirewallStealthMode: postures.macosFirewallStealthMode ?? false,
linuxAppArmorEnabled: postures.linuxAppArmorEnabled ?? false,
linuxSELinuxEnabled: postures.linuxSELinuxEnabled ?? false
};
return encodeHexLowerCase(
@@ -21,14 +36,23 @@ function fingerprintHash(fp: any): string {
);
}
export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
if (!fingerprint || !olm.olmId || Object.keys(fingerprint).length < 1) {
export async function handleFingerprintInsertion(
olm: Olm,
fingerprint: any,
postures: any
) {
if (
!olm?.olmId ||
!fingerprint ||
!postures ||
Object.keys(fingerprint).length === 0 ||
Object.keys(postures).length === 0
) {
return;
}
const hash = fingerprintHash(fingerprint);
const now = Math.floor(Date.now() / 1000);
const hash = fingerprintSnapshotHash(fingerprint, postures);
const [current] = await db
.select()
@@ -43,7 +67,9 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
olmId: olm.olmId,
firstSeen: now,
lastSeen: now,
lastCollectedAt: now,
// fingerprint
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
@@ -52,7 +78,22 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
platformFingerprint: fingerprint.platformFingerprint,
biometricsEnabled: postures.biometricsEnabled,
diskEncrypted: postures.diskEncrypted,
firewallEnabled: postures.firewallEnabled,
autoUpdatesEnabled: postures.autoUpdatesEnabled,
tpmAvailable: postures.tpmAvailable,
windowsDefenderEnabled: postures.windowsDefenderEnabled,
macosSipEnabled: postures.macosSipEnabled,
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
macosFirewallStealthMode: postures.macosFirewallStealthMode,
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures.linuxSELinuxEnabled
})
.returning();
@@ -69,6 +110,21 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint,
biometricsEnabled: postures.biometricsEnabled,
diskEncrypted: postures.diskEncrypted,
firewallEnabled: postures.firewallEnabled,
autoUpdatesEnabled: postures.autoUpdatesEnabled,
tpmAvailable: postures.tpmAvailable,
windowsDefenderEnabled: postures.windowsDefenderEnabled,
macosSipEnabled: postures.macosSipEnabled,
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
macosFirewallStealthMode: postures.macosFirewallStealthMode,
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures.linuxSELinuxEnabled,
hash,
collectedAt: now
});
@@ -76,7 +132,6 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
return;
}
// Get most recent snapshot hash
const [latestSnapshot] = await db
.select({ hash: fingerprintSnapshots.hash })
.from(fingerprintSnapshots)
@@ -87,7 +142,6 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
const changed = !latestSnapshot || latestSnapshot.hash !== hash;
if (changed) {
// Insert snapshot if it has changed
await db.insert(fingerprintSnapshots).values({
fingerprintId: current.fingerprintId,
@@ -101,15 +155,30 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint,
biometricsEnabled: postures.biometricsEnabled,
diskEncrypted: postures.diskEncrypted,
firewallEnabled: postures.firewallEnabled,
autoUpdatesEnabled: postures.autoUpdatesEnabled,
tpmAvailable: postures.tpmAvailable,
windowsDefenderEnabled: postures.windowsDefenderEnabled,
macosSipEnabled: postures.macosSipEnabled,
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
macosFirewallStealthMode: postures.macosFirewallStealthMode,
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures.linuxSELinuxEnabled,
hash,
collectedAt: now
});
// Update current fingerprint fully
await db
.update(currentFingerprint)
.set({
lastSeen: now,
lastCollectedAt: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
@@ -119,11 +188,25 @@ export async function handleFingerprintInsertion(olm: Olm, fingerprint: any) {
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
platformFingerprint: fingerprint.platformFingerprint,
biometricsEnabled: postures.biometricsEnabled,
diskEncrypted: postures.diskEncrypted,
firewallEnabled: postures.firewallEnabled,
autoUpdatesEnabled: postures.autoUpdatesEnabled,
tpmAvailable: postures.tpmAvailable,
windowsDefenderEnabled: postures.windowsDefenderEnabled,
macosSipEnabled: postures.macosSipEnabled,
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
macosFirewallStealthMode: postures.macosFirewallStealthMode,
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures.linuxSELinuxEnabled
})
.where(eq(currentFingerprint.fingerprintId, current.fingerprintId));
} else {
// No change, so only bump lastSeen
await db
.update(currentFingerprint)
.set({ lastSeen: now })

View File

@@ -1,5 +1,5 @@
import { disconnectClient, getClientConfigVersion } from "#dynamic/routers/ws";
import { clientPostureSnapshots, db, currentFingerprint } from "@server/db";
import { db } from "@server/db";
import { MessageHandler } from "@server/routers/ws";
import { clients, olms, Olm } from "@server/db";
import { eq, lt, isNull, and, or } from "drizzle-orm";
@@ -215,36 +215,11 @@ export const handleOlmPingMessage: MessageHandler = async (context) => {
.set({ archived: false })
.where(eq(olms.olmId, olm.olmId));
}
await handleFingerprintInsertion(olm, fingerprint);
} catch (error) {
logger.error("Error handling ping message", { error });
}
const now = Math.floor(Date.now() / 1000);
if (postures && olm.clientId) {
await db.insert(clientPostureSnapshots).values({
clientId: olm.clientId,
biometricsEnabled: postures?.biometricsEnabled,
diskEncrypted: postures?.diskEncrypted,
firewallEnabled: postures?.firewallEnabled,
autoUpdatesEnabled: postures?.autoUpdatesEnabled,
tpmAvailable: postures?.tpmAvailable,
windowsDefenderEnabled: postures?.windowsDefenderEnabled,
macosSipEnabled: postures?.macosSipEnabled,
macosGatekeeperEnabled: postures?.macosGatekeeperEnabled,
macosFirewallStealthMode: postures?.macosFirewallStealthMode,
linuxAppArmorEnabled: postures?.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures?.linuxSELinuxEnabled,
collectedAt: now
});
}
await handleFingerprintInsertion(olm, fingerprint, postures);
return {
message: {

View File

@@ -1,9 +1,4 @@
import {
clientPostureSnapshots,
db,
currentFingerprint,
orgs
} from "@server/db";
import { db, orgs } from "@server/db";
import { MessageHandler } from "@server/routers/ws";
import {
clients,
@@ -20,6 +15,7 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { buildSiteConfigurationForOlmClient } from "./buildConfiguration";
import { OlmErrorCodes, sendOlmError } from "./error";
import { handleFingerprintInsertion } from "./fingerprintingUtils";
export const handleOlmRegisterMessage: MessageHandler = async (context) => {
logger.info("Handling register olm message!");
@@ -50,85 +46,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
return;
}
if (fingerprint) {
const [existingFingerprint] = await db
.select()
.from(currentFingerprint)
.where(eq(currentFingerprint.olmId, olm.olmId))
.limit(1);
if (!existingFingerprint) {
await db.insert(currentFingerprint).values({
olmId: olm.olmId,
firstSeen: now,
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
});
} else {
const hasChanges =
existingFingerprint.username !== fingerprint.username ||
existingFingerprint.hostname !== fingerprint.hostname ||
existingFingerprint.platform !== fingerprint.platform ||
existingFingerprint.osVersion !== fingerprint.osVersion ||
existingFingerprint.kernelVersion !==
fingerprint.kernelVersion ||
existingFingerprint.arch !== fingerprint.arch ||
existingFingerprint.deviceModel !== fingerprint.deviceModel ||
existingFingerprint.serialNumber !== fingerprint.serialNumber ||
existingFingerprint.platformFingerprint !==
fingerprint.platformFingerprint;
if (hasChanges) {
await db
.update(currentFingerprint)
.set({
lastSeen: now,
username: fingerprint.username,
hostname: fingerprint.hostname,
platform: fingerprint.platform,
osVersion: fingerprint.osVersion,
kernelVersion: fingerprint.kernelVersion,
arch: fingerprint.arch,
deviceModel: fingerprint.deviceModel,
serialNumber: fingerprint.serialNumber,
platformFingerprint: fingerprint.platformFingerprint
})
.where(eq(currentFingerprint.olmId, olm.olmId));
}
}
}
if (postures) {
await db.insert(clientPostureSnapshots).values({
clientId: olm.clientId,
biometricsEnabled: postures?.biometricsEnabled,
diskEncrypted: postures?.diskEncrypted,
firewallEnabled: postures?.firewallEnabled,
autoUpdatesEnabled: postures?.autoUpdatesEnabled,
tpmAvailable: postures?.tpmAvailable,
windowsDefenderEnabled: postures?.windowsDefenderEnabled,
macosSipEnabled: postures?.macosSipEnabled,
macosGatekeeperEnabled: postures?.macosGatekeeperEnabled,
macosFirewallStealthMode: postures?.macosFirewallStealthMode,
linuxAppArmorEnabled: postures?.linuxAppArmorEnabled,
linuxSELinuxEnabled: postures?.linuxSELinuxEnabled,
collectedAt: now
});
}
await handleFingerprintInsertion(olm, fingerprint, postures);
if (
(olmVersion && olm.version !== olmVersion) ||