fix(vpn): handle VPN forwarding mode downgrades and support runtime VPN config updates
This commit is contained in:
@@ -112,14 +112,11 @@ export class VpnManager {
|
||||
const subnet = this.getSubnet();
|
||||
const wgListenPort = this.config.wgListenPort ?? 51820;
|
||||
|
||||
// Auto-detect hybrid mode: if any persisted client uses host IP and mode is
|
||||
// 'socket' (or unset), upgrade to 'hybrid' so the daemon can handle both
|
||||
let configuredMode = this.forwardingModeOverride ?? this.config.forwardingMode ?? 'socket';
|
||||
if (anyClientUsesHostIp && configuredMode === 'socket') {
|
||||
configuredMode = 'hybrid';
|
||||
const desiredForwardingMode = this.getDesiredForwardingMode(anyClientUsesHostIp);
|
||||
if (anyClientUsesHostIp && desiredForwardingMode === 'hybrid') {
|
||||
logger.log('info', 'VPN: Auto-upgrading forwarding mode to hybrid (client with useHostIp detected)');
|
||||
}
|
||||
const forwardingMode = configuredMode === 'hybrid' ? 'hybrid' : configuredMode;
|
||||
const forwardingMode = desiredForwardingMode;
|
||||
const isBridge = forwardingMode === 'bridge';
|
||||
this.resolvedForwardingMode = forwardingMode;
|
||||
this.forwardingModeOverride = undefined;
|
||||
@@ -218,7 +215,7 @@ export class VpnManager {
|
||||
throw new Error('VPN server not running');
|
||||
}
|
||||
|
||||
await this.ensureForwardingModeForHostIpClient(opts.useHostIp === true);
|
||||
await this.ensureForwardingModeForNextClient(opts.useHostIp === true);
|
||||
|
||||
const doc = new VpnClientDoc();
|
||||
doc.clientId = opts.clientId;
|
||||
@@ -298,6 +295,7 @@ export class VpnManager {
|
||||
if (doc) {
|
||||
await doc.delete();
|
||||
}
|
||||
await this.reconcileForwardingMode();
|
||||
this.config.onClientChanged?.();
|
||||
}
|
||||
|
||||
@@ -368,8 +366,10 @@ export class VpnManager {
|
||||
await this.persistClient(client);
|
||||
|
||||
if (this.vpnServer) {
|
||||
await this.ensureForwardingModeForHostIpClient(client.useHostIp === true);
|
||||
await this.vpnServer.updateClient(clientId, this.buildClientRuntimeUpdate(client));
|
||||
const restarted = await this.reconcileForwardingMode();
|
||||
if (!restarted) {
|
||||
await this.vpnServer.updateClient(clientId, this.buildClientRuntimeUpdate(client));
|
||||
}
|
||||
}
|
||||
|
||||
this.config.onClientChanged?.();
|
||||
@@ -563,6 +563,28 @@ export class VpnManager {
|
||||
?? 'socket';
|
||||
}
|
||||
|
||||
private hasHostIpClients(extraHostIpClient = false): boolean {
|
||||
if (extraHostIpClient) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const client of this.clients.values()) {
|
||||
if (client.useHostIp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private getDesiredForwardingMode(hasHostIpClients = this.hasHostIpClients()): 'socket' | 'bridge' | 'hybrid' {
|
||||
const configuredMode = this.forwardingModeOverride ?? this.config.forwardingMode ?? 'socket';
|
||||
if (configuredMode !== 'socket') {
|
||||
return configuredMode;
|
||||
}
|
||||
return hasHostIpClients ? 'hybrid' : 'socket';
|
||||
}
|
||||
|
||||
private getDefaultDestinationPolicy(
|
||||
forwardingMode: 'socket' | 'bridge' | 'hybrid',
|
||||
useHostIp = false,
|
||||
@@ -633,16 +655,45 @@ export class VpnManager {
|
||||
};
|
||||
}
|
||||
|
||||
private async ensureForwardingModeForHostIpClient(useHostIp: boolean): Promise<void> {
|
||||
if (!useHostIp || !this.vpnServer) return;
|
||||
if (this.getResolvedForwardingMode() !== 'socket') return;
|
||||
|
||||
logger.log('info', 'VPN: Restarting server in hybrid mode to support a host-IP client');
|
||||
this.forwardingModeOverride = 'hybrid';
|
||||
private async restartWithForwardingMode(
|
||||
forwardingMode: 'socket' | 'bridge' | 'hybrid',
|
||||
reason: string,
|
||||
): Promise<void> {
|
||||
logger.log('info', `VPN: Restarting server in ${forwardingMode} mode ${reason}`);
|
||||
this.forwardingModeOverride = forwardingMode;
|
||||
await this.stop();
|
||||
await this.start();
|
||||
}
|
||||
|
||||
private async ensureForwardingModeForNextClient(useHostIp: boolean): Promise<void> {
|
||||
if (!this.vpnServer) return;
|
||||
|
||||
const desiredForwardingMode = this.getDesiredForwardingMode(this.hasHostIpClients(useHostIp));
|
||||
if (desiredForwardingMode === this.getResolvedForwardingMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.restartWithForwardingMode(desiredForwardingMode, 'to support a host-IP client');
|
||||
}
|
||||
|
||||
private async reconcileForwardingMode(): Promise<boolean> {
|
||||
if (!this.vpnServer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const desiredForwardingMode = this.getDesiredForwardingMode();
|
||||
const currentForwardingMode = this.getResolvedForwardingMode();
|
||||
if (desiredForwardingMode === currentForwardingMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const reason = desiredForwardingMode === 'socket'
|
||||
? 'because no host-IP clients remain'
|
||||
: 'to support host-IP clients';
|
||||
await this.restartWithForwardingMode(desiredForwardingMode, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async persistClient(client: VpnClientDoc): Promise<void> {
|
||||
await client.save();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user