通过 SSH 隧道端口转发实现内网穿透
前面介绍了一种使用 V2ray 实现的内网穿透方法,虽然达成了目的,但有诸多不足,例如必须在两台设备上同时运行 V2ray,配置文件比较难懂等。尤其是对平时没有科学上网需求(自然也没有科学上网经历)的同学而言,下载 V2ray 本就是件令人头疼的事。
但事实上,常用的 ssh 命令也可以用来搞内网穿透,只需要一条命令即可。
现在假设我们面临如下图所示的一种情形:
左侧蓝色框内为家里的局域网,其中包含了一台运行 80 端口 Web 服务的服务器 Server C,其内网 IP 为 192.168.1.100,通过路由器的 NAT 地址转换接入 Internet。右侧绿色框内为外部的互联网,其中有一台拥有公网 IP 地址(1.2.3.4)的服务器 Server B。另有一台能访问互联网,但未接入左侧局域网的设备 Laptop A。现 Laptop A 想通过连接 Server B 的 8080 端口来访问 Server C80 端口上的 Web 服务。
可见该情形就是最一般的内网穿透情形,在上面的网络拓扑结构中,既然 Server B 拥有公网 IP,我们可以不过分地假设 Server C 能通过 SSH 与 Server B 建立连接,其中,Server B 的 SSH 登录用户名为 user。另外,只要 Server B 愿意,Laptop A 也可以正常访问到它,那么就可以通过在 Server C 到 Server B 之间建立 SSH 隧道,来实现 Laptop A 对 Server C 的访问。
接下来先简单介绍一下 ssh 命令中的端口转发功能,这里用到的端口转发称为所谓的 “远程端口转发”,另外还有 “本地端口转发” 等。“远程端口转发” 命令常用格式如下:
ssh -R [bind_address:]port:host:hostport user@ip
-R 表示 remote,bind_address 和 port 表示远程主机上的监听地址、端口对,host 和 hostport 表示目标主机服务的地址、端口对,user 与 ip 分别表示远程主机的用户与公网 IP。其中 bind_adderss 可以省略,在省略情况下,该值相当于 0.0.0.0,即监听所有地址。
上面的命令达到的目的是,将对公网主机 Server B 的 port 端口的访问转发到对内网主机 Server C(host)的 hostport 端口的访问。
具体实现方法为(以拓扑图内的 IP 地址为例):
在 Server C 上运行命令:
ssh -R 8080:localhost:80 user@1.2.3.4
8080 是 Laptop A 访问 Server B 时用到的端口,localhost 和 80 表示转发的端口是位于本地的 80 端口,后面的 user、1.2.3.4 即 ssh 用户、IP 地址对。
如果 Server C 因为某些原因没办法运行 ssh 命令,可以在局域网中接入一台 Linux,然后在该设备上运行命令:
ssh -R 8080:192.168.1.100:80 user@1.2.3.4
能达到相同的效果。
不过,这样运行命令,会在设备上打开一个 shell 终端,如果关闭了终端,也将切断建立的 SSH 隧道,有一点不方便,为了避免之,可以将命令改成如下:
ssh -fNR 8080:localhost:80 user@1.2.3.4
新增参数 - f 表示将 SSH 转入后台运行,-N 表示只连接远程主机,不打开远程 shell 终端。
在实际使用时,除了远程主机防火墙放行所需的 8080 端口外,还需要对 SSH 服务进行配置:
远程主机 Server B 需要修改 sshd_config 文件:
sudo vi /etc/ssh/sshd_config
将文件中的 GatewayPorts
项设置为 yes,该配置表示允许任何人连接到转发的端口,否则将只有远程主机自身可以连接。
另外由于一段时间不发送数据包会导致 SSH 连接自动中断,为了保持 SSH 的长连接,一般可以从服务端或客户端进行配置,我感觉在客户端配置更灵活一些,即配置在发起 SSH 连接的设备上,如下:
Server C 修改 ssh_config 文件:
sudo vi /etc/ssh/ssh_config
在文件的 Host * 下面加上 ServerAliveInterval
项:
Host *
ServerAliveInterval 60
该配置使得客户端每隔 60 秒向服务器发送一个 KeepAlive 请求,若服务器发出响应,则保持连接。
经过实测,使用该方法进行内网穿透需要稳定持续的网络连接作为保证,我在校园网状态下,一旦切换网络流量出口便会导致 SSH 隧道断开。