通过 SSH 反向代理访问内网服务,并增强连接可靠性

前言

首先,什么是反向代理?下图解释了它与正向代理的区别。

本质上,都是网络代理。正向代理更偏向于 client 端,而反向代理更偏向 server 端。(其实这个概念并不太重要)

系统中自带的 ssh 命令,就可以满足正向/反向代理的需求。首先了解 ssh 命令的一些参数:

SSH 参数介绍

参数解释
-f后台执行ssh指令
-C允许压缩数据
-N不执行远程指令
-R将对方主机的某个端口转发到本地端指定机器的指定端口(下文解释)
-L将本机的某个端口转发到远端指定机器的指定端口(下文解释)

重点就是 -L、-R 两个参数,下面是它们的原理详解:

原理(重要)

-L [bind_address:]port:host:hostport
本地主机上侦听 port 端口(bind_address可选),一旦这个端口上有了连接,该连接就经过 SSH 通道转发出去,同时 Remote 主机和 host:hostport 建立连接。访问 Local 机器的 port 相当于访问 Remote 眼中的 host:hostport。如下图:


-R [bind_address:]port:host:hostport
远程主机上侦听 port 端口( bind_address可选,且需要先打开 sshd 的 GatewayPorts 选项,否则只能监听127.0.0.1),一旦这个端口上有了连接,该连接就经过 SSH 通道转发出去,同时 Local 主机和 host:hostport 端口建立连接。访问 Remote 机器的 port 相当于访问 Local 眼中的 host:hostport。如下图:

本地/远程 的区分有时也不是那么强制。一般来说,IP 更为固定的是 远程 端,负责被动接受连接;IP 更为动态的是 本地 端,负责主动发起连接(否则 IP 一变,原来的命令就失效了)。如果双方的 IP 都固定且可以互相访问,-L -R 都可以用。

按上面的知识,尝试实现下图中的 client → server 通信。

案例

假设我们有下图的网络拓扑:

其中 Proxy 1.2.3.4 为我们的公网服务器,且 IP 固定;client 和 proxy 在不同的 LAN,proxy 和 server 也在不同的 LAN,client & server 的 IP 都不固定。我们想用 client 访问 server 的 8000 端口。

在 Server 端操作

首先登录到 server 机器上,向 proxy 机器建立 SSH 连接,并把 proxy 机器的 7890 端口转发到 server 的 8000 端口。注意这时的“本地”机器指 server,“远程”机器指 proxy。

# 将远程公网服务器 7890 端口转发至本地 8000 端口
# 访问 Remote 机器的 port 相当于访问 Local 眼中的 host:hostport
# ssh -fCNR port:localhost:hostport 远程用户@远程ip
ssh -fCNR 7890:127.0.0.1:8000 remote_user@1.2.3.4

执行上述命令后,即可发现 proxy 机侦听了 127.0.0.1:7890,访问这个端口,相当于访问 server 机的 8000 端口。

在 Client 端操作

然后在 client 机器上,向 proxy 机器建立 SSH 连接,并把 client 机器的 5678 端口转发到 proxy 机器的 7890 端口。 注意这时的“本地”机器指 client,“远程”机器指 proxy。

# 将本地 5678 端口转发到远程的 7890 端口
# ssh -fCNL *:port:host:hostport 远程用户@远程ip
ssh -fCNL *:5678:127.0.0.1:7890 remote_user@1.2.3.4

执行上述命令后,即可发现 client 机侦听了 0.0.0.0:5678,访问这个端口,相当于访问 proxy 机的 7890 端口。配合上一个的转发,即访问的是 server 机的 8000 端口。

AutoSSH

上面的都是理论知识,实际使用时,很可能会遇到稳定性问题,例如连接建立后闲置太久被自动断开。最佳的办法是使用 AutoSSH 来替代 SSH,并传入 SSH 自带的 keepalive 参数。Linux 系统可通过 apt/yum 等包管理器安装。关于 AutoSSH 详细的说明可参考文末的”SSH tunnelling for fun and profit”一文。

举例来说,把原来的

ssh -fCNL *:5678:127.0.0.1:7890 remote_user@1.2.3.4

替换下面的即可。就是把 ssh 改为 autossh,加上-M 0参数,再加上两个-o参数。

autossh -f -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -CNL *:5678:127.0.0.1:7890 remote_user@1.2.3.4

相关参考

  1. Jerry Sheh. https://jerrysheh.me/post/44ca081e.html, 有改动转载
  2. https://blog.creke.net/722.html, 有改动转载
  3. man ssh 帮助文档
  4. SSH tunnelling for fun and profit: Autossh (everythingcli.org)
  5. 若只需做正向的端口转发,不需SSH隧道的话,还可以借助 ncat 工具ncat --keep-open --listen 12345 --sh-exec "ncat 123.4.5.6 23456"可把本地的12345端口转发至远端的23456端口

发表评论