使用 SSH 隧道穿透能力访问远程内网设备
当我们需要调试远端的设备时,ssh 隧道能力可以帮助我们解决内网穿透的问题,也就是通过公网的代理服务器从一个局域网跳转到另一个局域网。
ssh 端口(ssh 隧道)转发简介
ssh 运行于本地设备,它的作用:登录到目标机器并在目标机器上执行命令。它可以建立一个安全通道,为不安全网络上两个不受信任的主机提供安全的加密通信。X11 连接、任意 TCP 端口和 UNIX 域套接字也可以通过 ssh 安全通道进行转发。
如果指定了命令,命令将在远程主机上执行,而不是在本机 shell 里执行。
ssh 选项简介
详细的 ssh 选项功能可使用man ssh
查看,这里主要介绍和端口转发功能密切相关的几个选项。
-T
不分配伪终端;
-C
请求压缩所有数据(包括 stdin、stdout、stderr 和用于转发的 X11、TCP 和 UNIX 域连接的数据)。
-f
请求 ssh 在执行命令之前转到后台。如果用户希望 ssh 在后台运行,但 ssh 需要用户提供密码或口令,使用 -f 选项就很有用,在用户输入密码之后,ssh 就会转入后台运行。
-N
不执行远程命令。此选项用于只需要端口转发功能时。
-g
允许远程主机连接到本地转发端口。如果用于多路复用连接,则必须在主进程上指定此选项。
-L

ssh -L选项
数据从本机转发到远程。本机上指定 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
-R
将远程主机(代理机 A)指定端口上的连接转发到本机端口;
-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 连接,连接到当前选项中指定的端口或套接字。
-D [bind_address:]port
指定本地“动态”应用程序级端口转发。它的工作方式是分配一个套接字来监听本地端口(可选绑定指定的 bind_address)。每当连接到此端口时,连接都通过安全通道进行转发,然后使用应用程序协议确定将远程计算机连接到何处。目前支持 SOCKS4 和 SOCKS5 协议,ssh 将充当 SOCKS 服务器。只有 root 用户可以转发特权端口。还可以在配置文件中指定动态端口转发。
ssh 是一种安全的远程登录及传输协议。ssh 可用于远程登录、远程文件传输等。
ssh 是安全的 shell。
使用 ssh 协议可以安全地将客户端与服务端通过网络连接起来,这为远程调试设备提供了可能性。
ssh 远程登录后的操作完全同本地 shell 的操作一致。
ssh 安全登录的方式有 2 种,密码口令验证和密钥验证。

ssh命令选项

密钥验证流程
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
利用 ssh 隧道建立远程调试环境

ssh隧道调试环境
代理机:把一个具有公网 IP 的中间服务器用作 ssh 代理,将这台代理机称作代理 A(Agent)。
目标机:把待调试的目标机器称作目标机 T(Target)。目标机通常是待调试的设备,处于局域网内,外网无法直接访问内网中的设备。
本地机:把调试用的本地计算机称作本地机 L(Local)。本地机通常也位于局域网内。
L 和 T 无法互相访问,但 L 和 T 都能访问 A。我们将 T 通过 ssh 连接到 A,将 L 也通过 ssh 连接到 A,A 用于转发数据,这样就能使用本地计算机 L 来访问远端设备 T。
目标机 T 建立与代理机 A 的 ssh 连接
目标机 T 上的 sshc 连接代理机 A 上的 sshd:
$ ssh -T -f -N -g -R :10022:127.0.0.1:22 QWQ@120.198.45.126
这条命令的作用:
- 1. 建立一条 ssh 连接,T 上的 ssh 客户端连接到 A 上的 ssh 服务器,A 的 IP 是 120.198.45.126,端口号是 10022,账号是 QWQ;
- 2. 如果有其他 ssh 客户端连接到了 A 的 10022 端口上,则 A 会将这条连接转发到 T,T 在内部建立新的连接,连接到本机 22 端口。
这条命令在 T 上执行。在 T 连接 A 这条命令里,T 是本地主机(local),A 是远程主机(remote)。
QWQ@120.198.45.126
表示使用远程主机 120.198.45.126 上的用户 QWQ 来连接远程主机;
:10022:127.0.0.1:22
表示本机回环接口(127.0.0.1,也可使用本机其他网络接口的地址,比如以太网 IP 或 WiFi 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)
代码程序中运行 ssh 命令小 case
- 1. 调用 ssh 命令时,为避免对 ssh 服务端的指纹询问,需要添加跳过选项。

ssh连接时对服务端指纹信任询问
dropbear 使用-y
选项。
$dbclient -y -T -f -N -g -R :10022:127.0.0.1:22 QWQ@120.198.45.126
openssh 使用-o StrictHostKeyChecking=no
选项。
$ ssh -o "StrictHostKeyChecking no" -T -f -N -g -R :10022:127.0.0.1:22 QWQ@120.198.45.126
- 1. 免密码输入交互
登录 ssh 服务时,通常会有密码验证交互。

ssh登录时密码交互
避免由用户手动输入登录密码有如下方法:
- 1. 用 ssh-copy-id 把本地主机的公钥复制到远程主机的 authorized_keys 文件上,登录不需要输入密码。
- 2. 用 expect 调用 shell 脚本,向 shell 脚本发送密码。这种方式是模拟键盘输入。
- 3. 如果是 openssh,则用 sshpass 向 ssh 命令行传递密码。如果是 dropbear,则通过 DROPBEAR_PASSWORD 环境变量向 ssh 命令行传递密码。
我们采用第 3 种方法。
假如代理机 A 上用户 QWQ 密码是 123456,则最终 C 代码里应执行的指令如下:
# openssh
sshpass -p '123456' ssh -o "StrictHostKeyChecking no" -T -f -N -g -R :10022:127.0.0.1:22 QWQ@120.198.45.126
注意:
远程映射端口,在服务器上端口监听在回环地址上,导致外部无法访问。
解决方法: 修改 SSH 服务端配置文件。
SSH 服务端的配置文件 /etc/ssh/sshd_config 中有一个参数 GatewayPorts,它控制着远程端口转发是否允许绑定到非环回地址。
把GatewayPorts 这一行注释放开,其值修改为 clientspecified。
GatewayPorts 指定是否允许远程主机连接到为客户端转发的端口。默认情况下,sshd(8)将远程端口转发绑定到环回地址。这将阻止其他远程主机连接到转发的端口。GatewayPorts可用于指定sshd应允许远程端口转发绑定到非环回地址,从而允许其他主机进行连接。参数可以是no,强制远程端口转发仅对本地主机可用,yes强制远程端口转发绑定到通配符地址,或者clientspecified允许客户端选择转发绑定到的地址。默认情况是no。