468 lines
10 KiB
Markdown
468 lines
10 KiB
Markdown
|
# SmartProxy Port Handling
|
||
|
|
||
|
This document covers all the port handling capabilities in SmartProxy, including port range specification, dynamic port mapping, and runtime port management.
|
||
|
|
||
|
## Port Range Syntax
|
||
|
|
||
|
SmartProxy offers flexible port range specification through the `TPortRange` type, which can be defined in three different ways:
|
||
|
|
||
|
### 1. Single Port
|
||
|
|
||
|
```typescript
|
||
|
// Match a single port
|
||
|
{
|
||
|
match: {
|
||
|
ports: 443
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 2. Array of Specific Ports
|
||
|
|
||
|
```typescript
|
||
|
// Match multiple specific ports
|
||
|
{
|
||
|
match: {
|
||
|
ports: [80, 443, 8080]
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 3. Port Range
|
||
|
|
||
|
```typescript
|
||
|
// Match a range of ports
|
||
|
{
|
||
|
match: {
|
||
|
ports: [{ from: 8000, to: 8100 }]
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 4. Mixed Port Specifications
|
||
|
|
||
|
You can combine different port specification methods in a single rule:
|
||
|
|
||
|
```typescript
|
||
|
// Match both specific ports and port ranges
|
||
|
{
|
||
|
match: {
|
||
|
ports: [80, 443, { from: 8000, to: 8100 }]
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Port Forwarding Options
|
||
|
|
||
|
SmartProxy offers several ways to handle port forwarding from source to target:
|
||
|
|
||
|
### 1. Static Port Forwarding
|
||
|
|
||
|
Forward to a fixed target port:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: 8080
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 2. Preserve Source Port
|
||
|
|
||
|
Forward to the same port on the target:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: 'preserve'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 3. Dynamic Port Mapping
|
||
|
|
||
|
Use a function to determine the target port based on connection context:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: (context) => {
|
||
|
// Calculate port based on request details
|
||
|
return 8000 + (context.port % 100);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Port Selection Context
|
||
|
|
||
|
When using dynamic port mapping functions, you have access to a rich context object that provides details about the connection:
|
||
|
|
||
|
```typescript
|
||
|
interface IRouteContext {
|
||
|
// Connection information
|
||
|
port: number; // The matched incoming port
|
||
|
domain?: string; // The domain from SNI or Host header
|
||
|
clientIp: string; // The client's IP address
|
||
|
serverIp: string; // The server's IP address
|
||
|
path?: string; // URL path (for HTTP connections)
|
||
|
query?: string; // Query string (for HTTP connections)
|
||
|
headers?: Record<string, string>; // HTTP headers (for HTTP connections)
|
||
|
|
||
|
// TLS information
|
||
|
isTls: boolean; // Whether the connection is TLS
|
||
|
tlsVersion?: string; // TLS version if applicable
|
||
|
|
||
|
// Route information
|
||
|
routeName?: string; // The name of the matched route
|
||
|
routeId?: string; // The ID of the matched route
|
||
|
|
||
|
// Additional properties
|
||
|
timestamp: number; // The request timestamp
|
||
|
connectionId: string; // Unique connection identifier
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Common Port Mapping Patterns
|
||
|
|
||
|
### 1. Port Offset Mapping
|
||
|
|
||
|
Forward traffic to target ports with a fixed offset:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: (context) => context.port + 1000
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 2. Domain-Based Port Mapping
|
||
|
|
||
|
Forward to different backend ports based on the domain:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: (context) => {
|
||
|
switch (context.domain) {
|
||
|
case 'api.example.com': return 8001;
|
||
|
case 'admin.example.com': return 8002;
|
||
|
case 'staging.example.com': return 8003;
|
||
|
default: return 8000;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### 3. Load Balancing with Hash-Based Distribution
|
||
|
|
||
|
Distribute connections across a port range using a deterministic hash function:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: (context) => {
|
||
|
// Simple hash function to ensure consistent mapping
|
||
|
const hostname = context.domain || '';
|
||
|
const hash = hostname.split('').reduce((a, b) => a + b.charCodeAt(0), 0);
|
||
|
return 8000 + (hash % 10); // Map to ports 8000-8009
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## IPv6-Mapped IPv4 Compatibility
|
||
|
|
||
|
SmartProxy automatically handles IPv6-mapped IPv4 addresses for optimal compatibility. When a connection from an IPv4 address (e.g., `192.168.1.1`) arrives as an IPv6-mapped address (`::ffff:192.168.1.1`), the system normalizes these addresses for consistent matching.
|
||
|
|
||
|
This is particularly important when:
|
||
|
|
||
|
1. Matching client IP restrictions in route configurations
|
||
|
2. Preserving source IP for outgoing connections
|
||
|
3. Tracking connections and rate limits
|
||
|
|
||
|
No special configuration is needed - the system handles this normalization automatically.
|
||
|
|
||
|
## Dynamic Port Management
|
||
|
|
||
|
SmartProxy allows for runtime port configuration changes without requiring a restart.
|
||
|
|
||
|
### Adding and Removing Ports
|
||
|
|
||
|
```typescript
|
||
|
// Get the SmartProxy instance
|
||
|
const proxy = new SmartProxy({ /* config */ });
|
||
|
|
||
|
// Add a new listening port
|
||
|
await proxy.addListeningPort(8081);
|
||
|
|
||
|
// Remove a listening port
|
||
|
await proxy.removeListeningPort(8082);
|
||
|
```
|
||
|
|
||
|
### Runtime Route Updates
|
||
|
|
||
|
```typescript
|
||
|
// Get current routes
|
||
|
const currentRoutes = proxy.getRoutes();
|
||
|
|
||
|
// Add new route for the new port
|
||
|
const newRoute = {
|
||
|
name: 'New Dynamic Route',
|
||
|
match: {
|
||
|
ports: 8081,
|
||
|
domains: ['dynamic.example.com']
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: 9000
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Update the route configuration
|
||
|
await proxy.updateRoutes([...currentRoutes, newRoute]);
|
||
|
|
||
|
// Remove routes for a specific port
|
||
|
const routesWithout8082 = currentRoutes.filter(route => {
|
||
|
const ports = proxy.routeManager.expandPortRange(route.match.ports);
|
||
|
return !ports.includes(8082);
|
||
|
});
|
||
|
await proxy.updateRoutes(routesWithout8082);
|
||
|
```
|
||
|
|
||
|
## Performance Considerations
|
||
|
|
||
|
### Port Range Expansion
|
||
|
|
||
|
When using large port ranges, SmartProxy uses internal caching to optimize performance. For example, a range like `{ from: 1000, to: 2000 }` is expanded only once and then cached for future use.
|
||
|
|
||
|
### Port Range Validation
|
||
|
|
||
|
The system automatically validates port ranges to ensure:
|
||
|
|
||
|
1. Port numbers are within the valid range (1-65535)
|
||
|
2. The "from" value is not greater than the "to" value in range specifications
|
||
|
3. Port ranges do not contain duplicate entries
|
||
|
|
||
|
Invalid port ranges will be logged as warnings and skipped during configuration.
|
||
|
|
||
|
## Configuration Recipes
|
||
|
|
||
|
### Global Port Range
|
||
|
|
||
|
Listen on a large range of ports and forward to the same ports on a backend:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
name: 'Global port range forwarding',
|
||
|
match: {
|
||
|
ports: [{ from: 8000, to: 9000 }]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'backend.example.com',
|
||
|
port: 'preserve'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Domain-Specific Port Ranges
|
||
|
|
||
|
Different port ranges for different domain groups:
|
||
|
|
||
|
```typescript
|
||
|
[
|
||
|
{
|
||
|
name: 'API port range',
|
||
|
match: {
|
||
|
ports: [{ from: 8000, to: 8099 }]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'api.backend.example.com',
|
||
|
port: 'preserve'
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
name: 'Admin port range',
|
||
|
match: {
|
||
|
ports: [{ from: 9000, to: 9099 }]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'admin.backend.example.com',
|
||
|
port: 'preserve'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
```
|
||
|
|
||
|
### Mixed Internal/External Port Forwarding
|
||
|
|
||
|
Forward specific high-numbered ports to standard ports on internal servers:
|
||
|
|
||
|
```typescript
|
||
|
[
|
||
|
{
|
||
|
name: 'Web server forwarding',
|
||
|
match: {
|
||
|
ports: [8080, 8443]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'web.internal',
|
||
|
port: (context) => context.port === 8080 ? 80 : 443
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
name: 'Database forwarding',
|
||
|
match: {
|
||
|
ports: [15432]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'db.internal',
|
||
|
port: 5432
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
```
|
||
|
|
||
|
## Debugging Port Configurations
|
||
|
|
||
|
When troubleshooting port forwarding issues, enable detailed logging:
|
||
|
|
||
|
```typescript
|
||
|
const proxy = new SmartProxy({
|
||
|
routes: [ /* your routes */ ],
|
||
|
enableDetailedLogging: true
|
||
|
});
|
||
|
```
|
||
|
|
||
|
This will log:
|
||
|
- Port configuration during startup
|
||
|
- Port matching decisions during routing
|
||
|
- Dynamic port function results
|
||
|
- Connection details including source and target ports
|
||
|
|
||
|
## Port Security Considerations
|
||
|
|
||
|
### Restricting Ports
|
||
|
|
||
|
For security, you may want to restrict which ports can be accessed by specific clients:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
name: 'Restricted port range',
|
||
|
match: {
|
||
|
ports: [{ from: 8000, to: 9000 }],
|
||
|
clientIp: ['10.0.0.0/8'] // Only internal network can access these ports
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'internal.example.com',
|
||
|
port: 'preserve'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Rate Limiting by Port
|
||
|
|
||
|
Apply different rate limits for different port ranges:
|
||
|
|
||
|
```typescript
|
||
|
{
|
||
|
name: 'API ports with rate limiting',
|
||
|
match: {
|
||
|
ports: [{ from: 8000, to: 8100 }]
|
||
|
},
|
||
|
action: {
|
||
|
type: 'forward',
|
||
|
target: {
|
||
|
host: 'api.example.com',
|
||
|
port: 'preserve'
|
||
|
},
|
||
|
security: {
|
||
|
rateLimit: {
|
||
|
enabled: true,
|
||
|
maxRequests: 100,
|
||
|
window: 60 // 60 seconds
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Best Practices
|
||
|
|
||
|
1. **Use Specific Port Ranges**: Instead of large ranges (e.g., 1-65535), use specific ranges for specific purposes
|
||
|
|
||
|
2. **Prioritize Routes**: When multiple routes could match, use the `priority` field to ensure the most specific route is matched first
|
||
|
|
||
|
3. **Name Your Routes**: Use descriptive names to make debugging easier, especially when using port ranges
|
||
|
|
||
|
4. **Use Preserve Port Where Possible**: Using `port: 'preserve'` is more efficient and easier to maintain than creating multiple specific mappings
|
||
|
|
||
|
5. **Limit Dynamic Port Functions**: While powerful, complex port functions can be harder to debug; prefer simple map or math-based functions
|
||
|
|
||
|
6. **Use Port Variables**: For complex setups, define your port ranges as variables for easier maintenance:
|
||
|
|
||
|
```typescript
|
||
|
const API_PORTS = [{ from: 8000, to: 8099 }];
|
||
|
const ADMIN_PORTS = [{ from: 9000, to: 9099 }];
|
||
|
|
||
|
const routes = [
|
||
|
{
|
||
|
name: 'API Routes',
|
||
|
match: { ports: API_PORTS, /* ... */ },
|
||
|
// ...
|
||
|
},
|
||
|
{
|
||
|
name: 'Admin Routes',
|
||
|
match: { ports: ADMIN_PORTS, /* ... */ },
|
||
|
// ...
|
||
|
}
|
||
|
];
|
||
|
```
|