feat(smart-proxy): add hot-reloadable global ingress security policy across Rust and TypeScript proxy layers

This commit is contained in:
2026-04-26 15:11:10 +00:00
parent 8fa3a51b03
commit af4908b63f
53 changed files with 2350 additions and 1196 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
* SmartProxy models
*/
// Export everything except IAcmeOptions from interfaces
export type { ISmartProxyOptions, ISmartProxyCertStore, IConnectionRecord, TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './interfaces.js';
export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyCertStore, IConnectionRecord, TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './interfaces.js';
export * from './route-types.js';
export * from './metrics-types.js';
+7 -1
View File
@@ -29,6 +29,11 @@ export interface ISmartProxyCertStore {
}
import type { IRouteConfig } from './route-types.js';
export interface ISmartProxySecurityPolicy {
blockedIps?: string[];
blockedCidrs?: string[];
}
/**
* Provision object for static or HTTP-01 certificate
*/
@@ -137,6 +142,7 @@ export interface ISmartProxyOptions {
// Rate limiting and security
maxConnectionsPerIP?: number; // Maximum simultaneous connections from a single IP
connectionRateLimitPerMinute?: number; // Max new connections per minute from a single IP
securityPolicy?: ISmartProxySecurityPolicy; // Global ingress block policy, enforced before routing
// Enhanced keep-alive settings
keepAliveTreatment?: 'standard' | 'extended' | 'immortal'; // How to treat keep-alive connections
@@ -276,4 +282,4 @@ export interface IConnectionRecord {
path?: string;
headers?: Record<string, string>;
};
}
}
+2 -1
View File
@@ -1,5 +1,5 @@
import type { IProtocolCacheEntry, IProtocolDistribution } from './metrics-types.js';
import type { IAcmeOptions, ISmartProxyOptions } from './interfaces.js';
import type { IAcmeOptions, ISmartProxyOptions, ISmartProxySecurityPolicy } from './interfaces.js';
import type {
IRouteAction,
IRouteConfig,
@@ -75,6 +75,7 @@ export interface IRustProxyOptions {
keepAliveInactivityMultiplier?: number;
extendedKeepAliveLifetime?: number;
metrics?: ISmartProxyOptions['metrics'];
securityPolicy?: ISmartProxySecurityPolicy;
acme?: IRustAcmeOptions;
}
@@ -7,6 +7,7 @@ import type {
IRustRouteConfig,
IRustStatistics,
} from './models/rust-types.js';
import type { ISmartProxySecurityPolicy } from './models/interfaces.js';
/**
* Type-safe command definitions for the Rust proxy IPC protocol.
@@ -15,6 +16,7 @@ type TSmartProxyCommands = {
start: { params: { config: IRustProxyOptions }; result: void };
stop: { params: Record<string, never>; result: void };
updateRoutes: { params: { routes: IRustRouteConfig[] }; result: void };
setSecurityPolicy: { params: { policy: ISmartProxySecurityPolicy }; result: void };
getMetrics: { params: Record<string, never>; result: IRustMetricsSnapshot };
getStatistics: { params: Record<string, never>; result: IRustStatistics };
provisionCertificate: { params: { routeName: string }; result: void };
@@ -139,6 +141,10 @@ export class RustProxyBridge extends plugins.EventEmitter {
await this.bridge.sendCommand('updateRoutes', { routes });
}
public async setSecurityPolicy(policy: ISmartProxySecurityPolicy): Promise<void> {
await this.bridge.sendCommand('setSecurityPolicy', { policy });
}
public async getMetrics(): Promise<IRustMetricsSnapshot> {
return this.bridge.sendCommand('getMetrics', {} as Record<string, never>);
}
+10 -1
View File
@@ -17,7 +17,7 @@ import { Mutex } from './utils/mutex.js';
import { ConcurrencySemaphore } from './utils/concurrency-semaphore.js';
// Types
import type { ISmartProxyOptions, TSmartProxyCertProvisionObject, IAcmeOptions, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './models/interfaces.js';
import type { ISmartProxyOptions, ISmartProxySecurityPolicy, TSmartProxyCertProvisionObject, IAcmeOptions, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './models/interfaces.js';
import type { IRouteConfig } from './models/route-types.js';
import type { IMetrics } from './models/metrics-types.js';
import type { IRustCertificateStatus, IRustProxyOptions, IRustStatistics } from './models/rust-types.js';
@@ -350,6 +350,15 @@ export class SmartProxy extends plugins.EventEmitter {
.catch((err) => logger.log('error', `Unexpected error in cert provisioning after route update: ${err.message}`, { component: 'smart-proxy' }));
}
/**
* Update the global ingress security policy without changing routes.
* The Rust engine applies this before route selection and backend connection.
*/
public async updateSecurityPolicy(policy: ISmartProxySecurityPolicy): Promise<void> {
this.settings.securityPolicy = policy;
await this.bridge.setSecurityPolicy(policy);
}
/**
* Provision a certificate for a named route.
*/
@@ -182,6 +182,7 @@ export function buildRustProxyOptions(
keepAliveInactivityMultiplier: settings.keepAliveInactivityMultiplier,
extendedKeepAliveLifetime: settings.extendedKeepAliveLifetime,
metrics: settings.metrics,
securityPolicy: settings.securityPolicy,
acme: serializeAcmeForRust(acme),
};
}