BREAKING CHANGE(IRouteSecurity): Consolidate duplicated IRouteSecurity interfaces by unifying property names
This commit is contained in:
		| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@push.rocks/smartproxy', | ||||
|   version: '17.0.0', | ||||
|   version: '18.0.0', | ||||
|   description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' | ||||
| } | ||||
|   | ||||
| @@ -199,8 +199,8 @@ export class SharedSecurityManager { | ||||
|     } | ||||
|      | ||||
|     // Check IP against route security settings | ||||
|     const ipAllowList = route.security.ipAllowList || route.security.allowedIps; | ||||
|     const ipBlockList = route.security.ipBlockList || route.security.blockedIps; | ||||
|     const ipAllowList = route.security.ipAllowList; | ||||
|     const ipBlockList = route.security.ipBlockList; | ||||
|      | ||||
|     const allowed = this.isIPAuthorized(clientIp, ipAllowList, ipBlockList); | ||||
|      | ||||
|   | ||||
| @@ -40,9 +40,10 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements | ||||
|    | ||||
|   /** | ||||
|    * Get a target from the configuration, supporting round-robin selection | ||||
|    * @param incomingPort Optional incoming port for 'preserve' mode | ||||
|    * @returns A resolved target object with host and port | ||||
|    */ | ||||
|   protected getTargetFromConfig(): { host: string, port: number } { | ||||
|   protected getTargetFromConfig(incomingPort: number = 80): { host: string, port: number } { | ||||
|     const { target } = this.config; | ||||
|      | ||||
|     // Handle round-robin host selection | ||||
| @@ -55,32 +56,37 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements | ||||
|       const randomIndex = Math.floor(Math.random() * target.host.length); | ||||
|       return { | ||||
|         host: target.host[randomIndex], | ||||
|         port: this.resolvePort(target.port) | ||||
|         port: this.resolvePort(target.port, incomingPort) | ||||
|       }; | ||||
|     } | ||||
|      | ||||
|     // Single host | ||||
|     return { | ||||
|       host: target.host, | ||||
|       port: this.resolvePort(target.port) | ||||
|       port: this.resolvePort(target.port, incomingPort) | ||||
|     }; | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    * Resolves a port value, handling 'preserve' and function ports | ||||
|    * @param port The port value to resolve | ||||
|    * @param incomingPort Optional incoming port to use for 'preserve' mode | ||||
|    */ | ||||
|   protected resolvePort(port: number | 'preserve' | ((ctx: any) => number)): number { | ||||
|   protected resolvePort( | ||||
|     port: number | 'preserve' | ((ctx: any) => number),  | ||||
|     incomingPort: number = 80 | ||||
|   ): number { | ||||
|     if (typeof port === 'function') { | ||||
|       try { | ||||
|         // Create a minimal context for the function | ||||
|         const ctx = { port: 80 }; // Default port for minimal context | ||||
|         // Create a minimal context for the function that includes the incoming port | ||||
|         const ctx = { port: incomingPort }; | ||||
|         return port(ctx); | ||||
|       } catch (err) { | ||||
|         console.error('Error resolving port function:', err); | ||||
|         return 80; // Default fallback port | ||||
|         return incomingPort; // Fall back to incoming port | ||||
|       } | ||||
|     } else if (port === 'preserve') { | ||||
|       return 80; // Default port for 'preserve' in base handler | ||||
|       return incomingPort; // Use the actual incoming port for 'preserve' | ||||
|     } else { | ||||
|       return port; | ||||
|     } | ||||
|   | ||||
| @@ -38,6 +38,7 @@ export class HttpForwardingHandler extends ForwardingHandler { | ||||
|     // For HTTP, we mainly handle parsed requests, but we can still set up | ||||
|     // some basic connection tracking | ||||
|     const remoteAddress = socket.remoteAddress || 'unknown'; | ||||
|     const localPort = socket.localPort || 80; | ||||
|      | ||||
|     socket.on('close', (hadError) => { | ||||
|       this.emit(ForwardingHandlerEvents.DISCONNECTED, { | ||||
| @@ -54,7 +55,8 @@ export class HttpForwardingHandler extends ForwardingHandler { | ||||
|     }); | ||||
|      | ||||
|     this.emit(ForwardingHandlerEvents.CONNECTED, { | ||||
|       remoteAddress | ||||
|       remoteAddress, | ||||
|       localPort | ||||
|     }); | ||||
|   } | ||||
|    | ||||
| @@ -64,8 +66,11 @@ export class HttpForwardingHandler extends ForwardingHandler { | ||||
|    * @param res The HTTP response | ||||
|    */ | ||||
|   public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { | ||||
|     // Get the target from configuration | ||||
|     const target = this.getTargetFromConfig(); | ||||
|     // Get the local port from the request (for 'preserve' port handling) | ||||
|     const localPort = req.socket.localPort || 80; | ||||
|      | ||||
|     // Get the target from configuration, passing the incoming port | ||||
|     const target = this.getTargetFromConfig(localPort); | ||||
|      | ||||
|     // Create a custom headers object with variables for substitution | ||||
|     const variables = { | ||||
|   | ||||
| @@ -27,8 +27,8 @@ export interface ISmartProxyOptions { | ||||
|       port: number; // Default port to use when not specified in routes | ||||
|     }; | ||||
|     security?: { | ||||
|       allowedIps?: string[]; // Default allowed IPs | ||||
|       blockedIps?: string[]; // Default blocked IPs | ||||
|       ipAllowList?: string[]; // Default allowed IPs | ||||
|       ipBlockList?: string[]; // Default blocked IPs | ||||
|       maxConnections?: number; // Default max connections | ||||
|     }; | ||||
|     preserveSourceIP?: boolean; // Default source IP preservation | ||||
|   | ||||
| @@ -112,13 +112,39 @@ export interface IRouteAuthentication { | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Security options for route actions | ||||
|  * Security options for routes | ||||
|  */ | ||||
| export interface IRouteSecurity { | ||||
|   allowedIps?: string[]; | ||||
|   blockedIps?: string[]; | ||||
|   maxConnections?: number; | ||||
|   // Access control lists | ||||
|   ipAllowList?: string[];       // IP addresses that are allowed to connect | ||||
|   ipBlockList?: string[];       // IP addresses that are blocked from connecting | ||||
|    | ||||
|   // Connection limits | ||||
|   maxConnections?: number;      // Maximum concurrent connections | ||||
|    | ||||
|   // Authentication | ||||
|   authentication?: IRouteAuthentication; | ||||
|    | ||||
|   // Rate limiting | ||||
|   rateLimit?: IRouteRateLimit; | ||||
|    | ||||
|   // Authentication methods | ||||
|   basicAuth?: { | ||||
|     enabled: boolean; | ||||
|     users: Array<{ username: string; password: string }>; | ||||
|     realm?: string; | ||||
|     excludePaths?: string[]; | ||||
|   }; | ||||
|    | ||||
|   jwtAuth?: { | ||||
|     enabled: boolean; | ||||
|     secret: string; | ||||
|     algorithm?: string; | ||||
|     issuer?: string; | ||||
|     audience?: string; | ||||
|     expiresIn?: number; | ||||
|     excludePaths?: string[]; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -247,29 +273,7 @@ export interface IRouteRateLimit { | ||||
|   errorMessage?: string; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Security features for routes | ||||
|  */ | ||||
| export interface IRouteSecurity { | ||||
|   rateLimit?: IRouteRateLimit; | ||||
|   basicAuth?: { | ||||
|     enabled: boolean; | ||||
|     users: Array<{ username: string; password: string }>; | ||||
|     realm?: string; | ||||
|     excludePaths?: string[]; | ||||
|   }; | ||||
|   jwtAuth?: { | ||||
|     enabled: boolean; | ||||
|     secret: string; | ||||
|     algorithm?: string; | ||||
|     issuer?: string; | ||||
|     audience?: string; | ||||
|     expiresIn?: number; | ||||
|     excludePaths?: string[]; | ||||
|   }; | ||||
|   ipAllowList?: string[]; | ||||
|   ipBlockList?: string[]; | ||||
| } | ||||
| // IRouteSecurity is defined above - unified definition is used for all routes | ||||
|  | ||||
| /** | ||||
|  * CORS configuration for a route | ||||
|   | ||||
| @@ -289,11 +289,11 @@ export class RouteConnectionHandler { | ||||
|       // Check default security settings | ||||
|       const defaultSecuritySettings = this.settings.defaults?.security; | ||||
|       if (defaultSecuritySettings) { | ||||
|         if (defaultSecuritySettings.allowedIps && defaultSecuritySettings.allowedIps.length > 0) { | ||||
|         if (defaultSecuritySettings.ipAllowList && defaultSecuritySettings.ipAllowList.length > 0) { | ||||
|           const isAllowed = this.securityManager.isIPAuthorized( | ||||
|             remoteIP, | ||||
|             defaultSecuritySettings.allowedIps, | ||||
|             defaultSecuritySettings.blockedIps || [] | ||||
|             defaultSecuritySettings.ipAllowList, | ||||
|             defaultSecuritySettings.ipBlockList || [] | ||||
|           ); | ||||
|  | ||||
|           if (!isAllowed) { | ||||
|   | ||||
| @@ -213,8 +213,8 @@ export class RouteManager extends plugins.EventEmitter { | ||||
|     } | ||||
|      | ||||
|     // Check blocked IPs first | ||||
|     if (security.blockedIps && security.blockedIps.length > 0) { | ||||
|       for (const pattern of security.blockedIps) { | ||||
|     if (security.ipBlockList && security.ipBlockList.length > 0) { | ||||
|       for (const pattern of security.ipBlockList) { | ||||
|         if (this.matchIpPattern(pattern, clientIp)) { | ||||
|           return false; // IP is blocked | ||||
|         } | ||||
| @@ -222,8 +222,8 @@ export class RouteManager extends plugins.EventEmitter { | ||||
|     } | ||||
|      | ||||
|     // If there are allowed IPs, check them | ||||
|     if (security.allowedIps && security.allowedIps.length > 0) { | ||||
|       for (const pattern of security.allowedIps) { | ||||
|     if (security.ipAllowList && security.ipAllowList.length > 0) { | ||||
|       for (const pattern of security.ipAllowList) { | ||||
|         if (this.matchIpPattern(pattern, clientIp)) { | ||||
|           return true; // IP is allowed | ||||
|         } | ||||
|   | ||||
| @@ -63,16 +63,15 @@ export class SecurityManager { | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    * Check if an IP is authorized using forwarding security rules | ||||
|    * Check if an IP is authorized using security rules | ||||
|    * | ||||
|    * This method is used to determine if an IP is allowed to connect, based on security | ||||
|    * rules configured in the forwarding configuration. The allowed and blocked IPs are | ||||
|    * typically derived from domain.forwarding.security.allowedIps and blockedIps through | ||||
|    * DomainConfigManager.getEffectiveIPRules(). | ||||
|    * rules configured in the route configuration. The allowed and blocked IPs are | ||||
|    * typically derived from route.security.ipAllowList and ipBlockList. | ||||
|    * | ||||
|    * @param ip - The IP address to check | ||||
|    * @param allowedIPs - Array of allowed IP patterns from forwarding.security.allowedIps | ||||
|    * @param blockedIPs - Array of blocked IP patterns from forwarding.security.blockedIps | ||||
|    * @param allowedIPs - Array of allowed IP patterns from security.ipAllowList | ||||
|    * @param blockedIPs - Array of blocked IP patterns from security.ipBlockList | ||||
|    * @returns true if IP is authorized, false if blocked | ||||
|    */ | ||||
|   public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean { | ||||
| @@ -94,10 +93,10 @@ export class SecurityManager { | ||||
|    * Check if the IP matches any of the glob patterns from security configuration | ||||
|    * | ||||
|    * This method checks IP addresses against glob patterns and handles IPv4/IPv6 normalization. | ||||
|    * It's used to implement IP filtering based on the forwarding.security configuration. | ||||
|    * It's used to implement IP filtering based on the route.security configuration. | ||||
|    * | ||||
|    * @param ip - The IP address to check | ||||
|    * @param patterns - Array of glob patterns from forwarding.security.allowedIps or blockedIps | ||||
|    * @param patterns - Array of glob patterns from security.ipAllowList or ipBlockList | ||||
|    * @returns true if IP matches any pattern, false otherwise | ||||
|    */ | ||||
|   private isGlobIPMatch(ip: string, patterns: string[]): boolean { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user