天行健,君子以自强不息;地势坤,君子以厚德载物;

使用 SSH 隧道穿透能力访问远程内网设备

使用 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 隧道穿透能力访问远程内网设备》

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 端口转发模式

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 隧道穿透能力访问远程内网设备》

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. 1. 建立一条 ssh 连接,T 上的 ssh 客户端连接到 A 上的 ssh 服务器,A 的 IP 是 120.198.45.126,端口号是 10022,账号是 QWQ;
  2. 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. 1. 调用 ssh 命令时,为避免对 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. 1. 免密码输入交互
    登录 ssh 服务时,通常会有密码验证交互。
《使用 SSH 隧道穿透能力访问远程内网设备》

ssh登录时密码交互

避免由用户手动输入登录密码有如下方法:

  1. 1. 用 ssh-copy-id 把本地主机的公钥复制到远程主机的 authorized_keys 文件上,登录不需要输入密码。
  2. 2. 用 expect 调用 shell 脚本,向 shell 脚本发送密码。这种方式是模拟键盘输入。
  3. 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。

 

点赞

发表回复