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:
+23
-13
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user