fix(wireguard): check every line of generated client configs

The WireGuard and AmneziaWG .conf builders (subscription service, clients
page, inbound modal and subscription page) checked only a few of the values
they write. Each builder now assembles the file line by line and renders
nothing when a line holds a control character, so every value is covered
whatever stored it. A decoded vpn:// payload is refused when it carries a
wg-quick hook directive, which no panel builder writes, and the subscription
page hides a config block that rendered nothing.

The inbound form now validates a WireGuard/AmneziaWG client's allowedIPs on
create and on update, including the stored clients a protocol switch keeps,
as the client endpoints already did.
This commit is contained in:
MHSanaei
2026-10-24 01:10:09 +02:00
parent 57fd61790c
commit 0faad4f4f3
12 changed files with 285 additions and 222 deletions
@@ -2,6 +2,7 @@ import type { HostEndpoint } from '@/lib/hosts/host-link';
import { formatInboundLabel } from '@/lib/inbounds/label';
import { preferPublicHost, resolveShareHost } from '@/lib/xray/inbound-link';
import { effectiveMtu } from '@/lib/xray/amneziawg-obfuscation';
import { joinConfLines } from '@/lib/xray/conf-lines';
import type { ClientRecord, InboundOption } from '@/hooks/useClients';
// AmneziaWG clients are wire-identical to WireGuard clients (same
@@ -58,13 +59,7 @@ export function buildAmneziaWGClientConfig(
.filter(Boolean)
.join(' - ');
// These land unescaped in [Interface]; a newline here would inject a
// config line (e.g. a rogue PostUp) into the downloaded .conf.
const privateKey = client.privateKey || client.password || '';
for (const v of [privateKey, server?.primaryDns ?? '', server?.secondaryDns ?? '', remark]) {
if (/[\r\n]/.test(v)) return '';
}
const dnsParts = [server?.primaryDns, server?.secondaryDns].filter((v) => !!v && v.trim() !== '');
const lines = ['[Interface]', `PrivateKey = ${privateKey}`, `Address = ${address}`];
if (dnsParts.length > 0) lines.push(`DNS = ${dnsParts.join(', ')}`);
@@ -109,5 +104,5 @@ export function buildAmneziaWGClientConfig(
lines.push('AllowedIPs = 0.0.0.0/0, ::/0', `Endpoint = ${endpoint}`);
if (client.keepAlive && client.keepAlive > 0)
lines.push(`PersistentKeepalive = ${client.keepAlive}`);
return lines.join('\n');
return joinConfLines(lines);
}
@@ -1,5 +1,6 @@
import type { HostEndpoint } from '@/lib/hosts/host-link';
import { formatInboundLabel } from '@/lib/inbounds/label';
import { joinConfLines } from '@/lib/xray/conf-lines';
import { preferPublicHost, resolveShareHost } from '@/lib/xray/inbound-link';
import type { ClientRecord, InboundOption } from '@/hooks/useClients';
@@ -54,5 +55,5 @@ export function buildWireguardClientConfig(
lines.push('AllowedIPs = 0.0.0.0/0, ::/0', `Endpoint = ${endpoint}`);
if (client.keepAlive && client.keepAlive > 0)
lines.push(`PersistentKeepalive = ${client.keepAlive}`);
return lines.join('\n');
return joinConfLines(lines);
}