317 lines
10 KiB
TypeScript
317 lines
10 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import type { OpsServer } from '../classes.opsserver.js';
|
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
|
import { MetricsManager } from '../../monitoring/index.js';
|
|
|
|
export class SecurityHandler {
|
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
|
|
constructor(private opsServerRef: OpsServer) {
|
|
// Add this handler's router to the parent
|
|
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
this.registerHandlers();
|
|
}
|
|
|
|
private registerHandlers(): void {
|
|
// Security Metrics Handler
|
|
this.typedrouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityMetrics>(
|
|
'getSecurityMetrics',
|
|
async (dataArg, toolsArg) => {
|
|
const metrics = await this.collectSecurityMetrics();
|
|
return {
|
|
metrics: {
|
|
blockedIPs: metrics.blockedIPs,
|
|
reputationScores: metrics.reputationScores,
|
|
spamDetected: metrics.spamDetection.detected,
|
|
malwareDetected: metrics.malwareDetected,
|
|
phishingDetected: metrics.phishingDetected,
|
|
authenticationFailures: metrics.authFailures,
|
|
suspiciousActivities: metrics.suspiciousActivities,
|
|
},
|
|
trends: dataArg.includeDetails ? {
|
|
spam: metrics.trends.spam,
|
|
malware: metrics.trends.malware,
|
|
phishing: metrics.trends.phishing,
|
|
} : undefined,
|
|
};
|
|
}
|
|
)
|
|
);
|
|
|
|
// Active Connections Handler
|
|
this.typedrouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetActiveConnections>(
|
|
'getActiveConnections',
|
|
async (dataArg, toolsArg) => {
|
|
const connections = await this.getActiveConnections(dataArg.protocol, dataArg.state);
|
|
const connectionInfos: interfaces.data.IConnectionInfo[] = connections.map(conn => ({
|
|
id: conn.id,
|
|
remoteAddress: conn.source.ip,
|
|
localAddress: conn.destination.ip,
|
|
startTime: conn.startTime,
|
|
protocol: conn.type === 'http' ? 'https' : conn.type as any,
|
|
state: conn.status as any,
|
|
bytesReceived: Math.floor(conn.bytesTransferred / 2),
|
|
bytesSent: Math.floor(conn.bytesTransferred / 2),
|
|
}));
|
|
|
|
const summary = {
|
|
total: connectionInfos.length,
|
|
byProtocol: connectionInfos.reduce((acc, conn) => {
|
|
acc[conn.protocol] = (acc[conn.protocol] || 0) + 1;
|
|
return acc;
|
|
}, {} as { [protocol: string]: number }),
|
|
byState: connectionInfos.reduce((acc, conn) => {
|
|
acc[conn.state] = (acc[conn.state] || 0) + 1;
|
|
return acc;
|
|
}, {} as { [state: string]: number }),
|
|
};
|
|
|
|
return {
|
|
connections: connectionInfos,
|
|
summary,
|
|
};
|
|
}
|
|
)
|
|
);
|
|
|
|
// Network Stats Handler - provides comprehensive network metrics
|
|
this.typedrouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler(
|
|
'getNetworkStats',
|
|
async (dataArg, toolsArg) => {
|
|
// Get network stats from MetricsManager if available
|
|
if (this.opsServerRef.dcRouterRef.metricsManager) {
|
|
const networkStats = await this.opsServerRef.dcRouterRef.metricsManager.getNetworkStats();
|
|
|
|
return {
|
|
connectionsByIP: Array.from(networkStats.connectionsByIP.entries()).map(([ip, count]) => ({ ip, count })),
|
|
throughputRate: networkStats.throughputRate,
|
|
topIPs: networkStats.topIPs,
|
|
totalDataTransferred: networkStats.totalDataTransferred,
|
|
};
|
|
}
|
|
|
|
// Fallback if MetricsManager not available
|
|
return {
|
|
connectionsByIP: [],
|
|
throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
|
|
topIPs: [],
|
|
totalDataTransferred: { bytesIn: 0, bytesOut: 0 },
|
|
};
|
|
}
|
|
)
|
|
);
|
|
|
|
// Rate Limit Status Handler
|
|
this.typedrouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRateLimitStatus>(
|
|
'getRateLimitStatus',
|
|
async (dataArg, toolsArg) => {
|
|
const status = await this.getRateLimitStatus(dataArg.domain, dataArg.ip);
|
|
const limits: interfaces.data.IRateLimitInfo[] = status.limits.map(limit => ({
|
|
domain: limit.identifier,
|
|
currentRate: limit.current,
|
|
limit: limit.limit,
|
|
remaining: limit.limit - limit.current,
|
|
resetTime: limit.resetAt,
|
|
blocked: limit.status === 'limited',
|
|
}));
|
|
|
|
return {
|
|
limits,
|
|
globalLimit: dataArg.includeBlocked ? {
|
|
current: limits.reduce((sum, l) => sum + l.currentRate, 0),
|
|
limit: 1000, // Global limit
|
|
remaining: 1000 - limits.reduce((sum, l) => sum + l.currentRate, 0),
|
|
} : undefined,
|
|
};
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
private async collectSecurityMetrics(): Promise<{
|
|
blockedIPs: string[];
|
|
reputationScores: { [domain: string]: number };
|
|
spamDetection: {
|
|
detected: number;
|
|
falsePositives: number;
|
|
};
|
|
malwareDetected: number;
|
|
phishingDetected: number;
|
|
authFailures: number;
|
|
suspiciousActivities: number;
|
|
trends: {
|
|
spam: Array<{ timestamp: number; value: number }>;
|
|
malware: Array<{ timestamp: number; value: number }>;
|
|
phishing: Array<{ timestamp: number; value: number }>;
|
|
};
|
|
}> {
|
|
// Get metrics from MetricsManager if available
|
|
if (this.opsServerRef.dcRouterRef.metricsManager) {
|
|
const securityStats = await this.opsServerRef.dcRouterRef.metricsManager.getSecurityStats();
|
|
return {
|
|
blockedIPs: [], // TODO: Track actual blocked IPs
|
|
reputationScores: {},
|
|
spamDetection: {
|
|
detected: securityStats.spamDetected,
|
|
falsePositives: 0,
|
|
},
|
|
malwareDetected: securityStats.malwareDetected,
|
|
phishingDetected: securityStats.phishingDetected,
|
|
authFailures: securityStats.authFailures,
|
|
suspiciousActivities: 0,
|
|
trends: {
|
|
spam: [],
|
|
malware: [],
|
|
phishing: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
// Fallback if MetricsManager not available
|
|
return {
|
|
blockedIPs: [],
|
|
reputationScores: {},
|
|
spamDetection: {
|
|
detected: 0,
|
|
falsePositives: 0,
|
|
},
|
|
malwareDetected: 0,
|
|
phishingDetected: 0,
|
|
authFailures: 0,
|
|
suspiciousActivities: 0,
|
|
trends: {
|
|
spam: [],
|
|
malware: [],
|
|
phishing: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
private async getActiveConnections(
|
|
protocol?: 'http' | 'https' | 'smtp' | 'smtps',
|
|
state?: string
|
|
): Promise<Array<{
|
|
id: string;
|
|
type: 'http' | 'smtp' | 'dns';
|
|
source: {
|
|
ip: string;
|
|
port: number;
|
|
country?: string;
|
|
};
|
|
destination: {
|
|
ip: string;
|
|
port: number;
|
|
service?: string;
|
|
};
|
|
startTime: number;
|
|
bytesTransferred: number;
|
|
status: 'active' | 'idle' | 'closing';
|
|
}>> {
|
|
const connections: Array<{
|
|
id: string;
|
|
type: 'http' | 'smtp' | 'dns';
|
|
source: {
|
|
ip: string;
|
|
port: number;
|
|
country?: string;
|
|
};
|
|
destination: {
|
|
ip: string;
|
|
port: number;
|
|
service?: string;
|
|
};
|
|
startTime: number;
|
|
bytesTransferred: number;
|
|
status: 'active' | 'idle' | 'closing';
|
|
}> = [];
|
|
|
|
// Get connection info and network stats from MetricsManager if available
|
|
if (this.opsServerRef.dcRouterRef.metricsManager) {
|
|
const connectionInfo = await this.opsServerRef.dcRouterRef.metricsManager.getConnectionInfo();
|
|
const networkStats = await this.opsServerRef.dcRouterRef.metricsManager.getNetworkStats();
|
|
|
|
// Use IP-based connection data from the new metrics API
|
|
if (networkStats.connectionsByIP && networkStats.connectionsByIP.size > 0) {
|
|
let connIndex = 0;
|
|
const publicIp = this.opsServerRef.dcRouterRef.options.publicIp || 'server';
|
|
|
|
for (const [ip, count] of networkStats.connectionsByIP) {
|
|
// Create a connection entry for each active IP connection
|
|
for (let i = 0; i < Math.min(count, 5); i++) { // Limit to 5 connections per IP for UI performance
|
|
connections.push({
|
|
id: `conn-${connIndex++}`,
|
|
type: 'http',
|
|
source: {
|
|
ip: ip,
|
|
port: Math.floor(Math.random() * 50000) + 10000, // High port range
|
|
},
|
|
destination: {
|
|
ip: publicIp,
|
|
port: 443,
|
|
service: 'proxy',
|
|
},
|
|
startTime: Date.now() - Math.floor(Math.random() * 3600000), // Within last hour
|
|
bytesTransferred: Math.floor(networkStats.totalDataTransferred.bytesIn / networkStats.connectionsByIP.size),
|
|
status: 'active',
|
|
});
|
|
}
|
|
}
|
|
} else if (connectionInfo.length > 0) {
|
|
// Fallback to route-based connection info if no IP data available
|
|
connectionInfo.forEach((info, index) => {
|
|
connections.push({
|
|
id: `conn-${index}`,
|
|
type: 'http',
|
|
source: {
|
|
ip: 'unknown',
|
|
port: 0,
|
|
},
|
|
destination: {
|
|
ip: this.opsServerRef.dcRouterRef.options.publicIp || 'server',
|
|
port: 443,
|
|
service: info.source,
|
|
},
|
|
startTime: info.lastActivity.getTime(),
|
|
bytesTransferred: 0,
|
|
status: 'active',
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Filter by protocol if specified
|
|
if (protocol) {
|
|
return connections.filter(conn => {
|
|
if (protocol === 'https' || protocol === 'http') {
|
|
return conn.type === 'http';
|
|
}
|
|
return conn.type === protocol.replace('s', ''); // smtp/smtps -> smtp
|
|
});
|
|
}
|
|
|
|
return connections;
|
|
}
|
|
|
|
private async getRateLimitStatus(
|
|
domain?: string,
|
|
ip?: string
|
|
): Promise<{
|
|
limits: Array<{
|
|
identifier: string;
|
|
type: 'ip' | 'domain' | 'email';
|
|
limit: number;
|
|
current: number;
|
|
resetAt: number;
|
|
status: 'ok' | 'warning' | 'limited';
|
|
}>;
|
|
}> {
|
|
// TODO: Implement actual rate limit status collection
|
|
return {
|
|
limits: [],
|
|
};
|
|
}
|
|
} |