通常, 我们用于调试的计算机无法远程访问位于局域网中的待调试设备. 通过 SSH 的端口转发 (又称 SSH 隧道) 技术, 可以实现这种远程调试功能.
下文中, sshc 指 SSH 客户端, sshd 指 SSH 服务器.
1. SSH 端口转发模式简介
SSH 客户端运行于本地机器, 它的作用是: 登录到目标机器并在目标机器上执行命令. 它可以建立一个安全通道, 为不安全网络上两个不受信任的主机提供安全的加密通信. X11 连接, 任意 TCP 端口和 UNIX 域套接字也可以通过 SSH 安全通道进行转发.
SSH 连接并登录到指定的主机名(用户名可选). 如果指定了命令, 命令将在远程主机上执行, 而不是在本机 shell 里执行.
1.1 SSH 常用选项简介
SSH 端口转发相关的常用选项如下:
-C
请求压缩所有数据(包括 stdin,stdout,stderr 和用于转发的 X11,TCP 和 UNIX 域连接的数据). 压缩算法与 gzip 使用的算法相同, 压缩级别由 SSH 协议版本 1 的 CompressionLevel 选项控制. 在调制解调器线路和其他慢速连接上采用压缩是可取的, 但它会减慢快速网络上的速度.
-f
请求 SSH 在执行命令之前转到后台. 如果用户希望 SSH 在后台运行, 但 SSH 需要用户提供密码或口令, 使用 -f 选项就很有用, 在用户输入密码之后, SSH 就会转入后台运行. 这个选项隐含了 -n 选项的功能(-n 选项将 stdin 重定向到 /dev/null, 从而避免后台进程读 stdin). 在远程站点上启动 X11 程序的推荐方法是使用 "ssh -f host xterm" .
如果 ExitOnForwardFailure 配置选项设置的是 "yes", 则使用 -f 选项启动的 SSH 客户端会等所有的远程端口转发建立成功后才将自己转到后台运行.
-n
将 stdin 重定向到 /dev/null (实际上是为了防止后台进程从 stdin 读取数据). 当 SSH 在后台运行时必须使用此选项.
一个常见的技巧是使用它在目标机器上运行 X11 程序. 例如, SSH -n shadow.cs.hut.fi Emacs & 将在 shadows.cs.hut.fi 上启动 Emacs 程序. X11 的连接将通过加密通道自动转发. SSH 程序将在后台运行.(如果 SSH 需要请求密码或口令, 则此操作无效; 参见 - f 选项.)
-N
不执行远程命令. 此选项用于只需要端口转发功能时.
-g
允许远程主机连接到本地转发端口. 如果用于多路复用连接, 则必须在主进程上指定此选项.
-t
强制分配一个伪终端. 在目标机上执行任意的基于屏幕的程序时(例如, 实现菜单服务), 分配伪终端很有用. 使用多个 -t 选项则会强制分配终端, 即使 SSH 没有本地终端.
-T
禁止分配伪终端.
- -L [bind_address:]port:host:hostport
- -L [bind_address:]port:remote_socket
- -L local_socket:host:hostport
- -L local_socket:remote_socket
数据从本机转发到远程. 本机上指定 TCP 端口或 UNIX 套接字的连接将被转发到目标机上指定端口或套接字.
上述参数中, bind_address 指本地地址; port 指本地端口; local_socket 指本地 UNIX 套接字; host 指远程主机地址; hostport 指远程端口; remote_socket 指远程 UNIX 套接字.
本地 (SSH 客户端) 与远程 (SSH 服务端) 建立一条连接, 此连接的形式有四种:
本地 [bind_address:]port <====> 远程 host:hostport
本地 [bind_address:]port <====> 远程 remote_socket
本地 local_socket <====> 远程 host:hostport
本地 local_socket <====> 远程 remote_socket
位于本机的 SSH 客户端会分配一个套接字来监听本地 TCP 端口 (port), 此套接字可绑定本机地址(bind_address, 可选, 本机不同网卡具有不同的 IP 地址) 或本地 UNIX 套接字(local_socket). 每当一个连接建立于本地端口或本地套接字时, 此连接就会通过安全通道进行转发.
也可在配置文件中设置端口转发功能. 只有超级用户可以转发特权端口.
默认情况下, 本地端口是根据 GatewayPorts 设置选项绑定的. 但是, 使用显式的 bind_address 可将连接绑定到指定地址. bind_address 值是 "localhost" 时, 表示仅监听本机内部数据[TODO: 待验证], 值为空或 "*" 时, 表示监听本机所有网卡的监听端口.
注意: localhost 是个域名, 不是地址, 它可以被配置为任意的 IP 地址, 不过通常情况下都指向 127.0.0.1(ipv4)和 ::1.127.0.0.1 这个地址通常分配给 loopback 接口. loopback 是一个特殊的网络接口(可理解成虚拟网卡), 用于本机中各个应用之间的网络交互.
GatewayPorts 说明 (查阅 man sshd_config): 指定是否允许远程主机 (SSH 客户端) 连接到本机 (SSH 服务端) 转发端口. 默认情况下, sshd(8)将远程端口转发绑定到环回地址, 这将阻止其他远程主机连接到本机转发端口. GatewayPorts 也可设置为将将远程端口转发绑定到非环回地址, 从而允许其他远程主机连接到本机. GatewayPorts 值 "no", 表示强制远程端口转发仅对本机可用; 值 "yes", 表示强制远程端口转发绑定到通配符地址; 值 "clientspecified", 表示允许客户端选择转发绑定到的地址. 默认是 "no".
- -R [bind_address:]port:host:hostport
- -R [bind_address:]port:local_socket
- -R remote_socket:host:hostport
- -R remote_socket:local_socket
此选项在本地机上执行, 目标机上指定 TCP 端口或 UNIX 套接字的连接将被转发到本机上指定端口或套接字.
上述参数中, bind_address 指远程地址; port 指远程端口; remote_socket 指远程 UNIX 套接字; host 指本地地址; hostport 指本地端口; local_socket 指本地 UNIX 套接字.
工作原理: 位于远程的 SSH 服务端会分配一个套接字来监听 TCP 端口或 UNIX 套接字. 当目标机 (服务端) 上有新的连接建立时, 此连接会通过安全通道进行转发, 本地机执行当前命令的进程收到此转发的连接后, 会在本机内部新建一条 SSH 连接, 连接到当前选项中指定的端口或套接字. 参 2.3 节分析.
也可在配置文件中设置端口转发功能. 只有超级用户可以转发特权端口.
默认情况下, 目标机 (服务端) 上的 TCP 监听套接字只绑定回环接口. 也可将目标机上的监听套接字绑定指定的 bind_address 地址. bind_address 值为空或 "*" 时, 表示目标机上的监听套接字会监听目标机上的所有网络接口. 仅当目标机上 GatewayPorts 设置选项使能时, 通过此选项为目标机指定 bind_address 才能绑定成功(参考 sshd_config(5)).
如果 port 参数是 '0', 目标机 (服务端) 可在运行时动态分配监听端口并通知本地机(客户端), 如果同时指定了 "-O forward" 选项, 则动态分配的监听端口会被打印在标准输出上.
-D [bind_address:]port
指定本地 "动态" 应用程序级端口转发. 它的工作方式是分配一个套接字来监听本地端口(可选绑定指定的 bind_address). 每当连接到此端口时, 连接都通过安全通道进行转发, 然后使用应用程序协议确定将远程计算机连接到何处. 目前支持 SOCKS4 和 SOCKS5 协议, SSH 将充当 SOCKS 服务器. 只有 root 用户可以转发特权端口. 还可以在配置文件中指定动态端口转发.
IPv6 地址可以通过将地址括在方括号中来指定. 只有超级用户可以转发特权端口. 默认情况下, 本地端口是根据 GatewayPorts 设置选项进行绑定的. 但是, 可以使用显式的 bind_address 将连接绑定到特定的地址. bind_address 值为 "localhost" 时表示监听端口仅绑定为本地使用, 而空地址或 "*" 表示监听所有网络接口的此端口.
1.2 SSH 端口转发模式
SSH 的端口转发有三种模式:
本地: SSH -C -f -N -g -L local_listen_port:remote_host:remote_port agent_user@agent_host
将本地机监听端口 local_listen_port 上的数据转发到远程端口 remote_host:remote_port
远程: SSH -C -f -N -g -R agent_listen_port:local_host:local_port agent_user@agent_host
将代理机监听端口 agent_listen_port 上的数据转发到本地端口 local_host:local_port
动态: SSH -C -f -N -g -D listen_port agent_user@agent_host
2. 利用 SSH 隧道建立远程调试环境
组网环境下设备角色如下:
代理机: 把一个具有公网 IP 的中间服务器用作 SSH 代理, 将这台代理机称作代理 A(Agent).
目标机: 把待调试的目标机器称作目标机 T(Target). 目标机通常是待调试的设备, 处于局域网内, 外网无法直接访问内网中的设备.
本地机: 把调试用的本地计算机称作本地机 L(Local). 本地机通常也位于局域网内.
L 和 T 无法互相访问, 但 L 和 T 都能访问 A. 我们将 T 通过 SSH 连接到 A, 将 L 也通过 SSH 连接到 A,A 用于转发数据, 这样就能使用本地计算机 L 来访问远端设备 R.
2.1 目标机 T (sshc)
2.1.1 shell 中 T 连接 A
目标机 T 上的 sshc 连接代理机 A 上的 sshd:
SSH -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
这条命令的作用:
1. 建立一条 SSH 连接, T 上的 SSH 客户端连接到 A 上的 SSH 服务器, A 的 IP 是 120.198.45.126, 端口号是 10022, 账号是 10022;
2. 如果有其他 SSH 客户端连接到了 A 的 10022 端口上, 则 A 会将这条连接转发到 T,T 在内部建立新的连接, 连接到本机 22 端口.
这条命令在 T 上执行. 在 T 连接 A 这条命令里, T 是本地主机(local),A 是远程主机(remote).
解释一下此命令各选项:
-T 不分配伪终端;
-f 使 SSH 进程在用户输入密码之后转入后台运行;
-N 不执行远程指令, 即远程主机 (代理机 A) 不需执行指令, 只作端口转发;
-g 允许远程主机 (代理机 A) 连接到本地转发端口;
-R 将远程主机 (代理机 A) 指定端口上的连接转发到本机端口;
frank@120.198.45.126
表示使用远程主机 120.198.45.126 上的用户 frank 来连接远程主机;
:10022:127.0.0.1:22
表示本机回环接口 (127.0.0.1, 也可使用本机其他网络接口的地址, 比如以太网 IP 或 Wi-Fi IP) 的 22 端口连接到远程主机的 10022 接口, 因远程主机 10022 绑定的地址为空, 所以远程主机会监听其所有网络接口的 10022 端口.
在目标机 shell 中查看连接是否建立:
- root@localhost:~# ps | grep SSH
- 22850 root 2492 S SSH -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- 22894 root 3500 S grep SSH
在目标机 shell 中关闭 SSH 连接:
kill -9 $(pidof SSH)
此时在目标机 T 和代理机 A 中查看 SSH 连接信息, 两端都可以看到 SSH 连接不存在了.
2.1.2 C 代码中 T 连接 A 的处理
C 代码中主要还是调用 2.1.1 节中的命令. 但是由 C 代码编译生成的进程无法在命令行和用户进行交互, 因此要避免交互问题.
1. 避免首次连接时的 y/n(或 yes/no) 询问
如果是首次登录代理机 A, 本机 (目标机 T) 没有 A 的信息, 需用用户手动输入 y 之后才能继续. 打印如下:
- root@localhost:~# SSH -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- Host '120.198.45.126' is not in the trusted hosts file.
- (SSH-rsa fingerprint md5 86:09:0c:1b:fd:0b:02:8c:29:62:7f:ff:70:1b:64:f5)
- Do you want to continue connecting? (y/n)
如果 T 上有 A 的信息, 可通过执行删除操作: rm ~/.SSH/known_hosts 再进行上述测试.
如果是在 C 代码中执行登录命令, 进程在后台自动运行, 是无法和用户进行交互的. 为了避免交互动作, 应该禁止 SSH 发出 y/n 的询问.
如果 SSH 客户端是 dropbear SSH, 则添加 -y 参数, 如下:
SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
如果 SSH 客户端是 openssh, 则添加 -o StrictHostKeyChecking=no 选项, 如下:
SSH -o "StrictHostKeyChecking no" -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
2. 避免输入登录密码
避免由用户手动输入登录密码有如下方法:
1) 用 SSH-copy-id 把本地主机的公钥复制到远程主机的 authorized_keys 文件上, 登录不需要输入密码.
2) 用 expect 调用 shell 脚本, 向 shell 脚本发送密码. 这种方式是模拟键盘输入.
3) 如果是 openssh, 则用 sshpass 向 SSH 命令行传递密码. 如果是 dropbear, 则通过 DROPBEAR_PASSWORD 环境变量向 SSH 命令行传递密码.
我们采用第 3 种方法.
假如代理机 A 上用户 frank 密码是 123456, 则最终 C 代码里应执行的指令如下:
- # openssh
- sshpass -p '123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- # dropbear
- DROPBEAR_PASSWORD='123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
dropbear 无法接收 DROPBEAR_PASSWORD 变量传递密码的处理方法:
dropbear 包含 SSH 客户端和 SSH 服务器, 体积小巧, 常用于嵌入式设备. dropbear SSH 无法接收 sshpass 传入的密码信息. 但 dropbear SSH 可以通过环境变量 DROPBEAR_PASSWORD 传入密码信息. openwrt 从某一版开始, 通过打补丁的方式禁用了 DROPBEAR_PASSWORD 选项, 我们可以找到对应的补丁, 开启 DROPBEAR_PASSWORD 选项, 再重新编译生成 dropbear. 如下:
修改 dropbear patch 文件(如下路径位于 openwrt 源码根目录):
VIM package/network/services/dropbear/patches/120-openwrt_options.patch
将如下几行删除:
- @@ -226,7 +226,7 @@ much traffic. */
- * note that it will be provided for all "hidden" client-interactive
- * style prompts - if you want something more sophisticated, use
- * SSH_ASKPASS instead. Comment out this var to remove this functionality.*/
- -#define DROPBEAR_PASSWORD_ENV "DROPBEAR_PASSWORD"
- +/*#define DROPBEAR_PASSWORD_ENV "DROPBEAR_PASSWORD"*/
- /* Define this (as well as ENABLE_CLI_PASSWORD_AUTH) to allow the use of
- * a helper program for the SSH client. The helper program should be
重新编译生成 dropbear, 并替换设备里已安装的 dropbear.
- #define DEFAULT_SSH_AGENT_HOST "120.198.45.126"
- #define DEFAULT_SSH_AGENT_PORT "10022"
- #define DEFAULT_SSH_AGENT_USER "ssha_debug"
- #define DEFAULT_SSH_AGENT_PASSWD "220011ssha"
- int login_to_ssh_agent(const char *host, const char *port, const char *user, const char *passwd)
- {
- // openssh client:
- // sshpass -p '123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- // dropbear SSH clent:
- // DROPBEAR_PASSWORD='123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- char cmd[256];
- snprintf(cmd, sizeof(cmd), "DROPBEAR_PASSWORD='%s'ssh -y -T -f -N -g -R :%s:127.0.0.1:22 %s@%s",
- (passwd != NULL) ? passwd : DEFAULT_SSH_AGENT_PASSWD,
- (port != NULL) ? port : DEFAULT_SSH_AGENT_PORT,
- (user != NULL) ? user : DEFAULT_SSH_AGENT_USER,
- (host != NULL) ? host : DEFAULT_SSH_AGENT_HOST);
- printf("login to ssh agent: \n%s\n", cmd);
- system(cmd);
- return 0;
- }
2.2 代理机 A (sshd)
在 /etc/SSH/sshd_config 中添加如下几行后重启 SSH 服务:
- GatewayPorts yes
- UseDNS no
- GSSAPIAuthentication no
目标机 T 发起连接后, 在代理机 A 上查询目标机 T 是否连接成功:
sudo netstat -anp | grep 10022
打印形如:
- tcp 0 0 0.0.0.0:10022 0.0.0.0:* LISTEN 8264/sshd: frank
- tcp6 0 0 :::10022 :::* LISTEN 8264/sshd: frank
上述打印中, 8264 就是和目标机 T 保持连接的 sshd 进程号, 如需关闭当前连接重新建立一个新的连接, 则先在代理机 A 上执行:
kill -9 8264
然后再执行 2.1 节的指令, 就会建立一次新的代理连接.
为了安全, 我们可以专门新建一个用户, 仅用于 SSH 端口转发功能, 不能在 shell 中使用此用户登录. 如下创建一个 ssha_debug 的用户, 无 shell 登录权限. 然后为此用户创建密码. 注意系统中 nologin 文件的位置, 不同系统可能路径不同.
- sudo useradd ssha_debug -M -s /usr/sbin/nologin
- sudo passwd ssha_debug
2.3 本地机 L (sshc)
2.3.1 本地机 L 登录目标机 T
有三种方式:
1. 在本地机 L 上通过 SSH 登录代理机 A, 在 A 的 shell 中再登录目标机 T
代理服务器的公网 ip 是 120.198.45.126, 内网 ip 是 192.168.1.102.
1) 先使用 SSH(SecureCRT 或 OpenSSH 命令行) 登录上代理服务器的 shell. 如果调试机在内网, 既可登录代理机的外网 ip, 也可登录其内网 ip.
2) 在代理机的 shell 中执行如下命令登录远程设备:
SSH -p 10022 root@127.0.0.1 -vvv
注意, 此命令中用户 root 及其密码是远程设备上的账户.
如果提示 Host key 认证失败之类的信息, 请按提示执行如下命令:
SSH-keygen -f "/home/frank/.ssh/known_hosts" -R [127.0.0.1]:10022
也可直接删除当前用户目录下的 .SSH/known_hosts 文件.
然后重新执行登录设备操作.
建议优先使用此方法.
2. 在本地机 L 上使用 SSH 命令登录目标机 T
Win 10 系统默认安装有 OpenSSH 客户端. 可以在调试机 Windows 命令行中执行:
SSH -p 10022 root@120.198.45.126 -vvv
对于本地计算机来说, 待调试的设备 ip 地址不可见. 本机登录到代理机 120.198.45.126 的转发端口 10022, 通过代理机转发功能, 本地机能成功登录到远程设备上. 注意, 此命令中用户 root 及其密码是设备上的账户, 不是 SSH 代理服务器上的账户.
如果出现认证失败之类的信息. 可删除 C:/Users / 当前用户 /.SSH/known_hosts 文件, 然后再试.
3. 在本地机 L 上使用 SecureCRT 工具登录目标机 T
也可以直接使用 SecureCRT 软件, 设置好代理机的 ip(120.198.45.126) 和端口号(10022), 填上设备的登录用户和登录密码.
不建议使用此方法. 因为连接过程太长或连接失败的话, 无法看到错误提示信息.
2.3.2 查看代理机 A 打印信息
在 L 执行登录 T 之前查看打印信息:
- frank@SERVER:~$ sudo netstat -anp | grep 10022
- tcp 0 0 0.0.0.0:10022 0.0.0.0:* LISTEN 106438/sshd: frank
- tcp6 0 0 :::10022 :::* LISTEN 106438/sshd: frank
在 L 执行登录 T 之后查看打印信息:
- frank@SERVER:~$ sudo netstat -anp | grep 10022
- tcp 0 0 0.0.0.0:10022 0.0.0.0:* LISTEN 106438/sshd: frank
- tcp 0 0 192.168.1.102:10022 120.229.163.51:27027 ESTABLISHED 106438/sshd: frank
- tcp6 0 0 :::10022 :::* LISTEN 106438/sshd: frank
可以看到, 上述第二行是 L 执行登录命令后新出现的打印信息. 表示新建立了一条 L 到 A 的 SSH 连接.
L 端的外网地址 120.229.163.51:27027 连接到 A 上的 192.168.1.102:10022,L 通常位于局域网内, 具有一个内网地址, 120.229.163.51 可能是 L 连接的路由器的公网 IP.
这条连接建立后, A 将这条连接转发到 R.
2.3.3 查看目标机 T 打印信息
在 L 执行登录 T 之前查看打印信息:
- root@localhost:~# netstat -anp | grep 22
- tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 917/sshd
- tcp 0 0 192.168.202.140:47989 120.198.45.126:22 ESTABLISHED 9452/SSH
- tcp 0 0 192.168.202.140:22 192.168.202.100:64737 ESTABLISHED 2041/sshd
- tcp 0 0 :::22 :::* LISTEN 917/sshd
在 L 执行登录 T 之后查看打印信息:
- root@localhost:~# netstat -anp | grep 22
- tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 917/sshd
- tcp 0 0 192.168.202.140:47989 120.198.45.126:22 ESTABLISHED 9452/SSH
- tcp 0 0 192.168.202.140:51732 192.168.202.140:22 ESTABLISHED 9452/SSH
- tcp 0 0 192.168.202.140:22 192.168.202.140:51732 ESTABLISHED 9579/sshd
- tcp 0 0 :::22 :::* LISTEN 917/sshd
可以看到, 上述第 3 行和第 4 行是登录之后新增加的打印信息.
第 2 行, 表示 T 上的 SSH 客户端连接到了 A 上的 SSH 服务端, 进程号是 9452. 第 3 行, 表示进程 9452 收到了 A 转发来的 SSH 连接后, 在本机内部建立新的 SSH 连接, 使用 51732 端口号作为 SSH 客户端, 连接到本机 22 端口, 22 端口是 sshd 端口. 第 4 行, 表示本机新启动一个 sshd 进程, 来接收 sshc 的连接.
这样, L 到 T 的 SSH 通路彻底打通了. A 将来自 L 的连接转发到 R,R 在内部启动了 sshd 来处理来自 L 的请求, 通过 A 的代理作用, 实现了 L 上的 sshc 和 T 上的 sshd 的交互.
3. 典型使用场景步骤总结
上文已涵盖详细使用方法, 但篇幅太长. 此处简单总结使用步骤如下:
3.1 在代理机 A 上执行
使用 SecureCRT 登录代理机 A. 代理机外网 ip 120.198.45.126, 内网 ip 192.168.1.102, 端口 22. 如果本地机与代理机在同一个局域网里, 使用代理机的内网 ip 登录即可.
在代理机 shell 中查看是否有未关闭的 SSH 隧道:
sudo netstat -anp | grep 10022
若打印形如:
- tcp 0 0 0.0.0.0:10022 0.0.0.0:* LISTEN 8264/sshd: frank
- tcp6 0 0 :::10022 :::* LISTEN 8264/sshd: frank
则表示有未关闭的 SSH 隧道连接. 执行如下命令可关闭连接.
kill -9 8264
3.2 在目标机 T 上执行
使用远程应用程序接口或者在远程设备 T 上做一些特殊操作, 触发 T 执行如下两条指令之一:
- # openssh
- sshpass -p '123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
- # dropbear
- DROPBEAR_PASSWORD='123456' SSH -y -T -f -N -g -R :10022:127.0.0.1:22 frank@120.198.45.126
3.3 在本地机 L 上执行
在本地机 L 上执行如下指令, 登录远程目标机 T:
SSH -vvv -p 10022 root@120.198.45.126
另外一种变通的方式是, 在本地机先通过 SSH 登录上代理机 A 的 shell. 然后在 A 的 shell 中执行如下指令:
SSH -vvv -p 10022 root@127.0.0.1
4. 注意事项
1. 确保代理机 A 所在的网络防火墙不屏蔽 10022 端口
2. 确保代理机 A 上 /etc/SSH/sshd_config 配置文件设置正确
3. 关闭 SSH 隧道既可在代理机 A 上进行(关闭相应的 sshd 进程), 也可在目标机 T 上进行(关闭相应的 SSH 进程)
4. 每次只能访问一台目标机. 如果想同时访问多台, 可以代理机上设置多个转发端口, 每条连接使用一个端口进行转发
5. 为保证安全, 打开 SSH 隧道时尽量使用无登录权限的用户, 并且此用户的密码建议经常更新
5. 参考资料
[1] 阮一峰, SSH 原理与运用(二): 远程操作与端口转发
[2] SSH 代理设置 https://jin-yang.github.io/post/ssh-proxy.html
[3] SSH 命令和 SSH 服务详解
6. 修改记录
2019-11-20 V1.0 初稿
来源: https://www.cnblogs.com/leisure_chn/p/11903758.html