fix(vpn,target-profiles): refresh VPN client security when target profiles change and include profile target IPs in direct destination allow-lists
This commit is contained in:
@@ -30,6 +30,9 @@ export interface IVpnManagerConfig {
|
||||
* Called at config generation time (create/export). Returns CIDRs for WireGuard AllowedIPs.
|
||||
* When not set, defaults to [subnet]. */
|
||||
getClientAllowedIPs?: (targetProfileIds: string[]) => Promise<string[]>;
|
||||
/** Resolve per-client destination allow-list IPs from target profile IDs.
|
||||
* Returns IP strings that should bypass forceTarget and go direct to the real destination. */
|
||||
getClientDirectTargets?: (targetProfileIds: string[]) => string[];
|
||||
/** Forwarding mode: 'socket' (default, userspace NAT), 'bridge' (L2 bridge to host LAN),
|
||||
* or 'hybrid' (socket default, bridge for clients with useHostIp=true) */
|
||||
forwardingMode?: 'socket' | 'bridge' | 'hybrid';
|
||||
@@ -477,18 +480,28 @@ export class VpnManager {
|
||||
const security: plugins.smartvpn.IClientSecurity = {};
|
||||
const forceSmartproxy = client.forceDestinationSmartproxy ?? true;
|
||||
|
||||
// Collect direct targets from assigned TargetProfiles (bypass forceTarget for these IPs)
|
||||
const profileDirectTargets = this.config.getClientDirectTargets?.(client.targetProfileIds || []) || [];
|
||||
|
||||
// Merge with per-client explicit allow list
|
||||
const mergedAllowList = [
|
||||
...(client.destinationAllowList || []),
|
||||
...profileDirectTargets,
|
||||
];
|
||||
|
||||
if (!forceSmartproxy) {
|
||||
// Client traffic goes directly — not forced to SmartProxy
|
||||
security.destinationPolicy = {
|
||||
default: 'allow' as const,
|
||||
blockList: client.destinationBlockList,
|
||||
};
|
||||
} else if (client.destinationAllowList?.length || client.destinationBlockList?.length) {
|
||||
// Client is forced to SmartProxy, but with per-client allow/block overrides
|
||||
} else if (mergedAllowList.length || client.destinationBlockList?.length) {
|
||||
// Client is forced to SmartProxy, but with allow/block overrides
|
||||
// (includes TargetProfile direct targets that bypass SmartProxy)
|
||||
security.destinationPolicy = {
|
||||
default: 'forceTarget' as const,
|
||||
target: '127.0.0.1',
|
||||
allowList: client.destinationAllowList,
|
||||
allowList: mergedAllowList.length ? mergedAllowList : undefined,
|
||||
blockList: client.destinationBlockList,
|
||||
};
|
||||
}
|
||||
@@ -497,6 +510,20 @@ export class VpnManager {
|
||||
return security;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all client security policies against the running daemon.
|
||||
* Call this when TargetProfiles change so destination allow-lists stay in sync.
|
||||
*/
|
||||
public async refreshAllClientSecurity(): Promise<void> {
|
||||
if (!this.vpnServer) return;
|
||||
for (const client of this.clients.values()) {
|
||||
const security = this.buildClientSecurity(client);
|
||||
if (security.destinationPolicy) {
|
||||
await this.vpnServer.updateClient(client.clientId, { security });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Private helpers ────────────────────────────────────────────────────
|
||||
|
||||
private async loadOrGenerateServerKeys(): Promise<VpnServerKeysDoc> {
|
||||
|
||||
Reference in New Issue
Block a user