diff --git a/inc/InstallProtocolManager.php b/inc/InstallProtocolManager.php index e6cb609..541c807 100644 --- a/inc/InstallProtocolManager.php +++ b/inc/InstallProtocolManager.php @@ -427,14 +427,16 @@ class InstallProtocolManager { $metadata = $protocol['definition']['metadata'] ?? []; $serverData = $server->getData(); - $containerName = $serverData['container_name'] ?? ($metadata['container_name'] ?? 'amnezia-awg'); + // For multi-protocol servers, use container_name from protocol metadata first + // (vpn_servers.container_name stores the primary protocol's container, e.g. 'aivpn-server') + $containerName = $metadata['container_name'] ?? ($serverData['container_name'] ?? 'amnezia-awg'); $containerFilter = escapeshellarg('^' . $containerName . '$'); $containerArg = escapeshellarg($containerName); - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/awg0.conf - // На хосте может быть /opt/amnezia/awg2/wg0.conf (монтируется как /opt/amnezia/awg внутри контейнера) + // AWG2 uses awg0.conf (standard, same as native Amnezia app) + // Old AWG uses wg0.conf $isAwg2 = (stripos($containerName, 'awg2') !== false || ($protocol['slug'] ?? '') === 'awg2'); - $configDir = $isAwg2 ? '/opt/amnezia/awg' : '/opt/amnezia/awg'; + $configDir = '/opt/amnezia/awg'; $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; $containerListRaw = trim($server->executeCommand("docker ps -a --filter name={$containerFilter} --format '{{.Names}}'", true)); @@ -466,13 +468,18 @@ class InstallProtocolManager $containerState = trim($server->executeCommand("docker inspect --format '{{.State.Status}}' {$containerArg}", true)); - // Для AWG2 проверяем оба возможных имени файла конфигурации - $configFile = ($protocol['slug'] ?? '') === 'awg2' ? 'awg0.conf' : 'wg0.conf'; + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy panel installs) + $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; $wgConfig = $server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/{$configFile} 2>/dev/null", true); - if (trim($wgConfig) === '') { + if ($isAwg2 && (trim($wgConfig) === '' || strpos($wgConfig, '[Interface]') === false)) { + // Fallback to wg0.conf for legacy panel installs + $configFile = 'wg0.conf'; + $wgConfig = $server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/{$configFile} 2>/dev/null", true); + } + if (trim($wgConfig) === '' || strpos($wgConfig, '[Interface]') === false) { return [ 'status' => 'partial', - 'message' => "Контейнер найден, но конфигурация {$configFile} отсутствует", + 'message' => "Контейнер найден, но конфигурация wg0.conf/awg0.conf отсутствует", 'details' => [ 'container_name' => $containerName, 'container_status' => $containerState, @@ -532,11 +539,18 @@ class InstallProtocolManager $containerName = $details['container_name'] ?? ($protocol['definition']['metadata']['container_name'] ?? 'amnezia-awg'); $containerArg = escapeshellarg($containerName); - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/awg0.conf - // На хосте может быть /opt/amnezia/awg2/wg0.conf (монтируется как /opt/amnezia/awg внутри контейнера) + // Config is always wg0.conf — container CMD runs: awg-quick up /opt/amnezia/awg/wg0.conf $isAwg2 = (stripos($containerName, 'awg2') !== false || ($protocol['slug'] ?? '') === 'awg2'); - $configDir = '/opt/amnezia/awg'; // Внутри контейнера всегда /opt/amnezia/awg + $configDir = '/opt/amnezia/awg'; + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy) $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; + $testConf = trim($server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/{$configFile} 2>/dev/null", true)); + if ($isAwg2 && ($testConf === '' || strpos($testConf, '[Interface]') === false)) { + $configFile = 'wg0.conf'; + } + + // Determine interface name from config filename (wg0.conf -> wg0, awg0.conf -> awg0) + $ifaceName = str_replace('.conf', '', $configFile); // Try to ensure container is running and wg is up $server->executeCommand("docker start {$containerArg} 2>/dev/null || true", true); @@ -544,40 +558,64 @@ class InstallProtocolManager $server->executeCommand("docker exec -i {$containerArg} wg-quick up {$configDir}/{$configFile} 2>/dev/null || true", true); $pdo = DB::conn(); - $stmt = $pdo->prepare(' - UPDATE vpn_servers - SET vpn_port = ?, - server_public_key = ?, - preshared_key = ?, - awg_params = ?, - status = ?, - error_message = NULL, - deployed_at = COALESCE(deployed_at, NOW()), - install_protocol = ? - WHERE id = ? - '); - $stmt->execute([ - $details['vpn_port'] ?? null, - $details['server_public_key'] ?? null, - $details['preshared_key'] ?? null, - isset($details['awg_params']) ? json_encode($details['awg_params']) : null, - 'active', - $protocol['slug'] ?? ($isAwg2 ? 'awg2' : 'amnezia-wg'), - $server->getId() - ]); - - // Add entry to server_protocols table so protocol shows in installed list + $serverData = $server->getData(); + $serverId = $server->getId(); $protocolId = self::resolveProtocolId($protocol); - if ($protocolId) { + $protocolSlug = $protocol['slug'] ?? ($isAwg2 ? 'awg2' : 'amnezia-wg'); + + // Check if server already has another primary protocol installed + $existingProtocol = $serverData['install_protocol'] ?? ''; + $isSecondaryProtocol = ($existingProtocol !== '' && $existingProtocol !== $protocolSlug); + + if (!$isSecondaryProtocol) { + // Primary protocol — update vpn_servers $stmt = $pdo->prepare(' - INSERT INTO server_protocols (server_id, protocol_id, applied_at) - VALUES (?, ?, NOW()) - ON DUPLICATE KEY UPDATE applied_at = NOW() + UPDATE vpn_servers + SET vpn_port = ?, + server_public_key = ?, + preshared_key = ?, + awg_params = ?, + status = ?, + error_message = NULL, + deployed_at = COALESCE(deployed_at, NOW()), + install_protocol = ? + WHERE id = ? '); $stmt->execute([ - $server->getId(), - $protocolId + $details['vpn_port'] ?? null, + $details['server_public_key'] ?? null, + $details['preshared_key'] ?? null, + isset($details['awg_params']) ? json_encode($details['awg_params']) : null, + 'active', + $protocolSlug, + $serverId ]); + } else { + // Secondary protocol — only ensure server is active, don't overwrite primary protocol data + $stmt = $pdo->prepare('UPDATE vpn_servers SET status = ?, error_message = NULL WHERE id = ?'); + $stmt->execute(['active', $serverId]); + } + + // Store protocol-specific config in server_protocols (works for both primary and secondary) + if ($protocolId) { + $configData = json_encode([ + 'server_host' => $serverData['ip_address'] ?? $serverData['hostname'] ?? null, + 'server_port' => $details['vpn_port'] ?? null, + 'extras' => [ + 'vpn_port' => $details['vpn_port'] ?? null, + 'vpn_subnet' => $details['vpn_subnet'] ?? '10.8.1.0/24', + 'server_public_key' => $details['server_public_key'] ?? null, + 'preshared_key' => $details['preshared_key'] ?? null, + 'awg_params' => $details['awg_params'] ?? null, + 'container_name' => $containerName, + ], + ]); + $stmt = $pdo->prepare(' + INSERT INTO server_protocols (server_id, protocol_id, config_data, applied_at, created_at) + VALUES (?, ?, ?, NOW(), NOW()) + ON DUPLICATE KEY UPDATE config_data = VALUES(config_data), applied_at = NOW() + '); + $stmt->execute([$serverId, $protocolId, $configData]); } $server->refresh(); @@ -646,18 +684,28 @@ class InstallProtocolManager // Use existing keys and config } else { // Generate new key pair for this client - $newPrivateKey = trim($server->executeCommand("docker exec -i {$containerArg} /usr/bin/awg genkey 2>/dev/null", true)); - $newPublicKey = trim($server->executeCommand("docker exec -i {$containerArg} sh -c 'echo \"'\"$newPrivateKey\"'\" | /usr/bin/awg pubkey' 2>/dev/null", true)); + // Use awg for AWG2, wg for standard + $keyTool = $isAwg2 ? 'awg' : 'wg'; + $newPrivateKey = trim($server->executeCommand("docker exec {$containerArg} {$keyTool} genkey", true)); + if (!empty($newPrivateKey)) { + $escapedKey = escapeshellarg($newPrivateKey); + $newPublicKey = trim($server->executeCommand("docker exec {$containerArg} sh -c 'echo {$escapedKey} | {$keyTool} pubkey'", true)); + } else { + $newPublicKey = ''; + } - if (!empty($newPrivateKey) && !empty($newPublicKey)) { + Logger::appendInstall($serverId, "Restore: keygen for {$clientIp}: privkey_len=" . strlen($newPrivateKey) . " pubkey_len=" . strlen($newPublicKey)); + + if (!empty($newPrivateKey) && !empty($newPublicKey) && strlen($newPublicKey) >= 40) { $privateKey = $newPrivateKey; $protocolSlug = $protocol['slug'] ?? ''; + $serverHost = $serverData['host'] ?? $serverData['ip_address'] ?? $serverData['hostname'] ?? ''; $config = VpnClient::buildClientConfig( $privateKey, $clientIp, $details['server_public_key'] ?? '', $details['preshared_key'] ?? '', - $serverData['ip_address'] ?? $serverData['hostname'] ?? '', + $serverHost, $details['vpn_port'] ?? 51820, $details['awg_params'] ?? [], $protocolSlug @@ -667,6 +715,8 @@ class InstallProtocolManager // Mark that we need to update server config with new public key $needsServerConfigUpdate = true; $keyUpdates[] = ['old' => $pub, 'new' => $newPublicKey]; + } else { + Logger::appendInstall($serverId, "Restore: WARNING keygen failed for {$clientIp}, keeping original public key"); } } @@ -693,7 +743,7 @@ class InstallProtocolManager if ($needsServerConfigUpdate && !empty($keyUpdates)) { Logger::appendInstall($serverId, "Restore: updating server config with " . count($keyUpdates) . " new public keys"); - // Update awg0.conf - replace old public keys with new ones + // Update wg0.conf - replace old public keys with new ones $updatedConfig = $wgConfig; foreach ($keyUpdates as $update) { // Escape special characters for regex @@ -1403,6 +1453,7 @@ class InstallProtocolManager if ($isAwg) { $detection = self::detectBuiltinAwg($server, $protocol); + Logger::appendInstall($serverId, 'AWG detect result: status=' . ($detection['status'] ?? 'null') . ' message=' . ($detection['message'] ?? 'none')); if (($detection['status'] ?? '') === 'existing') { Logger::appendInstall($serverId, 'Existing AWG installation detected, restoring instead of reinstalling'); $restoreResult = self::restoreBuiltinAwg($server, $protocol, $detection, $options); @@ -2104,11 +2155,15 @@ class InstallProtocolManager $serverData = $server->getData(); $containerName = $serverData['container_name'] ?? 'amnezia-awg'; - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/awg0.conf + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy) $isAwg2 = (stripos($containerName, 'awg2') !== false || ($protocol['slug'] ?? '') === 'awg2'); - $configDir = '/opt/amnezia/awg'; // Внутри контейнера всегда /opt/amnezia/awg + $configDir = '/opt/amnezia/awg'; $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; $conf = $server->executeCommand("docker exec -i $containerName cat {$configDir}/{$configFile}", true); + if ($isAwg2 && (!$conf || strpos($conf, '[Interface]') === false)) { + $configFile = 'wg0.conf'; + $conf = $server->executeCommand("docker exec -i $containerName cat {$configDir}/{$configFile}", true); + } if (!$conf) return; @@ -2454,11 +2509,15 @@ class InstallProtocolManager $serverData = $server->getData(); $pid = self::resolveProtocolId($protocol); - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/awg0.conf + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy) $isAwg2 = (stripos($containerName, 'awg2') !== false || ($protocol['slug'] ?? '') === 'awg2'); - $configDir = '/opt/amnezia/awg'; // Внутри контейнера всегда /opt/amnezia/awg + $configDir = '/opt/amnezia/awg'; $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; $wgConfig = $server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/{$configFile} 2>/dev/null", true); + if ($isAwg2 && (trim($wgConfig) === '' || strpos($wgConfig, '[Interface]') === false)) { + $configFile = 'wg0.conf'; + $wgConfig = $server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/{$configFile} 2>/dev/null", true); + } $tableRaw = $server->executeCommand("docker exec -i {$containerArg} cat {$configDir}/clientsTable 2>/dev/null", true); $clientsTable = json_decode(trim($tableRaw), true); diff --git a/inc/VpnClient.php b/inc/VpnClient.php index c19e750..7137bb3 100644 --- a/inc/VpnClient.php +++ b/inc/VpnClient.php @@ -100,6 +100,61 @@ class VpnClient } } + // For multi-protocol setups, override server data with protocol-specific settings + // (subnet, keys, port, AWG params) from server_protocols.config_data or protocol metadata + if ($protocolId) { + try { + $stmtSp = $pdo->prepare('SELECT config_data FROM server_protocols WHERE server_id = ? AND protocol_id = ? LIMIT 1'); + $stmtSp->execute([$serverId, $protocolId]); + $spConfigRaw = $stmtSp->fetchColumn(); + if ($spConfigRaw) { + $spConfig = is_string($spConfigRaw) ? json_decode($spConfigRaw, true) : $spConfigRaw; + if (is_array($spConfig)) { + $spExtras = $spConfig['extras'] ?? []; + // If extras has 'result' subarray, merge it + if (isset($spExtras['result']) && is_array($spExtras['result'])) { + $spExtras = array_merge($spExtras, $spExtras['result']); + } + // Override server data with protocol-specific values + if (!empty($spExtras['server_public_key'])) { + $serverData['server_public_key'] = $spExtras['server_public_key']; + } + if (!empty($spExtras['preshared_key'])) { + $serverData['preshared_key'] = $spExtras['preshared_key']; + } + if (!empty($spExtras['vpn_port'])) { + $serverData['vpn_port'] = $spExtras['vpn_port']; + } + if (!empty($spConfig['server_port'])) { + $serverData['vpn_port'] = $spConfig['server_port']; + } + // Override AWG params from protocol config + // AWG params can be at extras level (Jc, S1, etc.) or nested in extras.awg_params + $awgOverride = []; + $awgSource = $spExtras; + if (isset($spExtras['awg_params']) && is_array($spExtras['awg_params'])) { + $awgSource = array_merge($awgSource, $spExtras['awg_params']); + } + foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5'] as $ak) { + if (isset($awgSource[$ak]) && $awgSource[$ak] !== '' && $awgSource[$ak] !== null) { + $awgOverride[$ak] = $awgSource[$ak]; + } + } + if (!empty($awgOverride)) { + $serverData['awg_params'] = json_encode($awgOverride); + } + } + } + } catch (Exception $e) { + error_log('Failed to load protocol config_data: ' . $e->getMessage()); + } + + // Override vpn_subnet from protocol definition metadata (e.g. AWG2 uses 10.8.1.0/24) + if (!empty($protoMetadata['vpn_subnet'])) { + $serverData['vpn_subnet'] = $protoMetadata['vpn_subnet']; + } + } + $clientIP = self::getNextClientIP($serverData); $loginBase = $login !== null && $login !== '' ? $login : $name; $loginBase = str_replace(' ', '_', trim($loginBase)); @@ -742,9 +797,15 @@ class VpnClient private static function generateClientKeys(array $serverData, string $clientName): array { $containerName = $serverData['container_name']; + $protocolSlug = (string) ($serverData['install_protocol'] ?? ''); + $isAwg2 = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2'); + $wgTool = $isAwg2 ? 'awg' : 'wg'; + $cmd = sprintf( - "docker exec -i %s sh -lc 'set -e; umask 077; priv=\$(wg genkey | tr -d " . '"' . "\\r\\n" . '"' . "); [ -n \"\$priv\" ] || { echo empty_private_key; exit 1; }; pub=\$(printf " . '"' . "%%s\\n" . '"' . " \"\$priv\" | wg pubkey | tr -d " . '"' . "\\r\\n" . '"' . "); [ -n \"\$pub\" ] || { echo empty_public_key; exit 1; }; printf " . '"' . "%%s\\n---\\n%%s\\n" . '"' . " \"\$priv\" \"\$pub\"'", - escapeshellarg($containerName) + "docker exec -i %s sh -lc 'set -e; umask 077; priv=\$(%s genkey | tr -d " . '"' . "\\r\\n" . '"' . "); [ -n \"\$priv\" ] || { echo empty_private_key; exit 1; }; pub=\$(printf " . '"' . "%%s\\n" . '"' . " \"\$priv\" | %s pubkey | tr -d " . '"' . "\\r\\n" . '"' . "); [ -n \"\$pub\" ] || { echo empty_public_key; exit 1; }; printf " . '"' . "%%s\\n---\\n%%s\\n" . '"' . " \"\$priv\" \"\$pub\"'", + escapeshellarg($containerName), + $wgTool, + $wgTool ); $escaped = escapeshellarg($cmd); @@ -1133,10 +1194,18 @@ class VpnClient { $containerName = $serverData['container_name']; $protocolSlug = (string) ($serverData['install_protocol'] ?? ''); - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/awg0.conf $isAwg2 = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2'); - $configDir = '/opt/amnezia/awg'; // Внутри контейнера всегда /opt/amnezia/awg + $configDir = '/opt/amnezia/awg'; + + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy panel installs) $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; + $testConf = trim(self::executeServerCommand($serverData, "docker exec -i {$containerName} cat {$configDir}/{$configFile} 2>/dev/null", true)); + if ($isAwg2 && ($testConf === '' || strpos($testConf, '[Interface]') === false)) { + $configFile = 'wg0.conf'; + } + // Interface name matches config filename (wg0.conf -> wg0, awg0.conf -> awg0) + $ifaceName = str_replace('.conf', '', $configFile); + $presharedKey = $serverData['preshared_key']; $publicKey = trim($publicKey); @@ -1144,16 +1213,21 @@ class VpnClient throw new Exception('Refusing to add client with empty public key'); } + // Determine correct tool names (awg for AWG2, wg for standard) + $wgTool = $isAwg2 ? 'awg' : 'wg'; + $wgQuickTool = $isAwg2 ? 'awg-quick' : 'wg-quick'; + // 1. Create temp file for PSK (to avoid shell escaping issues) $pskFile = '/tmp/' . bin2hex(random_bytes(8)) . '.psk'; $cmd1 = sprintf("docker exec -i %s sh -c 'echo \"%s\" > %s'", $containerName, $presharedKey, $pskFile); self::executeServerCommand($serverData, $cmd1, true); - // 2. Add peer using wg set - // wg set wg0 peer preshared-key allowed-ips + // 2. Add peer using wg/awg set $cmd2 = sprintf( - "docker exec -i %s wg set wg0 peer %s preshared-key %s allowed-ips %s/32", + "docker exec -i %s %s set %s peer %s preshared-key %s allowed-ips %s/32", $containerName, + $wgTool, + $ifaceName, escapeshellarg($publicKey), $pskFile, $clientIP @@ -1164,14 +1238,13 @@ class VpnClient $cmd3 = sprintf("docker exec -i %s rm -f %s", $containerName, $pskFile); self::executeServerCommand($serverData, $cmd3, true); - // 4. Persist to wg0.conf (append) + // 4. Persist to config file (append) $peerBlock = "\n[Peer]\n"; $peerBlock .= "PublicKey = {$publicKey}\n"; $peerBlock .= "PresharedKey = {$presharedKey}\n"; $peerBlock .= "AllowedIPs = {$clientIP}/32\n"; $escapedBlock = addslashes($peerBlock); - $configFile = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2') ? 'awg0.conf' : 'wg0.conf'; $cmd4 = sprintf("docker exec -i %s sh -c 'echo \"%s\" >> %s/%s'", $containerName, $escapedBlock, $configDir, $configFile); self::executeServerCommand($serverData, $cmd4, true); @@ -1180,7 +1253,7 @@ class VpnClient // 6. CRITICAL: Reload WG interface to apply AWG obfuscation params // Without this, the interface uses standard WireGuard without Jc/S1/S2/H1-H4 - $cmd5 = sprintf("docker exec -i %s sh -c 'ip link del wg0 2>/dev/null || true; wg-quick up %s/%s 2>&1'", $containerName, $configDir, $configFile); + $cmd5 = sprintf("docker exec -i %s sh -c 'ip link del %s 2>/dev/null || true; %s up %s/%s 2>&1'", $containerName, $ifaceName, $wgQuickTool, $configDir, $configFile); self::executeServerCommand($serverData, $cmd5, true); } @@ -1467,16 +1540,25 @@ class VpnClient { $containerName = $serverData['container_name']; $protocolSlug = (string) ($serverData['install_protocol'] ?? ''); - // Для AWG2 конфигурация внутри контейнера находится в /opt/amnezia/awg/ - $configDir = '/opt/amnezia/awg'; // Внутри контейнера всегда /opt/amnezia/awg + // Config dir inside container is always /opt/amnezia/awg + $configDir = '/opt/amnezia/awg'; - // Determine config filename - $configFile = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2') ? 'awg0.conf' : 'wg0.conf'; + // AWG2: try awg0.conf first (standard), fall back to wg0.conf (legacy panel installs) + $isAwg2 = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2'); + $configFile = $isAwg2 ? 'awg0.conf' : 'wg0.conf'; + $testConf = trim(self::executeServerCommand($serverData, "docker exec -i {$containerName} cat {$configDir}/{$configFile} 2>/dev/null", true)); + if ($isAwg2 && ($testConf === '' || strpos($testConf, '[Interface]') === false)) { + $configFile = 'wg0.conf'; + } + $ifaceName = str_replace('.conf', '', $configFile); + $wgTool = $isAwg2 ? 'awg' : 'wg'; - // First, remove using wg command (live removal) + // First, remove using wg/awg command (live removal) $removeCmd = sprintf( - "docker exec -i %s wg set wg0 peer %s remove", + "docker exec -i %s %s set %s peer %s remove", $containerName, + $wgTool, + $ifaceName, escapeshellarg($publicKey) ); @@ -1503,7 +1585,8 @@ class VpnClient self::executeServerCommand($serverData, $writeCmd, true); // Save config - $saveCmd = sprintf("docker exec -i %s wg-quick save wg0", $containerName); + $wgQuickTool = $isAwg2 ? 'awg-quick' : 'wg-quick'; + $saveCmd = sprintf("docker exec -i %s %s save %s", $containerName, $wgQuickTool, $ifaceName); self::executeServerCommand($serverData, $saveCmd, true); // Remove from clientsTable diff --git a/migrations/064_complete_awg2_original_params.sql b/migrations/064_complete_awg2_original_params.sql index 2d38b92..1f7b64e 100644 --- a/migrations/064_complete_awg2_original_params.sql +++ b/migrations/064_complete_awg2_original_params.sql @@ -51,7 +51,7 @@ 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" + 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/awg0.conf ]; do sleep 1; done; WG_QUICK_USERSPACE_IMPLEMENTATION=amneziawg-go awg-quick up /opt/amnezia/awg/awg0.conf && sleep infinity" sleep 2 else STATUS=$(docker inspect --format="{{.State.Status}}" "$CONTAINER_NAME" 2>/dev/null || echo "") @@ -59,12 +59,28 @@ else docker start "$CONTAINER_NAME" >/dev/null 2>&1 || true fi fi +# Check for existing config: first on host, then inside container (native Amnezia app installs) +CONF_FILE="/opt/amnezia/awg2/awg0.conf" +if [ ! -f "$CONF_FILE" ]; then + # Try to extract config from inside the container (native Amnezia app stores config without volume mount) + CONTAINER_CONF=$(docker exec "$CONTAINER_NAME" cat /opt/amnezia/awg/awg0.conf 2>/dev/null || true) + if [ -n "$CONTAINER_CONF" ] && echo "$CONTAINER_CONF" | grep -q "\\[Interface\\]"; then + mkdir -p /opt/amnezia/awg2 + echo "$CONTAINER_CONF" > /opt/amnezia/awg2/awg0.conf + # Also extract keys if available + docker exec "$CONTAINER_NAME" cat /opt/amnezia/awg/wireguard_server_private_key.key > /opt/amnezia/awg2/wireguard_server_private_key.key 2>/dev/null || true + docker exec "$CONTAINER_NAME" cat /opt/amnezia/awg/wireguard_server_public_key.key > /opt/amnezia/awg2/wireguard_server_public_key.key 2>/dev/null || true + docker exec "$CONTAINER_NAME" cat /opt/amnezia/awg/wireguard_psk.key > /opt/amnezia/awg2/wireguard_psk.key 2>/dev/null || true + docker exec "$CONTAINER_NAME" cat /opt/amnezia/awg/clientsTable > /opt/amnezia/awg2/clientsTable 2>/dev/null || true + echo "Extracted existing config from container" + 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:]") +if [ -f /opt/amnezia/awg2/awg0.conf ]; then + PORT=$(grep -E "^ListenPort" /opt/amnezia/awg2/awg0.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:]") + PSK=$(grep -E "^PresharedKey" /opt/amnezia/awg2/awg0.conf | head -1 | 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 @@ -83,7 +99,7 @@ if [ -f /opt/amnezia/awg2/wg0.conf ]; then 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") + VAL=$(sed -n -E "s/^[[:space:]]*$P[[:space:]]*=[[:space:]]*//p" /opt/amnezia/awg2/awg0.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" @@ -130,7 +146,7 @@ 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 +} > /opt/amnezia/awg2/awg0.conf echo "$PRIVATE_KEY" > /opt/amnezia/awg2/wireguard_server_private_key.key echo "$PUBLIC_KEY" > /opt/amnezia/awg2/wireguard_server_public_key.key diff --git a/templates/servers/view.twig b/templates/servers/view.twig index 3546b57..0bc3cc7 100644 --- a/templates/servers/view.twig +++ b/templates/servers/view.twig @@ -58,8 +58,10 @@

{{ t('servers.server_info') }}

{{ t('common.status') }}
{{ server.status }}
+ {% if server_protocols|length <= 1 %}
VPN Port
{{ server.vpn_port }}
Subnet
{{ server.vpn_subnet }}
+ {% endif %}