Files
makemd/server/src/core/security/NodeIdentityService.ts

107 lines
3.5 KiB
TypeScript
Raw Normal View History

import crypto from 'crypto';
import db from '../../config/database';
import { logger } from '../../utils/logger';
export interface NodeIdentity {
nodeId: string;
hardwareFingerprint: string;
clientCertFingerprint?: string; // [CORE_SEC_07] mTLS 证书指纹
publicKey: string;
status: 'PENDING' | 'TRUSTED' | 'REVOKED';
lastSeenAt: Date;
}
/**
* [CORE_SEC_04]
* @description Hub
*/
export class NodeIdentityService {
private static readonly TABLE_NAME = 'cf_node_identities';
/**
* / ( mTLS )
*/
static async registerNode(params: {
nodeId: string;
hardwareFingerprint: string;
clientCertFingerprint?: string;
publicKey: string;
}): Promise<boolean> {
const { nodeId, hardwareFingerprint, clientCertFingerprint, publicKey } = params;
const existing = await db(this.TABLE_NAME).where({ node_id: nodeId }).first();
if (existing) {
// 零信任校验:如果硬件指纹或证书指纹不匹配,拒绝更新
if (existing.hardware_fingerprint !== hardwareFingerprint) {
logger.error(`[ZeroTrust] Node ID ${nodeId} hardware fingerprint mismatch!`);
return false;
}
if (clientCertFingerprint && existing.client_cert_fingerprint && existing.client_cert_fingerprint !== clientCertFingerprint) {
logger.error(`[ZeroTrust] Node ID ${nodeId} mTLS certificate mismatch! Potential spoofing.`);
return false;
}
await db(this.TABLE_NAME).where({ node_id: nodeId }).update({
public_key: publicKey,
client_cert_fingerprint: clientCertFingerprint || existing.client_cert_fingerprint,
last_seen_at: new Date(),
updated_at: new Date()
});
} else {
await db(this.TABLE_NAME).insert({
node_id: nodeId,
hardware_fingerprint: hardwareFingerprint,
client_cert_fingerprint: clientCertFingerprint,
public_key: publicKey,
status: 'PENDING',
last_seen_at: new Date(),
created_at: new Date(),
updated_at: new Date()
});
logger.info(`[ZeroTrust] New node registered: ${nodeId} (mTLS: ${!!clientCertFingerprint})`);
}
return true;
}
/**
* (Challenge-Response)
*/
static async verifyNodeSignature(nodeId: string, payload: any, signature: string): Promise<boolean> {
const node = await db(this.TABLE_NAME).where({ node_id: nodeId, status: 'TRUSTED' }).first();
if (!node) return false;
try {
const verifier = crypto.createVerify('SHA256');
verifier.update(JSON.stringify(payload));
return verifier.verify(node.public_key, signature, 'base64');
} catch (err) {
return false;
}
}
/**
*
*/
static async initTable() {
const exists = await db.schema.hasTable(this.TABLE_NAME);
if (!exists) {
logger.info(`📦 Creating ${this.TABLE_NAME} table...`);
await db.schema.createTable(this.TABLE_NAME, (table) => {
table.string('node_id', 64).primary();
table.string('hardware_fingerprint', 128).notNullable();
table.string('client_cert_fingerprint', 128);
table.text('public_key').notNullable();
table.string('status', 16).defaultTo('PENDING');
table.timestamp('last_seen_at');
table.timestamps(true, true);
table.index(['hardware_fingerprint']);
});
logger.info(`✅ Table ${this.TABLE_NAME} created`);
}
}
}