feat: enhance VpnClient and VpnServer for improved command execution and configuration handling

This commit is contained in:
infosave2007
2026-04-04 12:45:04 +03:00
parent fc187ba819
commit da72a5b3ed
3 changed files with 79 additions and 18 deletions
+4
View File
@@ -69,3 +69,7 @@ scripts/test_xray_install.sh
scripts/test_online.php
API_AWG_DOCS.md
log.txt
scripts/bootstrap_awg_container.sh
scripts/fix_server_visibility.sh
scripts/remote_fix_client_create.sh
scripts/retest_client_api.sh
+40 -6
View File
@@ -741,6 +741,8 @@ class VpnClient
private static function syncServerKeysFromContainer(VpnServer $server, array $serverData): void
{
$containerName = $serverData['container_name'] ?? 'amnezia-awg';
$protocolSlug = (string) ($serverData['install_protocol'] ?? '');
$primaryConfigDir = $protocolSlug === 'awg2' ? '/opt/amnezia/awg2' : '/opt/amnezia/awg';
try {
// Try to get public key from wg show
@@ -755,23 +757,33 @@ class VpnClient
// Prefer that file (stable) and fall back to parsing the first peer PSK from wg0.conf.
$psk = '';
$pskKeyFileCmd = "docker exec $containerName sh -c \"cat /opt/amnezia/awg/wireguard_psk.key 2>/dev/null || true\"";
$pskKeyFileCmd = "docker exec $containerName sh -c \"cat $primaryConfigDir/wireguard_psk.key 2>/dev/null || cat /opt/amnezia/awg/wireguard_psk.key 2>/dev/null || true\"";
$psk = trim($server->executeCommand($pskKeyFileCmd, true));
if ($psk === '') {
$pskFromConfCmd = "docker exec $containerName sh -c \"grep -E '^[[:space:]]*PresharedKey[[:space:]]*=' /opt/amnezia/awg/wg0.conf 2>/dev/null | head -1 | sed -E 's/^[[:space:]]*PresharedKey[[:space:]]*=[[:space:]]*//' | tr -d '\\r'\" 2>/dev/null || true";
$pskFromConfCmd = "docker exec $containerName sh -c \"grep -E '^[[:space:]]*PresharedKey[[:space:]]*=' $primaryConfigDir/wg0.conf 2>/dev/null | head -1 | sed -E 's/^[[:space:]]*PresharedKey[[:space:]]*=[[:space:]]*//' | tr -d '\\r'\" 2>/dev/null || true";
$psk = trim($server->executeCommand($pskFromConfCmd, true));
}
if ($psk === '' && $primaryConfigDir !== '/opt/amnezia/awg') {
$pskFromAwgConfCmd = "docker exec $containerName sh -c \"grep -E '^[[:space:]]*PresharedKey[[:space:]]*=' /opt/amnezia/awg/wg0.conf 2>/dev/null | head -1 | sed -E 's/^[[:space:]]*PresharedKey[[:space:]]*=[[:space:]]*//' | tr -d '\\r'\" 2>/dev/null || true";
$psk = trim($server->executeCommand($pskFromAwgConfCmd, true));
}
if ($psk === '') {
$pskFromAltConfCmd = "docker exec $containerName sh -c \"grep -E '^[[:space:]]*PresharedKey[[:space:]]*=' /etc/wireguard/wg0.conf 2>/dev/null | head -1 | sed -E 's/^[[:space:]]*PresharedKey[[:space:]]*=[[:space:]]*//' | tr -d '\\r'\" 2>/dev/null || true";
$psk = trim($server->executeCommand($pskFromAltConfCmd, true));
}
// Extract DNS from config
$dnsCmd = "docker exec $containerName sh -c \"grep -E '^DNS' /opt/amnezia/awg/wg0.conf 2>/dev/null | head -1 | cut -d= -f2 | tr -d '[:space:]'\" 2>/dev/null || echo ''";
$dnsCmd = "docker exec $containerName sh -c \"grep -E '^DNS' $primaryConfigDir/wg0.conf 2>/dev/null | head -1 | cut -d= -f2 | tr -d '[:space:]'\" 2>/dev/null || echo ''";
$dns = trim($server->executeCommand($dnsCmd, true));
if (empty($dns) && $primaryConfigDir !== '/opt/amnezia/awg') {
$dnsAwgCmd = "docker exec $containerName sh -c \"grep -E '^DNS' /opt/amnezia/awg/wg0.conf 2>/dev/null | head -1 | cut -d= -f2 | tr -d '[:space:]'\" 2>/dev/null || echo ''";
$dns = trim($server->executeCommand($dnsAwgCmd, true));
}
if (empty($dns)) {
// Try alternative config location
$dnsCmd2 = "docker exec $containerName sh -c \"grep -E '^DNS' /etc/wireguard/wg0.conf 2>/dev/null | head -1 | cut -d= -f2 | tr -d '[:space:]'\" 2>/dev/null || echo ''";
@@ -800,7 +812,10 @@ class VpnClient
// Primary source: wg0.conf
if (empty($awgParams)) {
$awgParams = self::extractAwgParamsFromWg0Conf($server, $containerName, $primaryConfigDir . '/wg0.conf');
if (empty($awgParams) && $primaryConfigDir !== '/opt/amnezia/awg') {
$awgParams = self::extractAwgParamsFromWg0Conf($server, $containerName, '/opt/amnezia/awg/wg0.conf');
}
if (empty($awgParams)) {
$awgParams = self::extractAwgParamsFromWg0Conf($server, $containerName, '/etc/wireguard/wg0.conf');
}
@@ -954,11 +969,16 @@ class VpnClient
*/
private static function executeServerCommand(array $serverData, string $command, bool $sudo = false): string
{
if ($sudo && strtolower($serverData['username']) !== 'root') {
$command = "echo '{$serverData['password']}' | sudo -S " . $command;
$needsSudo = $sudo && strtolower((string) ($serverData['username'] ?? '')) !== 'root';
$baseCommand = $command;
if ($needsSudo) {
// Suppress sudo prompt noise in stdout to keep parser output stable.
$command = "echo '{$serverData['password']}' | sudo -S -p '' " . $command;
}
$escapedCommand = escapeshellarg($command);
$run = static function (string $cmd) use ($serverData): string {
$escapedCommand = escapeshellarg($cmd);
$sshCommand = 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",
$serverData['password'],
@@ -969,6 +989,20 @@ class VpnClient
);
return shell_exec($sshCommand) ?? '';
};
$output = $run($command);
// If sudo auth fails but docker is available without sudo (docker group), retry without sudo.
if (
$needsSudo
&& preg_match('/(^|\\n)docker(\\s|$)/', ltrim($baseCommand))
&& preg_match('/incorrect password attempts|sorry, try again|a password is required/i', $output)
) {
$output = $run($baseCommand);
}
return $output;
}
/**
+25 -2
View File
@@ -427,6 +427,7 @@ class VpnServer
*/
public function executeCommand(string $command, bool $sudo = false): string
{
$baseCommand = $command;
$escapedCommand = escapeshellarg($command);
// Determine auth method
@@ -448,8 +449,10 @@ class VpnServer
$escapedCommand
);
} else {
if ($sudo && strtolower($this->data['username']) !== 'root') {
$command = "echo '{$this->data['password']}' | sudo -S " . $command;
$needsSudo = $sudo && strtolower((string) ($this->data['username'] ?? '')) !== 'root';
if ($needsSudo) {
// Suppress sudo prompt text to keep command output machine-parseable.
$command = "echo '{$this->data['password']}' | sudo -S -p '' " . $command;
$escapedCommand = escapeshellarg($command);
}
@@ -467,6 +470,26 @@ class VpnServer
$output = shell_exec($sshCommand) ?? '';
// If sudo auth fails but user can run docker without sudo, retry docker commands directly.
if (
empty($this->data['ssh_key'])
&& !empty($needsSudo)
&& preg_match('/(^|\\n)docker(\\s|$)/', ltrim($baseCommand))
&& preg_match('/incorrect password attempts|sorry, try again|a password is required/i', $output)
) {
$escapedBaseCommand = escapeshellarg($baseCommand);
$sshCommandNoSudo = sprintf(
"sshpass -p '%s' ssh -p %d %s %s@%s %s 2>&1",
$this->data['password'],
$this->data['port'],
$sshOptions,
$this->data['username'],
$this->data['host'],
$escapedBaseCommand
);
$output = shell_exec($sshCommandNoSudo) ?? '';
}
if ($keyFile && file_exists($keyFile)) {
unlink($keyFile);
}