feat(amneziawg): add a button to randomize obfuscation parameters

Mirrors the existing key-regenerate button next to the private key
field. Client-side randomization matches the ranges/constraints of
GenerateObfuscation20's "default" preset (internal/amneziawg/params.go)
closely enough for a form suggestion — the user can still hand-edit any
field afterward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kuzz007
2026-07-25 10:20:52 +03:00
parent f075cd75d6
commit 5617cdcf31
4 changed files with 70 additions and 2 deletions
@@ -332,6 +332,58 @@ export default function InboundFormModal({
setV('settings.server.publicKey', kp.publicKey);
};
// Randomizes the AmneziaWG 2.0 obfuscation set client-side, mirroring the
// ranges/constraints of the Go backend's amneziawg.GenerateObfuscation20
// "default" preset (internal/amneziawg/params.go) closely enough for a form
// suggestion — exact parity isn't required since the user can still edit
// any field afterward, and a fresh, non-crypto-grade random value here is
// no weaker than what the backend would have generated on first save.
const regenInboundAwgObfuscation = () => {
const randInt = (min: number, max: number) => min + Math.floor(Math.random() * (max - min + 1));
const jc = randInt(3, 6);
const jmin = randInt(40, 89);
const jmax = jmin + randInt(50, 250);
const s1 = randInt(15, 150);
let s2 = randInt(15, 150);
while (s1 + 56 === s2) {
s2 = randInt(15, 150);
}
const s3 = randInt(8, 55);
const s4 = randInt(4, 27);
// Four non-overlapping "low-high" ranges for H1-H4: split the space into
// four bands and take a random sub-range from each (>= 1000 wide, low
// bound >= 5 since 1-4 are reserved for vanilla WireGuard message types).
const hMax = 2147483647;
const hMinWidth = 1000;
const lo = 5;
const bandSize = Math.floor((hMax - lo + 1) / 4);
const hRanges = Array.from({ length: 4 }, (_, i) => {
const bandLo = lo + i * bandSize;
const bandHi = bandLo + bandSize - 1;
const start = randInt(bandLo, bandHi - hMinWidth - 1);
const end = randInt(start + hMinWidth, bandHi - 1);
return `${start}-${end}`;
});
const i1 = `<r ${randInt(32, 256)}>`;
setV('settings.server.jc', jc);
setV('settings.server.jmin', jmin);
setV('settings.server.jmax', jmax);
setV('settings.server.s1', s1);
setV('settings.server.s2', s2);
setV('settings.server.s3', s3);
setV('settings.server.s4', s4);
setV('settings.server.h1', hRanges[0]);
setV('settings.server.h2', hRanges[1]);
setV('settings.server.h3', hRanges[2]);
setV('settings.server.h4', hRanges[3]);
setV('settings.server.i1', i1);
};
const matchesVlessAuth = (
block: { id?: string; label?: string } | undefined | null,
authId: string,
@@ -676,7 +728,13 @@ export default function InboundFormModal({
<>
{protocol === Protocols.WIREGUARD && <WireguardFields wgPubKey={wgPubKey} regenInboundWg={regenInboundWg} />}
{protocol === Protocols.AMNEZIAWG && <AmneziawgFields awgPubKey={awgPubKey} regenInboundAwg={regenInboundAwg} />}
{protocol === Protocols.AMNEZIAWG && (
<AmneziawgFields
awgPubKey={awgPubKey}
regenInboundAwg={regenInboundAwg}
regenInboundAwgObfuscation={regenInboundAwgObfuscation}
/>
)}
{protocol === Protocols.TUN && <TunFields />}
@@ -7,9 +7,10 @@ import { FormField } from '@/components/form/rhf';
interface AmneziawgFieldsProps {
awgPubKey: string;
regenInboundAwg: () => void;
regenInboundAwgObfuscation: () => void;
}
export default function AmneziawgFields({ awgPubKey, regenInboundAwg }: AmneziawgFieldsProps) {
export default function AmneziawgFields({ awgPubKey, regenInboundAwg, regenInboundAwgObfuscation }: AmneziawgFieldsProps) {
const { t } = useTranslation();
return (
<>
@@ -46,6 +47,11 @@ export default function AmneziawgFields({ awgPubKey, regenInboundAwg }: Amneziaw
>
<Input placeholder="eth0" />
</FormField>
<Form.Item label={t('pages.xray.amneziawg.obfuscation')}>
<Button icon={<ReloadOutlined />} onClick={regenInboundAwgObfuscation}>
{t('pages.xray.amneziawg.regenerateObfuscation')}
</Button>
</Form.Item>
<FormField name={['settings', 'server', 'jc']} label={t('pages.xray.amneziawg.jc')}>
<InputNumber min={0} style={{ width: '100%' }} />
</FormField>
+2
View File
@@ -1933,6 +1933,8 @@
"secondaryDns": "Secondary DNS",
"externalInterface": "External Interface",
"externalInterfaceHint": "Host NIC for NAT (PostUp/PostDown). Leave empty to auto-detect.",
"obfuscation": "Obfuscation parameters",
"regenerateObfuscation": "Regenerate",
"jc": "Jc (junk packet count)",
"jmin": "Jmin (junk packet min size)",
"jmax": "Jmax (junk packet max size)",
+2
View File
@@ -1816,6 +1816,8 @@
"secondaryDns": "Резервный DNS",
"externalInterface": "Внешний интерфейс",
"externalInterfaceHint": "Сетевой интерфейс хоста для NAT (PostUp/PostDown). Оставьте пустым для автоопределения.",
"obfuscation": "Параметры обфускации",
"regenerateObfuscation": "Сгенерировать заново",
"jc": "Jc (кол-во мусорных пакетов)",
"jmin": "Jmin (мин. размер мусорного пакета)",
"jmax": "Jmax (макс. размер мусорного пакета)",