You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用phpseclib在PHP中模拟SSH跳板机(Jump Host)行为?

解决方案:模拟SSH跳板(ProxyJump)的正确实现

你遇到的问题根源在于:当前代码是在堡垒机的Shell环境中执行ssh命令,这要求堡垒机上的用户持有访问目标机器的私钥,且环境配置正确(比如SSH Agent、密钥权限),这和ssh -J的代理转发逻辑完全不同。ssh -J是通过堡垒机建立TCP隧道,本地直接与目标机器建立SSH连接,私钥始终在本地使用,无需堡垒机持有密钥。

以下是针对不同库的正确实现方案:

方案一:phpseclib3 实现跳板

利用phpseclib3的端口转发功能,通过已建立的堡垒机连接创建到目标机器的转发Socket,再基于该Socket连接目标机器:

<?php
require __DIR__ . '/../vendor/autoload.php';

use phpseclib\Crypt\RSA;
use phpseclib\Net\SSH2;

// 堡垒机参数
$bastionHost = 'bastion.net';
$bastionPort = 22;
$bastionUser = 'identifier';
$privateKeyPath = 'ssh_key';

// 目标机器参数
$targetHost = 'xx.xx.xx.xx';
$targetPort = 22;
$targetUser = 'identifier';

// 加载私钥(本地持有,无需传到堡垒机)
$rsa = new RSA();
$rsa->loadKey(file_get_contents($privateKeyPath));

// 1. 连接堡垒机
$bastionSSH = new SSH2($bastionHost, $bastionPort);
if (!$bastionSSH->login($bastionUser, $rsa)) {
    throw new \Exception('堡垒机登录失败');
}

// 2. 通过堡垒机创建到目标机器的转发Socket
$forwardedSocket = $bastionSSH->getForwardedPort('127.0.0.1', $targetPort, $targetHost);

// 3. 通过转发Socket连接目标机器
$targetSSH = new SSH2($forwardedSocket);
if (!$targetSSH->login($targetUser, $rsa)) {
    throw new \Exception('目标机器登录失败');
}

// 执行目标机器上的命令
echo $targetSSH->exec('whoami');

也可以使用phpseclib3的Proxy类简化流程:

<?php
require __DIR__ . '/../vendor/autoload.php';

use phpseclib\Crypt\RSA;
use phpseclib\Net\SSH2;
use phpseclib\Net\Proxy\SSH;

$bastionHost = 'bastion.net';
$bastionPort = 22;
$bastionUser = 'identifier';
$privateKeyPath = 'ssh_key';

$targetHost = 'xx.xx.xx.xx';
$targetPort = 22;
$targetUser = 'identifier';

$rsa = new RSA();
$rsa->loadKey(file_get_contents($privateKeyPath));

// 创建SSH代理(指向堡垒机)
$proxy = new SSH($bastionHost, $bastionPort);
$proxy->login($bastionUser, $rsa);

// 通过代理直接连接目标机器
$targetSSH = new SSH2($targetHost, $targetPort, ['proxy' => $proxy]);
if (!$targetSSH->login($targetUser, $rsa)) {
    throw new \Exception('目标机器登录失败');
}

echo $targetSSH->exec('uptime');

方案二:原生libssh2 实现跳板

使用libssh2的ssh2_channel_direct_tcpip建立TCP转发通道,再基于该通道连接目标机器:

<?php
// 堡垒机参数
$bastionHost = 'bastion.net';
$bastionPort = 22;
$bastionUser = 'identifier';
$privateKeyPath = 'ssh_key';
$pubKeyPath = $privateKeyPath . '.pub';

// 目标机器参数
$targetHost = 'xx.xx.xx.xx';
$targetPort = 22;
$targetUser = 'identifier';

// 1. 连接堡垒机
$bastionSession = ssh2_connect($bastionHost, $bastionPort);
if (!ssh2_auth_pubkey_file($bastionSession, $bastionUser, $pubKeyPath, $privateKeyPath)) {
    throw new \Exception('堡垒机登录失败');
}

// 2. 建立到目标机器的TCP转发通道
$forwardChannel = ssh2_channel_direct_tcpip($bastionSession, $targetHost, $targetPort);

// 3. 通过转发通道连接目标机器
$targetSession = ssh2_connect('localhost', 0, ['session' => $forwardChannel]);
if (!ssh2_auth_pubkey_file($targetSession, $targetUser, $pubKeyPath, $privateKeyPath)) {
    throw new \Exception('目标机器登录失败');
}

// 执行命令并输出结果
$stream = ssh2_exec($targetSession, 'hostname');
stream_set_blocking($stream, true);
echo stream_get_contents($stream);

关键原理说明

  • ssh -J的本质是本地→堡垒机→目标机器的TCP流量转发,私钥始终在本地用于身份验证,堡垒机仅作为流量中转节点。
  • 原方案的错误在于将SSH连接的控制权交给了堡垒机的Shell环境,依赖堡垒机的密钥配置,这不符合跳板的设计逻辑。

内容的提问来源于stack exchange,提问作者Raphaël

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 04:53:18