fix(awg2): resolve issue #50 client key generation and install timeout

Issue #50 (AmneziaWG 2.0 / awg2): "Failed to generate client keys" when
creating clients, and "Invalid server response" on first install.

- VpnClient::generateClientKeys() built its own password-only SSH command
  (PubkeyAuthentication=no, no sudo), bypassing VpnServer::executeCommand.
  That broke key-based servers and hosts where docker requires sudo. Route
  it through executeCommand so SSH-key auth and docker sudo auto-detection
  apply, matching every other remote operation.
- VpnClient::getNextClientIP() read /opt/amnezia/awg/wg0.conf only; AWG2
  uses awg0.conf. Read awg0.conf first, fall back to wg0.conf.
- deploy route: lift PHP time limit (set_time_limit(0) + ignore_user_abort)
  so the multi-minute awg2 docker build is not killed mid-request, which
  produced the truncated, non-JSON "Invalid server response".
- migration 070: drop `--no-cache` from the awg2 docker build so layers are
  reused, making installs and retries fast and idempotent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
infosave
2026-05-29 10:31:59 +03:00
parent 809b0ca63d
commit b819eb35b0
3 changed files with 50 additions and 13 deletions
+23 -13
View File
@@ -799,26 +799,33 @@ class VpnClient
$containerName = $serverData['container_name'];
$protocolSlug = (string) ($serverData['install_protocol'] ?? '');
$isAwg2 = (stripos($containerName, 'awg2') !== false || $protocolSlug === 'awg2');
// The amneziawg-go image ships `awg` and a `wg -> awg` symlink, so either
// tool works there. Use `awg` for AWG2 and `wg` otherwise.
$wgTool = $isAwg2 ? 'awg' : 'wg';
$cmd = sprintf(
"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),
// Inner script that runs inside the container shell. Generates a private
// key, derives the public key and prints them separated by a "---" marker.
$script = sprintf(
'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"',
$wgTool,
$wgTool
);
$escaped = escapeshellarg($cmd);
$sshCmd = sprintf(
"sshpass -p %s ssh -p %d -q -o LogLevel=ERROR -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PreferredAuthentications=password -o PubkeyAuthentication=no %s@%s %s 2>&1",
escapeshellarg($serverData['password']),
$serverData['port'],
$serverData['username'],
$serverData['host'],
$escaped
$cmd = sprintf(
'docker exec -i %s sh -lc %s',
escapeshellarg($containerName),
escapeshellarg($script)
);
$out = (string) shell_exec($sshCmd);
// Route the command through VpnServer::executeCommand so that SSH key
// authentication and automatic docker sudo detection are handled the same
// way as every other remote operation. The previous implementation built
// its own password-only SSH command (PubkeyAuthentication=no, no sudo),
// which failed on key-based servers and on hosts where docker needs sudo,
// producing the "Failed to generate client keys" error (issue #50).
$server = new VpnServer((int) $serverData['id']);
$out = (string) $server->executeCommand($cmd); // null sudo => auto-detect for docker
$parts = explode("---", trim($out));
if (count($parts) < 2) {
@@ -860,8 +867,11 @@ class VpnClient
try {
$containerName = $serverData['container_name'] ?? 'amnezia-awg';
$server = new VpnServer($serverData['id']);
// AWG2 stores its config as awg0.conf (inside the container the path is
// always /opt/amnezia/awg/). Read awg0.conf first, then fall back to the
// legacy wg0.conf so externally created peers are still detected.
$cmd = sprintf(
"docker exec %s cat /opt/amnezia/awg/wg0.conf 2>/dev/null",
"docker exec %s sh -c 'cat /opt/amnezia/awg/awg0.conf 2>/dev/null; cat /opt/amnezia/awg/wg0.conf 2>/dev/null'",
escapeshellarg($containerName)
);
$serverConfig = $server->executeCommand($cmd, true);