add new plan

This commit is contained in:
Philipp Kunz 2025-05-18 15:12:36 +00:00
parent 4e78dade64
commit 8dc6b5d849

View File

@ -6,6 +6,21 @@
## Overview
Simplify the ACME/Certificate system by consolidating components, removing unnecessary abstraction layers, and integrating directly into SmartProxy's route-based architecture.
## Core Principles
1. **No backward compatibility** - Clean break from legacy implementations
2. **No migration helpers** - Users must update to new configuration format
3. **Remove all legacy code** - Delete deprecated methods and interfaces
4. **Forward-only approach** - Focus on simplicity over compatibility
5. **No complexity for edge cases** - Only support the clean, new way
## Key Discoveries from Implementation Analysis
1. **SmartProxy already supports static routes** - The 'static' type exists in TRouteActionType
2. **Path-based routing works perfectly** - The route matching system handles paths with glob patterns
3. **Dynamic route updates are safe** - SmartProxy's updateRoutes() method handles changes gracefully
4. **Priority-based routing exists** - Routes are sorted by priority, ensuring ACME routes match first
5. **No separate HTTP server needed** - ACME challenges can be regular SmartProxy routes
## Current State Analysis
### Files to be Removed/Replaced
@ -353,10 +368,12 @@ export class SmartCertManager {
/**
* Create ACME challenge route
* NOTE: SmartProxy already handles path-based routing and priority
*/
private createChallengeRoute(): IRouteConfig {
return {
name: 'acme-challenge',
priority: 1000, // High priority to ensure it's checked first
match: {
ports: 80,
path: '/.well-known/acme-challenge/*'
@ -656,7 +673,7 @@ export class CertStore {
```
### Phase 2: Update Route Types
### Phase 2: Update Route Types and Handler
#### 2.1 Update route-types.ts
```typescript
@ -683,6 +700,7 @@ export interface IStaticResponse {
/**
* Update IRouteAction to support static handlers
* NOTE: The 'static' type already exists in TRouteActionType
*/
export interface IRouteAction {
type: TRouteActionType;
@ -694,6 +712,16 @@ export interface IRouteAction {
handler?: (context: IRouteContext) => Promise<IStaticResponse>; // For static routes
}
/**
* Extend IRouteConfig to ensure challenge routes have higher priority
*/
export interface IRouteConfig {
name?: string;
match: IRouteMatch;
action: IRouteAction;
priority?: number; // Already exists - ACME routes should use high priority
}
/**
* Extended TLS configuration for route actions
*/
@ -714,6 +742,101 @@ export interface IRouteTls {
}
```
#### 2.2 Add Static Route Handler
```typescript
// Add to ts/proxies/smart-proxy/route-connection-handler.ts
/**
* Handle the route based on its action type
*/
switch (route.action.type) {
case 'forward':
return this.handleForwardAction(socket, record, route, initialChunk);
case 'redirect':
return this.handleRedirectAction(socket, record, route);
case 'block':
return this.handleBlockAction(socket, record, route);
case 'static':
return this.handleStaticAction(socket, record, route);
default:
console.log(`[${connectionId}] Unknown action type: ${(route.action as any).type}`);
socket.end();
this.connectionManager.cleanupConnection(record, 'unknown_action');
}
/**
* Handle a static action for a route
*/
private async handleStaticAction(
socket: plugins.net.Socket,
record: IConnectionRecord,
route: IRouteConfig
): Promise<void> {
const connectionId = record.id;
if (!route.action.handler) {
console.error(`[${connectionId}] Static route '${route.name}' has no handler`);
socket.end();
this.connectionManager.cleanupConnection(record, 'no_handler');
return;
}
try {
// Build route context
const context: IRouteContext = {
port: record.localPort,
domain: record.lockedDomain,
clientIp: record.remoteIP,
serverIp: socket.localAddress!,
path: record.path, // Will need to be extracted from HTTP request
isTls: record.isTLS,
tlsVersion: record.tlsVersion,
routeName: route.name,
routeId: route.name,
timestamp: Date.now(),
connectionId
};
// Call the handler
const response = await route.action.handler(context);
// Send HTTP response
const headers = response.headers || {};
headers['Content-Length'] = Buffer.byteLength(response.body).toString();
let httpResponse = `HTTP/1.1 ${response.status} ${getStatusText(response.status)}\r\n`;
for (const [key, value] of Object.entries(headers)) {
httpResponse += `${key}: ${value}\r\n`;
}
httpResponse += '\r\n';
socket.write(httpResponse);
socket.write(response.body);
socket.end();
this.connectionManager.cleanupConnection(record, 'completed');
} catch (error) {
console.error(`[${connectionId}] Error in static handler: ${error}`);
socket.end();
this.connectionManager.cleanupConnection(record, 'handler_error');
}
}
// Helper function for status text
function getStatusText(status: number): string {
const statusTexts: Record<number, string> = {
200: 'OK',
404: 'Not Found',
500: 'Internal Server Error'
};
return statusTexts[status] || 'Unknown';
}
```
### Phase 3: SmartProxy Integration
#### 3.1 Update SmartProxy class
@ -1033,49 +1156,9 @@ export class NetworkProxyBridge {
}
```
### Phase 4: Migration Guide
### Phase 4: Configuration Examples (No Migration)
#### 4.1 Configuration Migration
```typescript
// Old configuration style
const proxy = new SmartProxy({
acme: {
enabled: true,
accountEmail: 'admin@example.com',
useProduction: true,
certificateStore: './certs'
},
routes: [{
match: { ports: 443, domains: 'example.com' },
action: {
type: 'forward',
target: { host: 'backend', port: 8080 },
tls: { mode: 'terminate', certificate: 'auto' }
}
}]
});
// New configuration style
const proxy = new SmartProxy({
routes: [{
match: { ports: 443, domains: 'example.com' },
action: {
type: 'forward',
target: { host: 'backend', port: 8080 },
tls: {
mode: 'terminate',
certificate: 'auto',
acme: {
email: 'admin@example.com',
useProduction: true
}
}
}
}]
});
```
#### 4.2 Test Migration
#### 4.1 New Configuration Format ONLY
```typescript
// Update test files to use new structure
// test/test.certificate-provisioning.ts
@ -1282,6 +1365,17 @@ sed -i '/port80\//d' ts/http/index.ts
# sed -i '/smartexpress/d' ts/plugins.ts
```
#### 6.2 Key Simplifications Achieved
1. **No custom ACME wrapper** - Direct use of @push.rocks/smartacme
2. **No separate HTTP server** - ACME challenges are regular routes
3. **Built-in path routing** - SmartProxy already handles path-based matching
4. **Built-in priorities** - Routes are already sorted by priority
5. **Safe updates** - Route updates are already thread-safe
6. **Minimal new code** - Mostly configuration and integration
The simplification leverages SmartProxy's existing capabilities rather than reinventing them.
#### 6.2 Update Package.json
```json
{
@ -1304,10 +1398,10 @@ sed -i '/port80\//d' ts/http/index.ts
- Simplify NetworkProxyBridge
- Remove old certificate system
3. **Day 3: Testing & Migration**
- Migrate existing tests
- Create new integration tests
- Test migration scenarios
3. **Day 3: Testing**
- Create new tests using new format only
- No migration testing needed
- Test all new functionality
4. **Day 4: Documentation & Cleanup**
- Update all documentation
@ -1316,17 +1410,33 @@ sed -i '/port80\//d' ts/http/index.ts
## Risk Mitigation
1. **Backward Compatibility**
- Create migration helper to convert old configs
- Deprecation warnings for old methods
- Phased rollout with feature flags
1. **Static Route Handler**
- Already exists in the type system
- Just needs implementation in route-connection-handler.ts
- Low risk as it follows existing patterns
2. **Testing Strategy**
- Unit tests for each new component
- Integration tests for full workflow
- Migration tests for existing deployments
2. **Route Updates During Operation**
- SmartProxy's updateRoutes() is already thread-safe
- Sequential processing prevents race conditions
- Challenge routes are added/removed atomically
3. **Rollback Plan**
- Keep old certificate module in separate branch
- Document rollback procedures
- Test rollback scenarios
3. **Port 80 Conflicts**
- Priority-based routing ensures ACME routes match first
- Path-based matching (`/.well-known/acme-challenge/*`) is specific
- Other routes on port 80 won't interfere
4. **Error Recovery**
- SmartAcme initialization failures are handled gracefully
- Null checks prevent crashes if ACME isn't available
- Routes continue to work without certificates
5. **Testing Strategy**
- Test concurrent ACME challenges
- Test route priority conflicts
- Test certificate renewal during high traffic
- Test the new configuration format only
6. **No Migration Path**
- Breaking change is intentional
- Old configurations must be manually updated
- No compatibility shims or helpers provided