feat(smart-proxy): Improve connection/rate-limit atomicity, SNI parsing, HttpProxy & ACME orchestration, and routing utilities

This commit is contained in:
2025-12-09 13:07:29 +00:00
parent a0b23a8e7e
commit 9c25bf0a27
8 changed files with 363 additions and 114 deletions

View File

@@ -78,7 +78,7 @@ export class RouteConnectionHandler {
// Always wrap the socket to prepare for potential PROXY protocol
const wrappedSocket = new WrappedSocket(socket);
// If this is from a trusted proxy, log it
if (this.smartProxy.settings.proxyIPs?.includes(remoteIP)) {
logger.log('debug', `Connection from trusted proxy ${remoteIP}, PROXY protocol parsing will be enabled`, {
@@ -87,31 +87,40 @@ export class RouteConnectionHandler {
});
}
// Validate IP against rate limits and connection limits
// Note: For wrapped sockets, this will use the underlying socket IP until PROXY protocol is parsed
const ipValidation = this.smartProxy.securityManager.validateIP(wrappedSocket.remoteAddress || '');
// Generate connection ID first for atomic IP validation and tracking
const connectionId = this.smartProxy.connectionManager.generateConnectionId();
const clientIP = wrappedSocket.remoteAddress || '';
// Atomically validate IP and track the connection to prevent race conditions
// This ensures concurrent connections from the same IP are properly limited
const ipValidation = this.smartProxy.securityManager.validateAndTrackIP(clientIP, connectionId);
if (!ipValidation.allowed) {
connectionLogDeduplicator.log(
'ip-rejected',
'warn',
`Connection rejected from ${wrappedSocket.remoteAddress}`,
{ remoteIP: wrappedSocket.remoteAddress, reason: ipValidation.reason, component: 'route-handler' },
wrappedSocket.remoteAddress
`Connection rejected from ${clientIP}`,
{ remoteIP: clientIP, reason: ipValidation.reason, component: 'route-handler' },
clientIP
);
cleanupSocket(wrappedSocket.socket, `rejected-${ipValidation.reason}`, { immediate: true });
return;
}
// Create a new connection record with the wrapped socket
const record = this.smartProxy.connectionManager.createConnection(wrappedSocket);
// Skip IP tracking since we already did it atomically above
const record = this.smartProxy.connectionManager.createConnection(wrappedSocket, {
connectionId,
skipIpTracking: true
});
if (!record) {
// Connection was rejected due to limit - socket already destroyed by connection manager
// Connection was rejected due to global limit - clean up the IP tracking we did
this.smartProxy.securityManager.removeConnectionByIP(clientIP, connectionId);
return;
}
// Emit new connection event
this.newConnectionSubject.next(record);
const connectionId = record.id;
// Note: connectionId was already generated above for atomic IP tracking
// Apply socket optimizations (apply to underlying socket)
const underlyingSocket = wrappedSocket.socket;