diff --git a/src/app/pages/admin/config.vue b/src/app/pages/admin/config.vue index e408d030..892fde05 100644 --- a/src/app/pages/admin/config.vue +++ b/src/app/pages/admin/config.vue @@ -48,14 +48,59 @@ /> - Amnezia - - - - - - - + AmneziaWG Obfuscation Parameters + + + + + +
* {{ $t('awg.*') }}
+ + + + + +
{{ $t('form.actions') }} diff --git a/src/app/pages/admin/interface.vue b/src/app/pages/admin/interface.vue index fa3b14ce..9a5c896f 100644 --- a/src/app/pages/admin/interface.vue +++ b/src/app/pages/admin/interface.vue @@ -22,22 +22,107 @@ /> - Amnezia - - - - - - - - - - - - - - - + AmneziaWG Obfuscation Parameters + + + + + + + +
* {{ $t('awg.*') }}
+ + + + + + + + + + + +
{{ $t('form.actions') }} diff --git a/src/app/pages/clients/[id].vue b/src/app/pages/clients/[id].vue index 80957198..0e15de2d 100644 --- a/src/app/pages/clients/[id].vue +++ b/src/app/pages/clients/[id].vue @@ -83,14 +83,59 @@ /> - Amnezia - - - - - - - + AmneziaWG Obfuscation Parameters + + + + + +
* {{ $t('awg.*') }}
+ + + + + +
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f76022cc..508621bb 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -237,5 +237,40 @@ "postUp": "PostUp", "preDown": "PreDown", "postDown": "PostDown" + }, + "awg": { + "jCLabel": "Junk packet count (Jc)", + "jCDescription": "Number of junk packets to send (1-128, recommended: 4-12)", + "jMinLabel": "Junk packet min size (Jmin)", + "jMinDescription": "Minimum size of junk packets (0-1279*, recommended: 8, must be < Jmax)", + "jMaxLabel": "Junk packet max size (Jmax)", + "jMaxDescription": "Maximum size of junk packets (1-1280*, recommended: 80, must be > Jmin)", + "s1Label": "Init packet junk size (S1)", + "s1Description": "Init packet junk size (0-1132[1280* - 148 = 1132], recommended: 15-150, S1+56 ≠ S2)", + "s2Label": "Response packet junk size (S2)", + "s2Description": "Response packet junk size (0-1188[1280* - 92 = 1188], recommended: 15-150)", + "s3Label": "Cookie reply packet junk size (S3)", + "s3Description": "Cookie reply packet junk size", + "s4Label": "Transport packet junk size (S4)", + "s4Description": "Transport packet junk size", + "i1Label": "Special junk packet 1 (I1)", + "i1Description": "Protocol mimic packet in hex format: ", + "i2Label": "Special junk packet 2 (I2)", + "i2Description": "Protocol mimic packet in hex format: ", + "i3Label": "Special junk packet 3 (I3)", + "i3Description": "Protocol mimic packet in hex format: ", + "i4Label": "Special junk packet 4 (I4)", + "i4Description": "Protocol mimic packet in hex format: ", + "i5Label": "Special junk packet 5 (I5)", + "i5Description": "Protocol mimic packet in hex format: ", + "h1Label": "Init magic header (H1)", + "h1Description": "Init packet header value (5-2147483647, must be unique from H2-H4)", + "h2Label": "Response magic header (H2)", + "h2Description": "Response packet header value (5-2147483647, must be unique from H1, H3, H4)", + "h3Label": "Cookie reply magic header (H3)", + "h3Description": "Cookie reply packet header value (5-2147483647, must be unique from H1, H2, H4)", + "h4Label": "Transport magic header (H4)", + "h4Description": "Transport packet header value (5-2147483647, must be unique from H1-H3)", + "*": "Values depend on the MTU" } } diff --git a/src/server/database/migrations/0002_optimal_pixie.sql b/src/server/database/migrations/0002_keen_sleepwalker.sql similarity index 75% rename from src/server/database/migrations/0002_optimal_pixie.sql rename to src/server/database/migrations/0002_keen_sleepwalker.sql index 9344b7b2..d19c0d29 100644 --- a/src/server/database/migrations/0002_optimal_pixie.sql +++ b/src/server/database/migrations/0002_keen_sleepwalker.sql @@ -5,6 +5,7 @@ ALTER TABLE `clients_table` ADD `i1` text;--> statement-breakpoint ALTER TABLE `clients_table` ADD `i2` text;--> statement-breakpoint ALTER TABLE `clients_table` ADD `i3` text;--> statement-breakpoint ALTER TABLE `clients_table` ADD `i4` text;--> statement-breakpoint +ALTER TABLE `clients_table` ADD `i5` text;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `j_c` integer DEFAULT 7;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `j_min` integer DEFAULT 10;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `j_max` integer DEFAULT 1000;--> statement-breakpoint @@ -16,14 +17,16 @@ ALTER TABLE `interfaces_table` ADD `i1` text;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `i2` text;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `i3` text;--> statement-breakpoint ALTER TABLE `interfaces_table` ADD `i4` text;--> statement-breakpoint -ALTER TABLE `interfaces_table` ADD `h1` integer;--> statement-breakpoint -ALTER TABLE `interfaces_table` ADD `h2` integer;--> statement-breakpoint -ALTER TABLE `interfaces_table` ADD `h3` integer;--> statement-breakpoint -ALTER TABLE `interfaces_table` ADD `h4` integer;--> statement-breakpoint +ALTER TABLE `interfaces_table` ADD `i5` text;--> statement-breakpoint +ALTER TABLE `interfaces_table` ADD `h1` integer DEFAULT 0;--> statement-breakpoint +ALTER TABLE `interfaces_table` ADD `h2` integer DEFAULT 0;--> statement-breakpoint +ALTER TABLE `interfaces_table` ADD `h3` integer DEFAULT 0;--> statement-breakpoint +ALTER TABLE `interfaces_table` ADD `h4` integer DEFAULT 0;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_j_c` integer DEFAULT 7;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_j_min` integer DEFAULT 10;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_j_max` integer DEFAULT 1000;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_i1` text;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_i2` text;--> statement-breakpoint ALTER TABLE `user_configs_table` ADD `default_i3` text;--> statement-breakpoint -ALTER TABLE `user_configs_table` ADD `default_i4` text; \ No newline at end of file +ALTER TABLE `user_configs_table` ADD `default_i4` text;--> statement-breakpoint +ALTER TABLE `user_configs_table` ADD `default_i5` text; \ No newline at end of file diff --git a/src/server/database/migrations/meta/0002_snapshot.json b/src/server/database/migrations/meta/0002_snapshot.json index 1acd28d3..43488542 100644 --- a/src/server/database/migrations/meta/0002_snapshot.json +++ b/src/server/database/migrations/meta/0002_snapshot.json @@ -1,7 +1,7 @@ { "version": "6", "dialect": "sqlite", - "id": "56cf26d3-6178-4089-9d94-533d3ea09a26", + "id": "e09bc17a-dab6-45a3-a09c-57af222b08fb", "prevId": "78de2e52-c4a8-4900-86c5-92f34739623a", "tables": { "clients_table": { @@ -186,6 +186,13 @@ "notNull": false, "autoincrement": false }, + "i5": { + "name": "i5", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, "dns": { "name": "dns", "type": "text", @@ -564,33 +571,44 @@ "notNull": false, "autoincrement": false }, + "i5": { + "name": "i5", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, "h1": { "name": "h1", "type": "integer", "primaryKey": false, "notNull": false, - "autoincrement": false + "autoincrement": false, + "default": 0 }, "h2": { "name": "h2", "type": "integer", "primaryKey": false, "notNull": false, - "autoincrement": false + "autoincrement": false, + "default": 0 }, "h3": { "name": "h3", "type": "integer", "primaryKey": false, "notNull": false, - "autoincrement": false + "autoincrement": false, + "default": 0 }, "h4": { "name": "h4", "type": "integer", "primaryKey": false, "notNull": false, - "autoincrement": false + "autoincrement": false, + "default": 0 }, "enabled": { "name": "enabled", @@ -886,6 +904,13 @@ "notNull": false, "autoincrement": false }, + "default_i5": { + "name": "default_i5", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, "host": { "name": "host", "type": "text", diff --git a/src/server/database/migrations/meta/_journal.json b/src/server/database/migrations/meta/_journal.json index a58478f9..9e4a5f4c 100644 --- a/src/server/database/migrations/meta/_journal.json +++ b/src/server/database/migrations/meta/_journal.json @@ -19,8 +19,8 @@ { "idx": 2, "version": "6", - "when": 1760948971158, - "tag": "0002_optimal_pixie", + "when": 1761298328460, + "tag": "0002_keen_sleepwalker", "breakpoints": true } ] diff --git a/src/server/database/repositories/client/schema.ts b/src/server/database/repositories/client/schema.ts index 048b0763..64368ef6 100644 --- a/src/server/database/repositories/client/schema.ts +++ b/src/server/database/repositories/client/schema.ts @@ -43,6 +43,7 @@ export const client = sqliteTable('clients_table', { i2: text(), i3: text(), i4: text(), + i5: text(), dns: text({ mode: 'json' }).$type(), serverEndpoint: text('server_endpoint'), enabled: int({ mode: 'boolean' }).notNull(), diff --git a/src/server/database/repositories/client/service.ts b/src/server/database/repositories/client/service.ts index 9218603e..97953cda 100644 --- a/src/server/database/repositories/client/service.ts +++ b/src/server/database/repositories/client/service.ts @@ -221,6 +221,7 @@ export class ClientService { i2: clientConfig.defaultI2, i3: clientConfig.defaultI3, i4: clientConfig.defaultI4, + i5: clientConfig.defaultI5, persistentKeepalive: clientConfig.defaultPersistentKeepalive, serverAllowedIps: [], enabled: true, diff --git a/src/server/database/repositories/client/types.ts b/src/server/database/repositories/client/types.ts index 304c083e..ae2ba852 100644 --- a/src/server/database/repositories/client/types.ts +++ b/src/server/database/repositories/client/types.ts @@ -79,6 +79,7 @@ export const ClientUpdateSchema = schemaForType()( i2: ISchema, i3: ISchema, i4: ISchema, + i5: ISchema, persistentKeepalive: PersistentKeepaliveSchema, serverEndpoint: AddressSchema.nullable(), dns: DnsSchema.nullable(), diff --git a/src/server/database/repositories/interface/schema.ts b/src/server/database/repositories/interface/schema.ts index 0c2992a4..c0ee71c0 100644 --- a/src/server/database/repositories/interface/schema.ts +++ b/src/server/database/repositories/interface/schema.ts @@ -3,10 +3,6 @@ import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { userConfig, hooks } from '../../schema'; -// H1/H2/H3/H4 — must be unique among each other; recommended range is from 5 to 2147483647 inclusive -const generateRandomHeaderValue = () => - Math.floor(Math.random() * 2147483642) + 5; - // maybe support multiple interfaces in the future export const wgInterface = sqliteTable('interfaces_table', { name: text().primaryKey(), @@ -28,10 +24,11 @@ export const wgInterface = sqliteTable('interfaces_table', { i2: text(), i3: text(), i4: text(), - h1: int().$defaultFn(generateRandomHeaderValue), - h2: int().$defaultFn(generateRandomHeaderValue), - h3: int().$defaultFn(generateRandomHeaderValue), - h4: int().$defaultFn(generateRandomHeaderValue), + i5: text(), + h1: int().default(0), + h2: int().default(0), + h3: int().default(0), + h4: int().default(0), // does nothing yet enabled: int({ mode: 'boolean' }).notNull(), createdAt: text('created_at') diff --git a/src/server/database/repositories/interface/types.ts b/src/server/database/repositories/interface/types.ts index 02793bf1..3051d3b3 100644 --- a/src/server/database/repositories/interface/types.ts +++ b/src/server/database/repositories/interface/types.ts @@ -42,6 +42,7 @@ export const InterfaceUpdateSchema = schemaForType()( i2: ISchema, i3: ISchema, i4: ISchema, + i5: ISchema, h1: HSchema, h2: HSchema, h3: HSchema, diff --git a/src/server/database/repositories/userConfig/schema.ts b/src/server/database/repositories/userConfig/schema.ts index ba26c822..3e8497a1 100644 --- a/src/server/database/repositories/userConfig/schema.ts +++ b/src/server/database/repositories/userConfig/schema.ts @@ -25,6 +25,7 @@ export const userConfig = sqliteTable('user_configs_table', { defaultI2: text('default_i2'), defaultI3: text('default_i3'), defaultI4: text('default_i4'), + defaultI5: text('default_i5'), host: text().notNull(), port: int().notNull(), createdAt: text('created_at') diff --git a/src/server/database/repositories/userConfig/types.ts b/src/server/database/repositories/userConfig/types.ts index a99af7f1..3043ea14 100644 --- a/src/server/database/repositories/userConfig/types.ts +++ b/src/server/database/repositories/userConfig/types.ts @@ -33,6 +33,7 @@ export const UserConfigUpdateSchema = schemaForType()( defaultI2: ISchema, defaultI3: ISchema, defaultI4: ISchema, + defaultI5: ISchema, host: host, }) ); diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index b024ebff..0a4a57d8 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -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(); + + 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(() => {}); diff --git a/src/server/utils/wgHelper.ts b/src/server/utils/wgHelper.ts index 4376e47b..d97baf3d 100644 --- a/src/server/utils/wgHelper.ts +++ b/src/server/utils/wgHelper.ts @@ -67,6 +67,7 @@ AllowedIPs = ${allowedIps.join(', ')}${extraLines.length ? `\n${extraLines.join( i2: wgInterface.i2, i3: wgInterface.i3, i4: wgInterface.i4, + i5: wgInterface.i5, H1: wgInterface.h1, H2: wgInterface.h2, H3: wgInterface.h3, @@ -134,6 +135,7 @@ PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`; i2: client.i2, i3: client.i3, i4: client.i4, + i5: client.i5, H1: wgInterface.h1, H2: wgInterface.h2, H3: wgInterface.h3,