This commit is contained in:
2025-05-10 13:59:34 +00:00
parent b17af3b81d
commit ffc8b22533
28 changed files with 2827 additions and 2366 deletions

View File

@ -11,7 +11,7 @@ import type { IRouteConfig } from './models/route-types.js';
* Manages NetworkProxy integration for TLS termination
*
* NetworkProxyBridge connects SmartProxy with NetworkProxy to handle TLS termination.
* It converts route configurations to NetworkProxy configuration format and manages
* It directly maps route configurations to NetworkProxy configuration format and manages
* certificate provisioning through Port80Handler when ACME is enabled.
*
* It is used by SmartProxy for routes that have:
@ -156,35 +156,90 @@ export class NetworkProxyBridge {
}
/**
* Register domains with Port80Handler
* Register domains from routes with Port80Handler for certificate management
*
* Extracts domains from routes that require TLS termination and registers them
* with the Port80Handler for certificate issuance and renewal.
*
* @param routes The route configurations to extract domains from
*/
public registerDomainsWithPort80Handler(domains: string[]): void {
public registerDomainsWithPort80Handler(routes: IRouteConfig[]): void {
if (!this.port80Handler) {
console.log('Cannot register domains - Port80Handler not initialized');
return;
}
for (const domain of domains) {
// Skip wildcards
if (domain.includes('*')) {
console.log(`Skipping wildcard domain for ACME: ${domain}`);
continue;
// Extract domains from routes that require TLS termination
const domainsToRegister = new Set<string>();
for (const route of routes) {
// Skip routes without domains or TLS configuration
if (!route.match.domains || !route.action.tls) continue;
// Only register domains for routes that terminate TLS
if (route.action.tls.mode !== 'terminate' && route.action.tls.mode !== 'terminate-and-reencrypt') continue;
// Extract domains from route
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
// Add each domain to the set (avoiding duplicates)
for (const domain of domains) {
// Skip wildcards
if (domain.includes('*')) {
console.log(`Skipping wildcard domain for ACME: ${domain}`);
continue;
}
domainsToRegister.add(domain);
}
// Register the domain
}
// Register each unique domain with Port80Handler
for (const domain of domainsToRegister) {
try {
this.port80Handler.addDomain({
domainName: domain,
sslRedirect: true,
acmeMaintenance: true
acmeMaintenance: true,
// Include route reference if we can find it
routeReference: this.findRouteReferenceForDomain(domain, routes)
});
console.log(`Registered domain with Port80Handler: ${domain}`);
} catch (err) {
console.log(`Error registering domain ${domain} with Port80Handler: ${err}`);
}
}
}
/**
* Finds the route reference for a given domain
*
* @param domain The domain to find a route reference for
* @param routes The routes to search
* @returns The route reference if found, undefined otherwise
*/
private findRouteReferenceForDomain(domain: string, routes: IRouteConfig[]): { routeId?: string; routeName?: string } | undefined {
// Find the first route that matches this domain
for (const route of routes) {
if (!route.match.domains) continue;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
if (domains.includes(domain)) {
return {
routeId: undefined, // No explicit IDs in our current routes
routeName: route.name
};
}
}
return undefined;
}
/**
* Forwards a TLS connection to a NetworkProxy for handling
@ -260,8 +315,8 @@ export class NetworkProxyBridge {
/**
* Synchronizes routes to NetworkProxy
*
* This method converts route configurations to NetworkProxy format and updates
* the NetworkProxy with the converted configurations. It handles:
* This method directly maps route configurations to NetworkProxy format and updates
* the NetworkProxy with these configurations. It handles:
*
* - Extracting domain, target, and certificate information from routes
* - Converting TLS mode settings to NetworkProxy configuration
@ -281,9 +336,9 @@ export class NetworkProxyBridge {
// Import fs directly since it's not in plugins
const fs = await import('fs');
let certPair;
let defaultCertPair;
try {
certPair = {
defaultCertPair = {
key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'),
};
@ -295,35 +350,40 @@ export class NetworkProxyBridge {
// Use empty placeholders - NetworkProxy will use its internal defaults
// or ACME will generate proper ones if enabled
certPair = {
defaultCertPair = {
key: '',
cert: '',
};
}
// Convert routes to NetworkProxy configs
const proxyConfigs = this.convertRoutesToNetworkProxyConfigs(routes, certPair);
// Map routes directly to NetworkProxy configs
const proxyConfigs = this.mapRoutesToNetworkProxyConfigs(routes, defaultCertPair);
// Update the proxy configs
await this.networkProxy.updateProxyConfigs(proxyConfigs);
console.log(`Synced ${proxyConfigs.length} configurations to NetworkProxy`);
// Register domains with Port80Handler for certificate issuance
if (this.port80Handler) {
this.registerDomainsWithPort80Handler(routes);
}
} catch (err) {
console.log(`Error syncing routes to NetworkProxy: ${err}`);
}
}
/**
* Convert routes to NetworkProxy configuration format
* Map routes directly to NetworkProxy configuration format
*
* This method transforms route-based configuration to NetworkProxy's configuration format.
* It processes each route and creates appropriate NetworkProxy configs for domains
* that require TLS termination.
* This method directly maps route configurations to NetworkProxy's format
* without any intermediate domain-based representation. It processes each route
* and creates appropriate NetworkProxy configs for domains that require TLS termination.
*
* @param routes Array of route configurations to convert
* @param routes Array of route configurations to map
* @param defaultCertPair Default certificate to use if no custom certificate is specified
* @returns Array of NetworkProxy configurations
*/
public convertRoutesToNetworkProxyConfigs(
public mapRoutesToNetworkProxyConfigs(
routes: IRouteConfig[],
defaultCertPair: { key: string; cert: string }
): plugins.tsclass.network.IReverseProxyConfig[] {
@ -339,6 +399,9 @@ export class NetworkProxyBridge {
// Skip routes without TLS configuration
if (!route.action.tls || !route.action.target) continue;
// Skip routes that don't require TLS termination
if (route.action.tls.mode !== 'terminate' && route.action.tls.mode !== 'terminate-and-reencrypt') continue;
// Get domains from route
const domains = Array.isArray(route.match.domains)
? route.match.domains
@ -346,13 +409,6 @@ export class NetworkProxyBridge {
// Create a config for each domain
for (const domain of domains) {
// Determine if this route requires TLS termination
const needsTermination = route.action.tls.mode === 'terminate' ||
route.action.tls.mode === 'terminate-and-reencrypt';
// Skip passthrough domains for NetworkProxy
if (route.action.tls.mode === 'passthrough') continue;
// Get certificate
let certKey = defaultCertPair.key;
let certCert = defaultCertPair.cert;
@ -370,14 +426,14 @@ export class NetworkProxyBridge {
const targetPort = route.action.target.port;
// Create NetworkProxy config
// Create the NetworkProxy config
const config: plugins.tsclass.network.IReverseProxyConfig = {
hostName: domain,
privateKey: certKey,
publicKey: certCert,
destinationIps: targetHosts,
destinationPorts: [targetPort],
// Headers handling happens in the request handler level
destinationPorts: [targetPort]
// Note: We can't include additional metadata as it's not supported in the interface
};
configs.push(config);
@ -387,6 +443,17 @@ export class NetworkProxyBridge {
return configs;
}
/**
* @deprecated This method is kept for backward compatibility.
* Use mapRoutesToNetworkProxyConfigs() instead.
*/
public convertRoutesToNetworkProxyConfigs(
routes: IRouteConfig[],
defaultCertPair: { key: string; cert: string }
): plugins.tsclass.network.IReverseProxyConfig[] {
return this.mapRoutesToNetworkProxyConfigs(routes, defaultCertPair);
}
/**
* @deprecated This method is deprecated and will be removed in a future version.
* Use syncRoutesToNetworkProxy() instead.
@ -395,14 +462,18 @@ export class NetworkProxyBridge {
* simply forwards to syncRoutesToNetworkProxy().
*/
public async syncDomainConfigsToNetworkProxy(): Promise<void> {
console.log('Method syncDomainConfigsToNetworkProxy is deprecated. Use syncRoutesToNetworkProxy instead.');
console.log('DEPRECATED: Method syncDomainConfigsToNetworkProxy will be removed in a future version.');
console.log('Please use syncRoutesToNetworkProxy() instead for direct route-based configuration.');
await this.syncRoutesToNetworkProxy(this.settings.routes || []);
}
/**
* Request a certificate for a specific domain
*
* @param domain The domain to request a certificate for
* @param routeName Optional route name to associate with this certificate
*/
public async requestCertificate(domain: string): Promise<boolean> {
public async requestCertificate(domain: string, routeName?: string): Promise<boolean> {
// Delegate to Port80Handler if available
if (this.port80Handler) {
try {
@ -412,14 +483,30 @@ export class NetworkProxyBridge {
console.log(`Certificate already exists for ${domain}`);
return true;
}
// Register the domain for certificate issuance
this.port80Handler.addDomain({
// Build the domain options
const domainOptions: any = {
domainName: domain,
sslRedirect: true,
acmeMaintenance: true
});
acmeMaintenance: true,
};
// Add route reference if available
if (routeName) {
domainOptions.routeReference = {
routeName
};
} else {
// Try to find a route reference from the current routes
const routeReference = this.findRouteReferenceForDomain(domain, this.settings.routes || []);
if (routeReference) {
domainOptions.routeReference = routeReference;
}
}
// Register the domain for certificate issuance
this.port80Handler.addDomain(domainOptions);
console.log(`Domain ${domain} registered for certificate issuance`);
return true;
} catch (err) {
@ -427,7 +514,7 @@ export class NetworkProxyBridge {
return false;
}
}
// Fall back to NetworkProxy if Port80Handler is not available
if (!this.networkProxy) {
console.log('Cannot request certificate - NetworkProxy not initialized');