feat(vpn): add tag-based VPN route access control and support configured initial VPN clients

This commit is contained in:
2026-03-30 12:07:58 +00:00
parent 43618abeba
commit eb211348d2
14 changed files with 125 additions and 33 deletions

View File

@@ -208,6 +208,12 @@ export interface IDcRouterOptions {
serverEndpoint?: string;
/** Override forwarding mode. Default: auto-detect (tun if root, socket otherwise) */
forwardingMode?: 'tun' | 'socket';
/** Pre-defined VPN clients created on startup */
clients?: Array<{
clientId: string;
serverDefinedClientTags?: string[];
description?: string;
}>;
};
}
@@ -453,7 +459,14 @@ export class DcRouter {
() => this.getConstructorRoutes(),
() => this.smartProxy,
() => this.options.http3,
() => this.options.vpnConfig?.enabled ? (this.options.vpnConfig.subnet || '10.8.0.0/24') : undefined,
this.options.vpnConfig?.enabled
? (tags?: string[]) => {
if (tags?.length && this.vpnManager) {
return this.vpnManager.getClientIpsForServerDefinedTags(tags);
}
return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
}
: undefined,
);
this.apiTokenManager = new ApiTokenManager(this.storageManager);
await this.apiTokenManager.initialize();
@@ -2086,6 +2099,11 @@ export class DcRouter {
dns: this.options.vpnConfig.dns,
serverEndpoint: this.options.vpnConfig.serverEndpoint,
forwardingMode: this.options.vpnConfig.forwardingMode,
initialClients: this.options.vpnConfig.clients,
onClientChanged: () => {
// Re-apply routes so tag-based ipAllowLists get updated
this.routeConfigManager?.applyRoutes();
},
});
await this.vpnManager.start();
@@ -2104,11 +2122,23 @@ export class DcRouter {
if (dcrouterRoute.vpn?.required) {
injectedCount++;
const existing = route.security?.ipAllowList || [];
let vpnAllowList: string[];
if (dcrouterRoute.vpn.allowedServerDefinedClientTags?.length && this.vpnManager) {
// Tag-based: only specific client IPs
vpnAllowList = this.vpnManager.getClientIpsForServerDefinedTags(
dcrouterRoute.vpn.allowedServerDefinedClientTags,
);
} else {
// No tags specified: entire VPN subnet
vpnAllowList = [vpnSubnet];
}
return {
...route,
security: {
...route.security,
ipAllowList: [...existing, vpnSubnet],
ipAllowList: [...existing, ...vpnAllowList],
},
};
}
@@ -2116,7 +2146,7 @@ export class DcRouter {
});
if (injectedCount > 0) {
logger.log('info', `VPN: Injected ipAllowList (${vpnSubnet}) into ${injectedCount} VPN-protected route(s)`);
logger.log('info', `VPN: Injected ipAllowList into ${injectedCount} VPN-protected route(s)`);
}
return result;