From c852fb13ff49619a1ecf29f4764772e672110589 Mon Sep 17 00:00:00 2001 From: baodev0 Date: Mon, 13 Jul 2026 15:30:55 +0700 Subject: [PATCH] Refactor VsphereHostSshService to use phpseclib for SSH execution, removing sshpass dependency; enhance error handling and logging --- app/Services/VsphereHostSshService.php | 51 +++++++++----------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/app/Services/VsphereHostSshService.php b/app/Services/VsphereHostSshService.php index 32bb031..8ae1841 100644 --- a/app/Services/VsphereHostSshService.php +++ b/app/Services/VsphereHostSshService.php @@ -7,12 +7,13 @@ namespace App\Services; use App\Http\Facade\SignRSA; use App\Models\InfraManagement; use Illuminate\Support\Facades\Log; +use phpseclib3\Net\SSH2; /** * VsphereHostSshService * * Executes pre-deployed PowerShell scripts on the vSphere Windows host - * via native SSH (sshpass). No phpseclib dependency. + * via phpseclib SSH (no sshpass required). * * Scripts in C:/CLISave/ accept 5 positional params: * VM_ID NEW_PASSWORD VCENTER_HOST VCENTER_USER VCENTER_PASSWORD @@ -36,8 +37,6 @@ final class VsphereHostSshService private const SCRIPT_CHANGE_PWD_WINDOWS = 'changePwdWindows.ps1'; private const CONNECT_TIMEOUT = 20; - private const MAX_RETRIES = 5; - private const RETRY_DELAY = 5; // ───────────────────────────────────────────────────────────────────────── @@ -232,46 +231,30 @@ final class VsphereHostSshService // ───────────────────────────────────────────────────────────────────────── /** - * Run a command on the vSphere SSH host via sshpass. - * Retries up to MAX_RETRIES times on connection failure. + * Run a command on the vSphere SSH host via phpseclib (no sshpass required). * * @throws \RuntimeException */ private function sshExec(string $remoteCmd): string { - $ssh = $this->sshConfig(); + $config = $this->sshConfig(); - // Full native SSH command executed on local shell - $sshCmd = sprintf( - 'sshpass -p %s ssh -o StrictHostKeyChecking=no -o ConnectTimeout=%d -p %d %s@%s %s 2>&1', - escapeshellarg($ssh['password']), - self::CONNECT_TIMEOUT, - $ssh['port'], - escapeshellarg($ssh['username']), - escapeshellarg($ssh['host']), - escapeshellarg($remoteCmd), - ); + $client = new SSH2($config['host'], $config['port'], self::CONNECT_TIMEOUT); - for ($attempt = 1; $attempt <= self::MAX_RETRIES; $attempt++) { - $output = shell_exec($sshCmd); - - if ($output === null) { - Log::warning('VsphereHostSshService: shell_exec returned null', [ - 'attempt' => $attempt, - ]); - } else { - Log::info('VsphereHostSshService: SSH executed', ['attempt' => $attempt]); - return $output; - } - - if ($attempt < self::MAX_RETRIES) { - sleep(self::RETRY_DELAY); - } + if (!$client->login($config['username'], $config['password'])) { + throw new \RuntimeException( + "SSH login failed: {$config['host']}:{$config['port']}" + ); } - throw new \RuntimeException( - 'SSH exec to ' . $ssh['host'] . ' failed after ' . self::MAX_RETRIES . ' attempts', - ); + $output = $client->exec($remoteCmd); + + Log::info('VsphereHostSshService: SSH executed', [ + 'host' => $config['host'], + 'port' => $config['port'], + ]); + + return (string) $output; } /**