Give WireGuard an explicit, admin-configurable subnet field

WireGuard previously had no configurable subnet at all -- only an
implicit one, either inferred from existing clients' own addresses
(wireguardAllocationBase) or a hardcoded 10.0.0.0/24 fallback when
none exist yet. AmneziaWG, by contrast, has always had a real
server.subnetIp/subnetCidr field in its settings, editable in the
UI. User request: give WireGuard the same treatment.

Backend: explicitWireguardSubnetBase reads an optional subnetIp/
subnetCidr pair from the inbound's own settings JSON (mirroring
AmneziaWG's defaultAmneziaWGSubnetBases). defaultWireguardClients
checks it first; only when unset does it fall back to today's
inference-from-existing-clients behavior, so an inbound saved before
this field existed keeps working exactly as it always has.

Frontend: subnetIp/subnetCidr added to WireguardInboundSettingsSchema
and the inbound form (mirroring AmneziaWG's own field layout/labels),
with a real default (10.0.0.0/24, the same value the backend already
fell back to) seeded for newly created inbounds so the field starts
populated and editable rather than blank. Translated across all 13
locales.

This also structurally closes the class of bug fixed in
82cc69f5/291c47b3: with wg and awg subnets explicit and
independently controllable, an admin who wants matching addresses
across both protocols can configure them to actually agree, instead
of one silently inheriting the other's incompatible range.
This commit is contained in:
Kuzz007
2026-08-04 11:27:51 +03:00
parent 291c47b3fb
commit 569b7fbc6b
22 changed files with 175 additions and 16 deletions
+10
View File
@@ -257,12 +257,20 @@ export interface WireguardInboundSeed {
mtu?: number;
secretKey?: string;
noKernelTun?: boolean;
subnetIp?: string;
subnetCidr?: number;
}
// WireGuard is multi-client now: a new inbound holds only the server identity
// (secretKey/mtu) and starts with no clients. Clients (peers) are added later
// through the client modal, which generates each one's keypair and a unique
// tunnel address. peers stays empty for backward-compatible parsing.
//
// subnetIp/subnetCidr default to 10.0.0.0/24 here — the same value the Go
// backend has always fallen back to for an inbound with no clients yet — so
// a freshly created inbound shows an explicit, editable value from the
// start (matching AmneziaWG's own subnet field), rather than an empty one
// that silently relies on server-side inference until an admin fills it in.
export function createDefaultWireguardInboundSettings(
seed: WireguardInboundSeed = {},
): WireguardInboundSettings {
@@ -272,6 +280,8 @@ export function createDefaultWireguardInboundSettings(
peers: [],
clients: [],
noKernelTun: seed.noKernelTun ?? false,
subnetIp: seed.subnetIp ?? '10.0.0.0',
subnetCidr: seed.subnetCidr ?? 24,
};
}
@@ -24,6 +24,12 @@ export default function WireguardFields({ wgPubKey, regenInboundWg }: WireguardF
<Form.Item label={t('pages.xray.wireguard.publicKey')}>
<Input value={wgPubKey} disabled />
</Form.Item>
<FormField name={['settings', 'subnetIp']} label={t('pages.xray.wireguard.subnetIp')}>
<Input placeholder="10.0.0.0" />
</FormField>
<FormField name={['settings', 'subnetCidr']} label={t('pages.xray.wireguard.subnetCidr')}>
<InputNumber min={1} max={32} style={{ width: '100%' }} />
</FormField>
<FormField name={['settings', 'mtu']} label="MTU">
<InputNumber />
</FormField>
@@ -66,5 +66,12 @@ export const WireguardInboundSettingsSchema = z.object({
clients: z.array(WireguardClientSchema).default([]),
noKernelTun: z.boolean().default(false),
domainStrategy: WireguardDomainStrategySchema.optional(),
// Admin-configurable base subnet new clients are auto-allocated from —
// mirrors AmneziaWG's settings.server.subnetIp/subnetCidr. Optional and
// left blank by default: an inbound that never sets this keeps the
// pre-existing behavior (infer from existing clients' own addresses, else
// fall back to 10.0.0.0/24 server-side).
subnetIp: z.string().default(''),
subnetCidr: optionalClearedInt(z.number().int().min(1).max(32)),
});
export type WireguardInboundSettings = z.infer<typeof WireguardInboundSettingsSchema>;
@@ -54,6 +54,8 @@ exports[`createDefault*InboundSettings factories > wireguard 1`] = `
"noKernelTun": false,
"peers": [],
"secretKey": "QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=",
"subnetCidr": 24,
"subnetIp": "10.0.0.0",
}
`;
@@ -622,6 +622,7 @@ exports[`InboundSchema (full) fixtures > parses wireguard-server byte-stably 1`]
},
],
"secretKey": "iJ2cBkrSGqRwIfYIDIxk7hr5RXfdR93MfJUL7yqkkH8=",
"subnetIp": "",
},
"shareAddr": "",
"shareAddrStrategy": "node",
@@ -248,6 +248,7 @@ exports[`InboundSettingsSchema fixtures > parses wireguard-basic byte-stably 1`]
},
],
"secretKey": "QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=",
"subnetIp": "",
},
}
`;