AmneziaWG 2.0 (#2226)

* feat!: awg

* feat: add description to fields, add I5

* fix: awg i18n

* fix: types

* minor fixes

* Remove TODO comment from types.ts

Removed TODO comment for more validation.

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
This commit is contained in:
Alexander Chepurnoy
2025-11-12 13:46:16 +07:00
committed by GitHub
parent a8ba7f7247
commit 6a282e6ab9
20 changed files with 1491 additions and 21 deletions
+21
View File
@@ -5,6 +5,9 @@ import type { InterfaceType } from '#db/repositories/interface/types';
const WG_DEBUG = debug('WireGuard');
const generateRandomHeaderValue = () =>
Math.floor(Math.random() * 2147483642) + 5;
class WireGuard {
/**
* Save and sync config
@@ -196,6 +199,24 @@ class WireGuard {
wgInterface = await Database.interfaces.get();
WG_DEBUG('New Wireguard Keys generated successfully.');
}
if (WG_ENV.WG_EXECUTABLE === 'awg' && wgInterface.h1 === 0) {
WG_DEBUG('Generating random AmneziaWG obfuscation parameters...');
const headers = new Set<number>();
while (headers.size < 4) {
headers.add(generateRandomHeaderValue());
}
const [h1, h2, h3, h4] = Array.from(headers);
wgInterface.h1 = h1!;
wgInterface.h2 = h2!;
wgInterface.h3 = h3!;
wgInterface.h4 = h4!;
Database.interfaces.update(wgInterface);
}
WG_DEBUG(`Starting Wireguard Interface ${wgInterface.name}...`);
await this.#saveWireguardConfig(wgInterface);
await wg.down(wgInterface.name).catch(() => {});
+18 -9
View File
@@ -12,7 +12,23 @@ export const OLD_ENV = {
PASSWORD_HASH: process.env.PASSWORD_HASH,
};
const OVERRIDE_AUTO_AWG = process.env.OVERRIDE_AUTO_AWG?.toLowerCase();
const detectAwg = async (): Promise<'awg' | 'wg'> => {
/** TODO: delete on next major version */
if (process.env.EXPERIMENTAL_AWG === 'true') {
const OVERRIDE_AUTO_AWG = process.env.OVERRIDE_AUTO_AWG?.toLowerCase();
if (
OVERRIDE_AUTO_AWG === ('wg' as const) ||
OVERRIDE_AUTO_AWG === ('awg' as const)
) {
return OVERRIDE_AUTO_AWG;
} else {
return await exec('modinfo amneziawg')
.then(() => 'awg' as const)
.catch(() => 'wg' as const);
}
} else return 'wg';
};
export const WG_ENV = {
/** UI is hosted on HTTP instead of HTTPS */
@@ -21,14 +37,7 @@ export const WG_ENV = {
PORT: assertEnv('PORT'),
/** If IPv6 should be disabled */
DISABLE_IPV6: process.env.DISABLE_IPV6 === 'true',
/** Override automatic detection */
OVERRIDE_AUTO_AWG:
OVERRIDE_AUTO_AWG === ('wg' as const) ||
OVERRIDE_AUTO_AWG === ('awg' as const)
? OVERRIDE_AUTO_AWG
: undefined,
/** TODO: delete on next major version */
EXPERIMENTAL_AWG: process.env.EXPERIMENTAL_AWG === 'true',
WG_EXECUTABLE: await detectAwg(),
};
export const WG_INITIAL_ENV = {
+12
View File
@@ -26,6 +26,18 @@ export const MtuSchema = z
.min(1024, { message: t('zod.mtu') })
.max(9000, { message: t('zod.mtu') });
export const JcSchema = z.number().min(1).max(128).nullable();
export const JminSchema = z.number().max(1279).nullable();
export const JmaxSchema = z.number().max(1280).nullable();
export const SSchema = z.number().max(1132).nullable();
export const HSchema = z.number().min(5).max(2147483647).nullable();
export const ISchema = z.string().nullable();
export const PortSchema = z
.number({ message: t('zod.port') })
.min(1, { message: t('zod.port') })
+61 -12
View File
@@ -9,17 +9,7 @@ type Options = {
enableIpv6?: boolean;
};
let wgExecutable: 'awg' | 'wg' = 'wg';
if (WG_ENV.EXPERIMENTAL_AWG) {
if (WG_ENV.OVERRIDE_AUTO_AWG !== undefined) {
wgExecutable = WG_ENV.OVERRIDE_AUTO_AWG;
} else {
wgExecutable = await exec('modinfo amneziawg')
.then(() => 'awg' as const)
.catch(() => 'wg' as const);
}
}
const wgExecutable = WG_ENV.WG_EXECUTABLE;
export const wg = {
generateServerPeer: (
@@ -62,6 +52,35 @@ AllowedIPs = ${allowedIps.join(', ')}${extraLines.length ? `\n${extraLines.join(
`${ipv4Addr}/${cidr4.prefix}` +
(enableIpv6 ? `, ${ipv6Addr}/${cidr6.prefix}` : '');
let awgLines: string[] = [];
if (wgExecutable === 'awg') {
const parameters = {
Jc: wgInterface.jC,
Jmin: wgInterface.jMin,
Jmax: wgInterface.jMax,
S1: wgInterface.s1,
S2: wgInterface.s2,
S3: wgInterface.s3,
S4: wgInterface.s4,
i1: wgInterface.i1,
i2: wgInterface.i2,
i3: wgInterface.i3,
i4: wgInterface.i4,
i5: wgInterface.i5,
H1: wgInterface.h1,
H2: wgInterface.h2,
H3: wgInterface.h3,
H4: wgInterface.h4,
} as const;
awgLines = Object.entries(parameters)
.filter(([_, value]) => !!value)
.map(([key, value]) => `${key} = ${value}`);
}
const extraLines = [...awgLines].filter((v) => v !== null);
return `# Note: Do not edit this file directly.
# Your changes will be overwritten!
@@ -71,6 +90,7 @@ PrivateKey = ${wgInterface.privateKey}
Address = ${address}
ListenPort = ${wgInterface.port}
MTU = ${wgInterface.mtu}
${extraLines.length ? `${extraLines.join('\n')}\n` : ''}
PreUp = ${iptablesTemplate(hooks.preUp, wgInterface)}
PostUp = ${iptablesTemplate(hooks.postUp, wgInterface)}
PreDown = ${iptablesTemplate(hooks.preDown, wgInterface)}
@@ -100,7 +120,36 @@ PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`;
const dnsLine =
dnsServers.length > 0 ? `DNS = ${dnsServers.join(', ')}` : null;
const extraLines = [dnsLine, ...hookLines].filter((v) => v !== null);
let awgLines: string[] = [];
if (wgExecutable === 'awg') {
const parameters = {
Jc: client.jC,
Jmin: client.jMin,
Jmax: client.jMax,
S1: wgInterface.s1,
S2: wgInterface.s2,
S3: wgInterface.s3,
S4: wgInterface.s4,
i1: client.i1,
i2: client.i2,
i3: client.i3,
i4: client.i4,
i5: client.i5,
H1: wgInterface.h1,
H2: wgInterface.h2,
H3: wgInterface.h3,
H4: wgInterface.h4,
} as const;
awgLines = Object.entries(parameters)
.filter(([_, value]) => !!value)
.map(([key, value]) => `${key} = ${value}`);
}
const extraLines = [dnsLine, ...hookLines, ...awgLines].filter(
(v) => v !== null
);
return `[Interface]
PrivateKey = ${client.privateKey}