feat: enhance AWG2 support with additional parameters and backward compatibility improvements

This commit is contained in:
infosave2007
2026-04-15 20:26:48 +03:00
parent 65b57344b4
commit 1065b1d849
6 changed files with 413 additions and 61 deletions
+2
View File
@@ -74,3 +74,5 @@ scripts/fix_server_visibility.sh
scripts/remote_fix_client_create.sh
scripts/retest_client_api.sh
scripts/awg2_retest_final.sh
migrations/065_aivpn_prebuilt.sql
migrations/069_fix_aivpn_prebuilt_binary.sql
+3 -3
View File
@@ -356,7 +356,7 @@ class InstallProtocolManager
$awgParams = $result['awg_params'] ?? null;
if (!is_array($awgParams)) {
$flat = [];
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'] as $k) {
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'] as $k) {
if (array_key_exists($k, $result) && $result[$k] !== '' && $result[$k] !== null) {
$flat[$k] = $result[$k];
}
@@ -1037,7 +1037,7 @@ class InstallProtocolManager
private static function parseWireGuardConfig(string $config): array
{
$lines = preg_split('/\r?\n/', $config);
$awgKeys = ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'];
$awgKeys = ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'];
$awgParams = [];
$listenPort = null;
@@ -1333,7 +1333,7 @@ class InstallProtocolManager
$resolvedAwgParams = $res['awg_params'] ?? null;
if (!is_array($resolvedAwgParams)) {
$candidate = [];
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4'] as $k) {
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'] as $k) {
if (array_key_exists($k, $res)) {
$candidate[$k] = $res[$k];
}
+66 -6
View File
@@ -74,12 +74,36 @@ class QrUtil
return self::urlsafe_b64_encode($header . $compressed);
}
public static function encodeOldPayloadFromConf(string $confText): string
public static function encodeOldPayloadFromConf(string $confText, string $protocolSlug = ''): string
{
$payload = self::buildOldEnvelopeFromConf($confText);
// For AWG2, use simple format: header + plain config text (like real Amnezia app)
// For other protocols, use the old JSON+compression format for backward compatibility
if ($protocolSlug === 'awg2') {
return self::encodeSimpleConf($confText);
}
// Old format for backward compatibility with regular AWG
$payload = self::buildOldEnvelopeFromConf($confText, $protocolSlug);
return self::encodeOldPayloadFromJson(json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
/**
* Encode config in simple format used by real Amnezia app for AWG2:
* Header (8 bytes): version (4) + length (4) + config text
* No compression, no JSON wrapper
*
* Note: In real app, bytes 8-11 contain first 4 bytes of config text,
* not a separate field. So header is only 8 bytes.
*/
private static function encodeSimpleConf(string $confText): string
{
$version = 0x07C00100; // Amnezia magic version number
$length = strlen($confText);
$header = pack('N2', $version, $length);
return self::urlsafe_b64_encode($header . $confText);
}
private static function resolveServerDescription(?string $endpointHost): string
{
$desc = (string) ($endpointHost ?? '');
@@ -181,11 +205,18 @@ class QrUtil
'H2' => null,
'H3' => null,
'H4' => null,
'I1' => null,
'I2' => null,
'I3' => null,
'I4' => null,
'I5' => null,
'Jc' => null,
'Jmin' => null,
'Jmax' => null,
'S1' => null,
'S2' => null,
'S3' => null,
'S4' => null,
];
foreach (explode("\n", $conf) as $line) {
$line = trim($line);
@@ -203,11 +234,18 @@ class QrUtil
'H2' => (string) ($params['H2'] ?? ''),
'H3' => (string) ($params['H3'] ?? ''),
'H4' => (string) ($params['H4'] ?? ''),
'I1' => (string) ($params['I1'] ?? ''),
'I2' => (string) ($params['I2'] ?? ''),
'I3' => (string) ($params['I3'] ?? ''),
'I4' => (string) ($params['I4'] ?? ''),
'I5' => (string) ($params['I5'] ?? ''),
'Jc' => (string) ($params['Jc'] ?? ''),
'Jmax' => (string) ($params['Jmax'] ?? ''),
'Jmin' => (string) ($params['Jmin'] ?? ''),
'S1' => (string) ($params['S1'] ?? ''),
'S2' => (string) ($params['S2'] ?? ''),
'S3' => (string) ($params['S3'] ?? ''),
'S4' => (string) ($params['S4'] ?? ''),
'allowed_ips' => $allowedIps ?: ['0.0.0.0/0', '::/0'],
'clientId' => $clientPubKey ?: '',
'client_ip' => preg_replace('/\/(\d{1,2})$/', '', (string) ($address ?? '')),
@@ -249,7 +287,7 @@ class QrUtil
return $vars;
}
private static function buildOldEnvelopeFromConf(string $conf): array
private static function buildOldEnvelopeFromConf(string $conf, string $protocolSlug = ''): array
{
$endpointHost = null;
$endpointPort = null;
@@ -327,11 +365,18 @@ class QrUtil
'H2' => null,
'H3' => null,
'H4' => null,
'I1' => null,
'I2' => null,
'I3' => null,
'I4' => null,
'I5' => null,
'Jc' => null,
'Jmin' => null,
'Jmax' => null,
'S1' => null,
'S2' => null,
'S3' => null,
'S4' => null,
];
foreach (explode("\n", $conf) as $line) {
$line = trim($line);
@@ -349,11 +394,18 @@ class QrUtil
'H2' => (string) ($params['H2'] ?? ''),
'H3' => (string) ($params['H3'] ?? ''),
'H4' => (string) ($params['H4'] ?? ''),
'I1' => (string) ($params['I1'] ?? ''),
'I2' => (string) ($params['I2'] ?? ''),
'I3' => (string) ($params['I3'] ?? ''),
'I4' => (string) ($params['I4'] ?? ''),
'I5' => (string) ($params['I5'] ?? ''),
'Jc' => (string) ($params['Jc'] ?? ''),
'Jmax' => (string) ($params['Jmax'] ?? ''),
'Jmin' => (string) ($params['Jmin'] ?? ''),
'S1' => (string) ($params['S1'] ?? ''),
'S2' => (string) ($params['S2'] ?? ''),
'S3' => (string) ($params['S3'] ?? ''),
'S4' => (string) ($params['S4'] ?? ''),
'allowed_ips' => $allowedIps ?: ['0.0.0.0/0', '::/0'],
'clientId' => $clientPubKey ?: '',
'client_ip' => preg_replace('/\/(\d{1,2})$/', '', (string) ($address ?? '')),
@@ -388,11 +440,19 @@ class QrUtil
'last_config' => json_encode($lastConfigObj, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT),
'port' => (string) $endpointPort,
'transport_proto' => 'udp',
],
'container' => 'amnezia-awg',
] + ($protocolSlug === 'awg2' ? [
'I1' => (string) ($params['I1'] ?? ''),
'I2' => (string) ($params['I2'] ?? ''),
'I3' => (string) ($params['I3'] ?? ''),
'I4' => (string) ($params['I4'] ?? ''),
'I5' => (string) ($params['I5'] ?? ''),
'S3' => (string) ($params['S3'] ?? ''),
'S4' => (string) ($params['S4'] ?? ''),
] : []),
'container' => $protocolSlug === 'awg2' ? 'amnezia-awg2' : 'amnezia-awg',
],
],
'defaultContainer' => 'amnezia-awg',
'defaultContainer' => $protocolSlug === 'awg2' ? 'amnezia-awg2' : 'amnezia-awg',
'description' => $serverDesc,
'dns1' => $dns1,
'dns2' => $dns2,
+131 -52
View File
@@ -142,26 +142,14 @@ class VpnClient
}
}
$defaultAwgParams = self::getAwgParamDefaults($slug);
// Add AWG parameters (use UPPERCASE keys internal logic)
foreach (['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'] as $key) {
foreach (array_keys($defaultAwgParams) as $key) {
if (isset($cleanAwgParams[$key])) {
$vars[$key] = $cleanAwgParams[$key];
} else {
// Default values for AWG params (Fallback only)
$defaults = [
'JC' => 5,
'JMIN' => 100,
'JMAX' => 200,
'S1' => 50,
'S2' => 100,
'S3' => 20,
'S4' => 10,
'H1' => 1,
'H2' => 2,
'H3' => 3,
'H4' => 4,
];
$vars[$key] = $defaults[$key] ?? 0;
$vars[$key] = $defaultAwgParams[$key];
}
}
@@ -176,6 +164,11 @@ class VpnClient
if (!isset($vars['Jmax']) && isset($vars['JMAX'])) {
$vars['Jmax'] = (string) $vars['JMAX'];
}
foreach (['S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'] as $key) {
if (!isset($vars[$key]) && isset($vars[strtoupper($key)])) {
$vars[$key] = (string) $vars[strtoupper($key)];
}
}
// Generate config from template
if ($protoRow && !empty($protoRow['output_template'])) {
@@ -190,12 +183,13 @@ class VpnClient
$serverData['preshared_key'],
$serverData['host'],
$serverData['vpn_port'],
is_array($awgParams) ? $awgParams : []
is_array($awgParams) ? $awgParams : [],
$slug
);
}
self::addClientToServer($serverData, $keys['public'], $clientIP);
$qrCode = self::generateQRCode($config);
$qrCode = self::generateQRCode($config, $slug);
$priv = $keys['private'];
$pub = $keys['public'];
$psk = $serverData['preshared_key'];
@@ -501,7 +495,7 @@ class VpnClient
$vars['last_config_json'] = json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
$qrCode = self::generateQRCode($config);
$qrCode = self::generateQRCode($config, $slug);
$priv = '';
$pub = '';
@@ -636,7 +630,8 @@ class VpnClient
$presharedKey,
$serverData['host'],
(int) $serverData['vpn_port'],
$awgParams
$awgParams,
(string) ($serverData['install_protocol'] ?? '')
);
}
@@ -654,7 +649,7 @@ class VpnClient
$vars['last_config_json'] = json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
$qrCode = $config !== '' ? self::generateQRCode($config) : '';
$qrCode = $config !== '' ? self::generateQRCode($config, $serverData['install_protocol'] ?? '') : '';
$status = strtolower($clientData['status'] ?? 'active') === 'disabled' ? 'disabled' : 'active';
$expiresAt = $clientData['expires_at'] ?? null;
@@ -784,12 +779,50 @@ class VpnClient
/**
* Auto-sync server keys from running container (for externally installed protocols)
*/
private static function getAwgParamDefaults(string $protocolSlug = ''): array
{
if ($protocolSlug === 'awg2') {
return [
'JC' => 5,
'JMIN' => 10,
'JMAX' => 50,
'S1' => 51,
'S2' => 125,
'S3' => 13,
'S4' => 9,
'H1' => '1443912531-1981073285',
'H2' => '1984025557-2135018048',
'H3' => '2145217268-2146643749',
'H4' => '2146790761-2146860793',
'I1' => '<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>',
'I2' => '',
'I3' => '',
'I4' => '',
'I5' => '',
];
}
return [
'JC' => 5,
'JMIN' => 100,
'JMAX' => 200,
'S1' => 50,
'S2' => 100,
'S3' => 20,
'S4' => 10,
'H1' => 1,
'H2' => 2,
'H3' => 3,
'H4' => 4,
];
}
private static function extractAwgParamsFromWg0Conf(VpnServer $server, string $containerName, string $confPath): array
{
$awgParams = [];
$awgLinesCmd = sprintf(
"docker exec %s sh -c \"grep -E '^[[:space:]]*(Jc|Jmin|Jmax|S1|S2|S3|S4|H1|H2|H3|H4)[[:space:]]*=' %s 2>/dev/null || true\"",
"docker exec %s sh -c \"grep -E '^[[:space:]]*(Jc|Jmin|Jmax|S1|S2|S3|S4|H1|H2|H3|H4|I1|I2|I3|I4|I5)[[:space:]]*=' %s 2>/dev/null || true\"",
escapeshellarg($containerName),
escapeshellarg($confPath)
);
@@ -800,9 +833,10 @@ class VpnClient
if ($line === '') {
continue;
}
if (preg_match('/^(Jc|Jmin|Jmax|S1|S2|S3|S4|H1|H2|H3|H4)\s*=\s*(\d+)\s*$/i', $line, $m)) {
if (preg_match('/^(Jc|Jmin|Jmax|S1|S2|S3|S4|H1|H2|H3|H4|I1|I2|I3|I4|I5)\s*=\s*(.*)$/i', $line, $m)) {
$k = strtoupper($m[1]);
$awgParams[$k] = (int) $m[2];
$value = trim($m[2]);
$awgParams[$k] = ctype_digit($value) ? (int) $value : $value;
}
}
@@ -911,10 +945,18 @@ class VpnClient
// Legacy attempt: some builds print jc/jmin/... in `wg show` output.
$wgShowCmd = "docker exec $containerName wg show wg0 2>/dev/null";
$wgOutput = (string) $server->executeCommand($wgShowCmd, true);
$paramNames = ['jc', 'jmin', 'jmax', 's1', 's2', 's3', 's4', 'h1', 'h2', 'h3', 'h4'];
$paramNames = ['jc', 'jmin', 'jmax', 's1', 's2', 's3', 's4', 'h1', 'h2', 'h3', 'h4', 'i1', 'i2', 'i3', 'i4', 'i5'];
foreach ($paramNames as $param) {
if (preg_match('/^\s*' . preg_quote($param, '/') . ':\s*(\d+)/mi', $wgOutput, $matches)) {
$awgParams[strtoupper($param)] = (int) $matches[1];
// For H1-H4 parameters, expect format like "1443912531-1981073285" (two values with dash)
// For other parameters, expect single integer value
if (in_array($param, ['h1', 'h2', 'h3', 'h4'], true)) {
if (preg_match('/^\s*' . preg_quote($param, '/') . ':\s*(\d+-\d+)/mi', $wgOutput, $matches)) {
$awgParams[strtoupper($param)] = $matches[1];
}
} else {
if (preg_match('/^\s*' . preg_quote($param, '/') . ':\s*(\d+)/mi', $wgOutput, $matches)) {
$awgParams[strtoupper($param)] = (int) $matches[1];
}
}
}
@@ -962,24 +1004,60 @@ class VpnClient
string $presharedKey,
string $serverHost,
int $serverPort,
array $awgParams
array $awgParams,
string $protocolSlug = ''
): string {
// Get default parameters for the protocol
$defaultParams = self::getAwgParamDefaults($protocolSlug);
// Normalize $awgParams keys to uppercase for consistency
$normalizedAwgParams = [];
foreach ($awgParams as $k => $v) {
$normalizedAwgParams[strtoupper($k)] = $v;
}
// Merge: use server params only if they have correct format, otherwise use defaults
// This is critical for H1-H4 which must have "value1-value2" format
$finalParams = $defaultParams;
foreach ($normalizedAwgParams as $key => $value) {
$upperKey = strtoupper($key);
// For H1-H4 parameters, only use server value if it has the correct "value1-value2" format
if (in_array($upperKey, ['H1', 'H2', 'H3', 'H4'], true)) {
if (is_string($value) && preg_match('/^\d+-\d+$/', $value)) {
$finalParams[$upperKey] = $value;
}
// Otherwise keep the default value
} else {
// For other parameters, use server value if present
$finalParams[$upperKey] = $value;
}
}
$config = "[Interface]\n";
$config .= "PrivateKey = {$privateKey}\n";
$config .= "Address = {$clientIP}/32\n";
$config .= "DNS = 1.1.1.1, 1.0.0.1\n";
$config .= "PrivateKey = {$privateKey}\n";
// Add AWG parameters
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'] as $key) {
if (isset($awgParams[$key])) {
$config .= "{$key} = {$awgParams[$key]}\n";
continue;
// Add AWG parameters (in the order used by Amnezia app)
// For awg2 include I1-I5, S3, S4; for regular awg only H1-H4, Jc, Jmin, Jmax, S1, S2
// Order: Jc, Jmin, Jmax, S1, S2, S3, S4, H1, H2, H3, H4, I1, I2, I3, I4, I5
$paramKeys = ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'];
if ($protocolSlug === 'awg2') {
$paramKeys = array_merge($paramKeys, ['I1', 'I2', 'I3', 'I4', 'I5']);
}
foreach ($paramKeys as $key) {
$value = null;
if (isset($finalParams[$key])) {
$value = $finalParams[$key];
} elseif (isset($finalParams[strtoupper($key)])) {
$value = $finalParams[strtoupper($key)];
}
// Accept uppercase keys too (JC/JMIN/JMAX/...)
$alt = strtoupper($key);
if (isset($awgParams[$alt])) {
$config .= "{$key} = {$awgParams[$alt]}\n";
// Always add parameter if it's defined (even if empty for I2-I5)
if ($value !== null) {
$config .= "{$key} = {$value}\n";
}
}
@@ -1122,7 +1200,7 @@ class VpnClient
* Generate QR code for configuration using Amnezia format
* Uses working QrUtil from /Users/oleg/Documents/amnezia
*/
private static function generateQRCode(string $config): string
private static function generateQRCode(string $config, string $protocolSlug = ''): string
{
require_once __DIR__ . '/QrUtil.php';
@@ -1158,8 +1236,8 @@ class VpnClient
}
// Fallback for WireGuard / default
// Use old Amnezia format with Qt/QDataStream encoding
$payloadOld = QrUtil::encodeOldPayloadFromConf($config);
// Use old Amnezia format with Qt/QDataStream encoding, but pass protocol slug
$payloadOld = QrUtil::encodeOldPayloadFromConf($config, $protocolSlug);
$dataUri = QrUtil::pngBase64($payloadOld);
return $dataUri;
} catch (Throwable $e) {
@@ -1484,7 +1562,7 @@ class VpnClient
// by duplicating them into canonical uppercase AWG keys.
foreach ($awgParams as $k => $v) {
$uk = strtoupper((string) $k);
if (in_array($uk, ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'], true) && !isset($awgParams[$uk])) {
if (in_array($uk, ['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'], true) && !isset($awgParams[$uk])) {
$awgParams[$uk] = $v;
}
}
@@ -1524,12 +1602,7 @@ class VpnClient
}
}
if (!isset($awgParams['S3'])) {
$awgParams['S3'] = 0;
}
if (!isset($awgParams['S4'])) {
$awgParams['S4'] = 0;
}
$awgParams = array_merge(self::getAwgParamDefaults($slug), $awgParams);
// Still missing? Refuse to overwrite config with template defaults.
foreach ($needKeys as $k) {
@@ -1567,7 +1640,7 @@ class VpnClient
'dns_servers' => (string) ($serverData['dns_servers'] ?? '1.1.1.1, 1.0.0.1'),
];
foreach (['JC', 'JMIN', 'JMAX', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'] as $key) {
foreach (array_keys(self::getAwgParamDefaults($slug)) as $key) {
if (isset($awgParams[$key])) {
$vars[$key] = $awgParams[$key];
}
@@ -1582,6 +1655,11 @@ class VpnClient
if (!isset($vars['Jmax']) && isset($vars['JMAX'])) {
$vars['Jmax'] = (string) $vars['JMAX'];
}
foreach (['S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'] as $key) {
if (!isset($vars[$key]) && isset($vars[strtoupper($key)])) {
$vars[$key] = (string) $vars[strtoupper($key)];
}
}
if ($protoRow && !empty($protoRow['output_template'])) {
require_once __DIR__ . '/ProtocolService.php';
@@ -1594,11 +1672,12 @@ class VpnClient
$presharedKeyForConfig,
(string) ($serverData['host'] ?? ''),
(int) ($serverData['vpn_port'] ?? 0),
$awgParams
$awgParams,
$slug
);
}
$qrCode = self::generateQRCode($config);
$qrCode = self::generateQRCode($config, $slug);
$pdo = DB::conn();
$stmt = $pdo->prepare('UPDATE vpn_clients SET config = ?, qr_code = ?, preshared_key = ? WHERE id = ?');
@@ -0,0 +1,211 @@
-- Complete AWG2 support with original Amnezia parameters, including I1-I5.
UPDATE protocols
SET output_template = '[Interface]
Address = {{client_ip}}/32
DNS = {{dns_servers}}
PrivateKey = {{private_key}}
Jc = {{Jc}}
Jmin = {{Jmin}}
Jmax = {{Jmax}}
S1 = {{S1}}
S2 = {{S2}}
S3 = {{S3}}
S4 = {{S4}}
H1 = {{H1}}
H2 = {{H2}}
H3 = {{H3}}
H4 = {{H4}}
I1 = {{I1}}
I2 = {{I2}}
I3 = {{I3}}
I4 = {{I4}}
I5 = {{I5}}
[Peer]
PublicKey = {{server_public_key}}
PresharedKey = {{preshared_key}}
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = {{server_host}}:{{server_port}}
PersistentKeepalive = 25',
install_script = '#!/bin/bash
set -euo pipefail
CONTAINER_NAME="${SERVER_CONTAINER:-amnezia-awg2}"
PORT_RANGE_START=${PORT_RANGE_START:-30000}
PORT_RANGE_END=${PORT_RANGE_END:-65000}
VPN_PORT="${SERVER_PORT:-$((RANDOM % (PORT_RANGE_END - PORT_RANGE_START + 1) + PORT_RANGE_START))}"
MTU=${MTU:-1280}
if ! command -v git &> /dev/null; then
apt-get update -qq && apt-get install -y -qq git >/dev/null 2>&1
fi
mkdir -p /opt/amnezia/awg2
if [ ! -d /opt/amnezia/awg2/src ]; then
git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-go.git /opt/amnezia/awg2/src
fi
docker build --no-cache -t amnezia-awg2 /opt/amnezia/awg2/src
EXISTING=$(docker ps -aq -f "name=$CONTAINER_NAME" 2>/dev/null | head -1)
if [ -z "$EXISTING" ]; then
docker run -d --name "$CONTAINER_NAME" --restart always --cap-add=NET_ADMIN --device /dev/net/tun -p "${VPN_PORT}:${VPN_PORT}/udp" -v /opt/amnezia/awg2:/opt/amnezia/awg amnezia-awg2 sh -c "while [ ! -f /opt/amnezia/awg/wg0.conf ]; do sleep 1; done; WG_QUICK_USERSPACE_IMPLEMENTATION=amneziawg-go awg-quick up /opt/amnezia/awg/wg0.conf && sleep infinity"
sleep 2
else
STATUS=$(docker inspect --format="{{.State.Status}}" "$CONTAINER_NAME" 2>/dev/null || echo "")
if [ "$STATUS" != "running" ]; then
docker start "$CONTAINER_NAME" >/dev/null 2>&1 || true
fi
fi
if [ -f /opt/amnezia/awg2/wg0.conf ]; then
PORT=$(grep -E "^ListenPort" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
PSK=$(cat /opt/amnezia/awg2/wireguard_psk.key 2>/dev/null || true)
if [ -z "$PSK" ]; then
PSK=$(grep -E "^PresharedKey" /opt/amnezia/awg2/wg0.conf | cut -d= -f2 | tr -d "[:space:]")
fi
PUBKEY=$(cat /opt/amnezia/awg2/wireguard_server_public_key.key 2>/dev/null || true)
if [ -z "$PUBKEY" ]; then
PRIVKEY=$(cat /opt/amnezia/awg2/wireguard_server_private_key.key 2>/dev/null || true)
if [ -n "$PRIVKEY" ]; then
PUBKEY=$(echo "$PRIVKEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
fi
fi
echo "Using existing AmneziaWG 2.0 configuration"
echo "Port: ${PORT:-$VPN_PORT}"
if [ -n "${PUBKEY:-}" ]; then echo "Server Public Key: $PUBKEY"; fi
if [ -n "${PSK:-}" ]; then echo "PresharedKey = $PSK"; fi
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
echo "Server Host: $EXTERNAL_IP"
for P in Jc Jmin Jmax S1 S2 S3 S4 H1 H2 H3 H4 I1 I2 I3 I4 I5; do
VAL=$(sed -n -E "s/^[[:space:]]*$P[[:space:]]*=[[:space:]]*//p" /opt/amnezia/awg2/wg0.conf | head -1 | tr -d "\r")
if [ -n "$VAL" ] || [[ "$P" =~ ^I[2-5]$ ]]; then echo "Variable: $P=$VAL"; fi
done
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"
exit 0
fi
PRIVATE_KEY=$(docker exec "$CONTAINER_NAME" wg genkey)
PUBLIC_KEY=$(echo "$PRIVATE_KEY" | docker exec -i "$CONTAINER_NAME" wg pubkey)
PRESHARED_KEY=$(docker exec "$CONTAINER_NAME" wg genpsk)
JC=5
JMIN=10
JMAX=50
S1_VAL=51
S2_VAL=125
S3_VAL=13
S4_VAL=9
H1_VAL="1443912531-1981073285"
H2_VAL="1984025557-2135018048"
H3_VAL="2145217268-2146643749"
H4_VAL="2146790761-2146860793"
I1_VAL="<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>"
I2_VAL=""
I3_VAL=""
I4_VAL=""
I5_VAL=""
{
echo "[Interface]"
echo "PrivateKey = $PRIVATE_KEY"
echo "Address = 10.8.1.1/24"
echo "ListenPort = $VPN_PORT"
echo "Jc = $JC"
echo "Jmin = $JMIN"
echo "Jmax = $JMAX"
echo "S1 = $S1_VAL"
echo "S2 = $S2_VAL"
echo "S3 = $S3_VAL"
echo "S4 = $S4_VAL"
echo "H1 = $H1_VAL"
echo "H2 = $H2_VAL"
echo "H3 = $H3_VAL"
echo "H4 = $H4_VAL"
echo "I1 = $I1_VAL"
echo "PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE"
echo "PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE"
} > /opt/amnezia/awg2/wg0.conf
echo "$PRIVATE_KEY" > /opt/amnezia/awg2/wireguard_server_private_key.key
echo "$PUBLIC_KEY" > /opt/amnezia/awg2/wireguard_server_public_key.key
echo "$PRESHARED_KEY" > /opt/amnezia/awg2/wireguard_psk.key
echo "[]" > /opt/amnezia/awg2/clientsTable
EXTERNAL_IP=$(curl -s -4 ifconfig.me 2>/dev/null || curl -s -4 icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
echo "AmneziaWG 2.0 installed successfully"
echo "Port: $VPN_PORT"
echo "Server Public Key: $PUBLIC_KEY"
echo "PresharedKey = $PRESHARED_KEY"
echo "Server Host: $EXTERNAL_IP"
echo "Variable: Jc=$JC"
echo "Variable: Jmin=$JMIN"
echo "Variable: Jmax=$JMAX"
echo "Variable: S1=$S1_VAL"
echo "Variable: S2=$S2_VAL"
echo "Variable: S3=$S3_VAL"
echo "Variable: S4=$S4_VAL"
echo "Variable: H1=$H1_VAL"
echo "Variable: H2=$H2_VAL"
echo "Variable: H3=$H3_VAL"
echo "Variable: H4=$H4_VAL"
echo "Variable: I1=$I1_VAL"
echo "Variable: dns_servers=1.1.1.1, 1.0.0.1"'
WHERE slug = 'awg2';
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
SELECT p.id, 'I1', 'text', '<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>', 'Original AmneziaWG packet template I1', false
FROM protocols p WHERE p.slug = 'awg2'
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'I1');
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
SELECT p.id, 'I2', 'text', '', 'Original AmneziaWG packet template I2', false
FROM protocols p WHERE p.slug = 'awg2'
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'I2');
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
SELECT p.id, 'I3', 'text', '', 'Original AmneziaWG packet template I3', false
FROM protocols p WHERE p.slug = 'awg2'
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'I3');
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
SELECT p.id, 'I4', 'text', '', 'Original AmneziaWG packet template I4', false
FROM protocols p WHERE p.slug = 'awg2'
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'I4');
INSERT INTO protocol_variables (protocol_id, variable_name, variable_type, default_value, description, required)
SELECT p.id, 'I5', 'text', '', 'Original AmneziaWG packet template I5', false
FROM protocols p WHERE p.slug = 'awg2'
AND NOT EXISTS (SELECT 1 FROM protocol_variables WHERE protocol_id = p.id AND variable_name = 'I5');
UPDATE protocol_variables pv
JOIN protocols p ON p.id = pv.protocol_id
SET pv.default_value = CASE pv.variable_name
WHEN 'Jc' THEN '5'
WHEN 'Jmin' THEN '10'
WHEN 'Jmax' THEN '50'
WHEN 'S1' THEN '51'
WHEN 'S2' THEN '125'
WHEN 'S3' THEN '13'
WHEN 'S4' THEN '9'
WHEN 'H1' THEN '1443912531-1981073285'
WHEN 'H2' THEN '1984025557-2135018048'
WHEN 'H3' THEN '2145217268-2146643749'
WHEN 'H4' THEN '2146790761-2146860793'
ELSE pv.default_value
END
WHERE p.slug = 'awg2'
AND pv.variable_name IN ('Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4');
-- Fix awg_params for all existing servers using awg2 protocol
-- Problem: H1-H4 parameters were stored with single values instead of "value1-value2" format
-- This was causing QR codes to be detected as "legacy" instead of proper AmneziaWG 2.0 format
UPDATE vpn_servers
SET awg_params = '{"JC":5,"JMIN":10,"JMAX":50,"S1":51,"S2":125,"S3":13,"S4":9,"H1":"1443912531-1981073285","H2":"1984025557-2135018048","H3":"2145217268-2146643749","H4":"2146790761-2146860793","I1":"<r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737>","I2":"","I3":"","I4":"","I5":""}'
WHERE install_protocol = 'awg2';
Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB