fix(awg2): auto-detect wg/awg tool inside container (real cause of issue #50)
Live testing against an AmneziaWG 2.0 server revealed the actual root cause of "Failed to generate client keys": the official Amnezia container image ships the userspace tool only as `wg` (a patched AmneziaWG binary) and has NO `awg` binary, while the panel hardcoded `awg` for AWG2. `awg genkey` then failed with "sh: awg: not found". (amneziawg-go ships `awg` with `wg` symlinked, so both names work there — but the Amnezia image does not.) - generateClientKeys(): detect the tool inside the container (`command -v awg || command -v wg`) instead of hardcoding `awg`. - addClientToServer(): resolve the tool via new resolveWgTool() helper so `wg set` / `wg-quick up` (peer apply) also work on the Amnezia image. - executeServerCommand(): delegate to VpnServer::executeCommand so SSH key auth + docker sudo auto-detection apply to all 19 call sites (it was password-only before). Verified end-to-end on a live AWG2 server: pre-fix code fails with "Failed to generate client keys: sh: awg: not found"; fixed code creates the client, generates keys, and the peer appears in `wg show wg0`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+48
-15
@@ -797,19 +797,20 @@ 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');
|
||||
// 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';
|
||||
|
||||
// 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
|
||||
);
|
||||
// Detect the WireGuard userspace tool INSIDE the container instead of
|
||||
// hardcoding it. Different AWG2 images expose it under different names:
|
||||
// the official Amnezia image ships only `wg` (a patched AmneziaWG binary),
|
||||
// while amneziawg-go provides `awg` (with `wg` symlinked to it). Hardcoding
|
||||
// `awg` made `awg genkey` fail with "awg: not found" on the Amnezia image,
|
||||
// which is the actual cause of the "Failed to generate client keys" error
|
||||
// in issue #50. Prefer `awg`, fall back to `wg`.
|
||||
$script = 'set -e; umask 077; '
|
||||
. 'tool=$(command -v awg 2>/dev/null || command -v wg 2>/dev/null); '
|
||||
. '[ -n "$tool" ] || { echo no_wg_tool; exit 1; }; '
|
||||
. 'priv=$("$tool" genkey | tr -d "\r\n"); [ -n "$priv" ] || { echo empty_private_key; exit 1; }; '
|
||||
. 'pub=$(printf "%s\n" "$priv" | "$tool" pubkey | tr -d "\r\n"); [ -n "$pub" ] || { echo empty_public_key; exit 1; }; '
|
||||
. 'printf "%s\n---\n%s\n" "$priv" "$pub"';
|
||||
|
||||
$cmd = sprintf(
|
||||
'docker exec -i %s sh -lc %s',
|
||||
@@ -1223,9 +1224,12 @@ 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';
|
||||
// Determine correct tool names by probing the container. The official
|
||||
// Amnezia image exposes only `wg`/`wg-quick`; amneziawg-go provides
|
||||
// `awg`/`awg-quick`. Hardcoding `awg` broke peer setup on the Amnezia
|
||||
// image (issue #50). Prefer `awg`, fall back to `wg`.
|
||||
$wgTool = $isAwg2 ? self::resolveWgTool($serverData, $containerName) : 'wg';
|
||||
$wgQuickTool = $wgTool . '-quick';
|
||||
|
||||
// 1. Create temp file for PSK (to avoid shell escaping issues)
|
||||
$pskFile = '/tmp/' . bin2hex(random_bytes(8)) . '.psk';
|
||||
@@ -1302,11 +1306,40 @@ class VpnClient
|
||||
self::executeServerCommand($serverData, $updateCmd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the WireGuard userspace tool name available inside a container.
|
||||
* Returns 'awg' when present, otherwise 'wg'. Used so AWG2 works on both the
|
||||
* official Amnezia image (ships `wg`) and amneziawg-go (ships `awg`).
|
||||
*/
|
||||
private static function resolveWgTool(array $serverData, string $containerName): string
|
||||
{
|
||||
$probe = sprintf(
|
||||
"docker exec -i %s sh -lc 'command -v awg >/dev/null 2>&1 && echo awg || echo wg'",
|
||||
escapeshellarg($containerName)
|
||||
);
|
||||
$tool = trim(self::executeServerCommand($serverData, $probe, true));
|
||||
return $tool === 'awg' ? 'awg' : 'wg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute command on server
|
||||
*/
|
||||
private static function executeServerCommand(array $serverData, string $command, bool $sudo = false): string
|
||||
{
|
||||
// Delegate to VpnServer::executeCommand so SSH key authentication, docker
|
||||
// sudo auto-detection and retry logic are shared with the rest of the
|
||||
// panel. The previous inline implementation was password-only and failed
|
||||
// on key-based servers (contributing to issue #50).
|
||||
if (!empty($serverData['id'])) {
|
||||
try {
|
||||
$server = new VpnServer((int) $serverData['id']);
|
||||
return $server->executeCommand($command, $sudo ? null : false);
|
||||
} catch (Exception $e) {
|
||||
error_log('executeServerCommand: delegate failed, using legacy path: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy fallback (no server id in $serverData): password-only SSH.
|
||||
$needsSudo = $sudo && strtolower((string) ($serverData['username'] ?? '')) !== 'root';
|
||||
$baseCommand = $command;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user