feat: enhance AWG2 support with additional parameters and backward compatibility improvements
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
+130
-51
@@ -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 |
Reference in New Issue
Block a user