这篇文章主要介绍了如何利用SSH 反向隧道穿透NAT,并演示了如何维持一条稳定的SSH 隧道。
假设有机器A 和B,A 有公网IP,B 位于NAT 之后并无可用的端口转发,现在想由A 主动向B 发起SSH 连接。由于B 在NAT 后端,无可用公网IP + 端口 这样一个组合,所以A 无法穿透NAT,这篇文章应对的就是这种情况。
首先有如下约定,因为很重要所以放在前面:
首先在B 上建立一个SSH 隧道,将A 的6766 端口转发到B 的22 端口上:
然后在A 上利用6766 端口反向SSH 到B:
要做的事情其实就是这么简单。
一个最简单的方法就是
下面在B 上做之前类似的事情,不同的是该隧道会由
之后你可以在A 上通过6766 端口访问B 了:
下面演示如何使用SSH 反向隧道,让C 连接到B。
首先在A 上编辑
然后重启
然后在B 上对之前用到的
之后在C 上利用A 的6766 端口SSH 连接到B:
至此你已经轻而易举的穿透了两层NAT。
首先打开A 上
然后在B 上新建一个用户autossh,根据权限最小化思想,B 上的
紧接着在B 上为autossh 用户创建SSH 密钥,并上传到A:
注意该密钥不要设置密码,也就是运行
然后在B 上创建以autossh 用户权限调用
在B 上让
如果你愿意,在这之后可以立刻启动它:
然后你可以在A 上使用这条反向隧道穿透B 所在的NAT SSH 连接到B:
或者是在C 上直接穿透两层NAT SSH 连接到B:
如果你对SSH 足够熟悉,你可以利用这条隧道做更多的事情,例如你可以在反向连接时指定动态端口转发:
假设C 是你家中的电脑,A 是你的VPS,B 是你公司的电脑。如果你这样做了,那么为浏览器设置端口为
相关帖子:https://briteming.blogspot.com/2016/04/ssh-tunnel-tips.html
------
假设有机器A 和B,A 有公网IP,B 位于NAT 之后并无可用的端口转发,现在想由A 主动向B 发起SSH 连接。由于B 在NAT 后端,无可用公网IP + 端口 这样一个组合,所以A 无法穿透NAT,这篇文章应对的就是这种情况。
首先有如下约定,因为很重要所以放在前面:
| 机器代号 | 机器位置 | 地址 | 账户 | ssh/sshd 端口 | 是否需要运行sshd |
|---|---|---|---|---|---|
| A | 位于公网 | a.site | usera | 22 | 是 |
| B | 位于NAT 之后 | localhost | userb | 22 | 是 |
| C | 位于NAT 之后 | localhost | userc | 22 | 否 |
这里默认你的系统init 程序为systemd,如果你使用其他的init 程序,如果没有特殊理由还是换到一个现代化的GNU/Linux 系统吧……
SSH 反向隧道
这种手段实质上是由B 向A 主动地建立一个SSH 隧道,将A 的6766 端口转发到B 的22 端口上,只要这条隧道不关闭,这个转发就是有效的。有了这个端口转发,只需要访问A 的6766 端口反向连接B 即可。首先在B 上建立一个SSH 隧道,将A 的6766 端口转发到B 的22 端口上:
|
|
|
|
隧道的维持
稳定性维持
然而不幸的是SSH 连接是会超时关闭的,如果连接关闭,隧道无法维持,那么A 就无法利用反向隧道穿透B 所在的NAT 了,为此我们需要一种方案来提供一条稳定的SSH 反向隧道。一个最简单的方法就是
autossh,这个软件会在超时之后自动重新建立SSH 隧道,这样就解决了隧道的稳定性问题,如果你使用Arch Linux,你可以这样获得它:
|
|
autossh 来维持:
|
|
-M 参数指定的端口用来监听隧道的状态,与端口转发无关。之后你可以在A 上通过6766 端口访问B 了:
|
|
隧道的自动建立
然而这又有了另外一个问题,如果B 重启隧道就会消失。那么需要有一种手段在B 每次启动时使用autossh 来建立SSH 隧道。很自然的一个想法就是做成服务,之后会给出在systemd 下的一种解决方案。“打洞”
之所以标题这么起,是因为自己觉得这件事情有点类似于UDP 打洞,即通过一台在公网的机器,让两台分别位于各自NAT 之后的机器可以建立SSH 连接。下面演示如何使用SSH 反向隧道,让C 连接到B。
首先在A 上编辑
sshd 的配置文件/etc/ssh/sshd_config,将GatewayPorts 开关打开:
|
|
sshd:
|
|
autossh 指令略加修改:
|
|
|
|
最终的解决方案
整合一下前面提到的,最终的解决方案如下:首先打开A 上
sshd 的GatewayPorts 开关,并重启sshd(如有需要)。然后在B 上新建一个用户autossh,根据权限最小化思想,B 上的
autossh 服务将以autossh 用户的身份运行,以尽大可能避免出现安全问题:
|
|
|
|
ssh-keygen 指令时尽管一路回车,不要输入额外的字符。然后在B 上创建以autossh 用户权限调用
autossh 的service 文件。将下面文本写入到文件/lib/systemd/system/autossh.service,并设置权限为644:
|
|
network-online.target 生效:
|
|
如果你使用然后设置该服务自动启动:systemd-networkd,你需要启用的服务则应当是systemd-networkd-wait-online。
|
|
|
|
|
|
|
|
|
|
7677 的sock4 本地(localhost)代理后,你就可以在家里的浏览器上看到公司内网的网页.相关帖子:https://briteming.blogspot.com/2016/04/ssh-tunnel-tips.html
------
使用 autossh 进行快速内网穿透
一、前言
总所周知,现在国内的普通宽带普遍都使用了内网 IP 地址。
如果想要搭建服务器,一般来说要么直接在云主机上搭建,要么在本地搭建然后借助云主机进行内网穿透。
由于云主机上存储空间的价格一般都比较昂贵,所以大多数人还是会选择内网穿透。
一说到搭建内网穿透服务器,可能大多数人(包括我)第一反应都是使用 Ngrok。
Ngrok 确实是一个很好的内网穿透全套解决方案,但是配置和管理都比较复杂,而且现在新版的 Ngrok 也已经开始商业化(闭源)了。
我一开始也是打算使用 Ngrok,但是弄了好久都不成功。结果在探索途中突然发现了 autossh 这玩意,顿时豁然开朗。我怎么就把 SSH 这么好的功能给忘了……
⚠️ 注意
• SSH 仅支持 TCP 端口映射。
autossh 使用了系统原生的 SSH 端口映射功能,性能开销非常小。
autossh 的配置也极其简单,只需要在服务器和客户端上进行少许的配置即可完成内网穿透。
本文纯属个人学习经验分享,仅供参考。如有错误请及时提出,谢谢!
二、操作环境
- 服务器:CentOS 7.4.1708(最小安装、关闭防火墙)
- 客户端:CentOS 7.4.1708
三、服务器的配置
3.1 配置 SSH 服务器
请使用 vim 之类的文本编辑器打开
/etc/security/limits.conf。
找到包含
GatewayPorts 字符串的这一行,然后将这一行改为:GatewayPorts clientspecified
也可以直接在文件末尾添加上面这一行。
接下来检查一下包含
PubkeyAuthentication 字符串的这一行,确定该行内容为:PubkeyAuthentication yes
(不是的话请手动修改)
修改、检查完成之后请保存文件。
该步骤的目的是允许客户端指定服务器内网穿透服务监听的端口,以及确认允许客户端使用密钥登录。
3.2 重启 SSH 服务
systemctl restart sshd
四、客户端的配置
4.1 安装 autossh 软件包
yum -y install autossh
4.2 生成 SSH 密钥,并上传至服务器
4.3 启动内网穿透
命令常用方法
autossh [-p <服务器 sshd 监听端口>] -M 0 -fN \
-o "PubkeyAuthentication=yes" \
-o "ServerAliveInterval 5" \
-o "ServerAliveCountMax 6" \
-o "ConnectTimeout=10" \
[-o ProxyCommand="nc --proxy-type http|socks4|socks5 --proxy <代理服务器地址>:<代理服务器端口> %h %p"] \
-R <远程服务器监听 IP>:<远程服务器监听端口>:<要映射服务的 IP>:<要映射服务的端口> \
[-R <远程服务器监听 IP>:<远程服务器监听端口>:<要映射服务的 IP>:<要映射服务的端口> ...] \
<服务器用户名>@<服务器 IP/域名>
注:可在同一 SSH 进程中映射多组端口。
| 选项 | 说明 |
|---|---|
-p <服务器 sshd 监听端口> | 配置服务器 SSH 服务的监听端口。 |
-M 0 | 不使用 autossh 自带的监控功能,只有当 ssh 进程退出时才会重新建立 ssh 连接。 |
-fN | 不在服务器上执行任何命令,仅用作端口转发。 |
-o "PubkeyAuthentication=yes" | 使用密钥对进行认证。 |
-o "PasswordAuthentication=no" | 禁止使用密码认证。 |
-o "ServerAliveInterval 5" | 客户端每隔 5 秒发送一个心跳包。 |
-o "ServerAliveCountMax 6" | 连续 6 个心跳包没回应视为连接丢失。 |
-o "ConnectTimeout=10" | 如果 SSH 进程不能在 10 秒内建立连接则自动退出。用于防止网络不好而导致的 SSH 进程假死。 |
-o ProxyCommand="nc --proxy-type http|socks4|socks5 --proxy <代理服务器地址>:<代理服务器端口> %h %p" | 透过代理服务器建立 SSH 连接。支持 SOCKS4、SOCKS5 及 HTTP 协议。(如果连接的是国外云主机强烈推荐使用) |
举例说明把本地的80端口映射到服务器上的8080端口,且服务器上的所有接口都要监听8080端口。autossh -M 0 -fN \ -o "PubkeyAuthentication=yes" \ -o "ServerAliveInterval 5" \ -o "ServerAliveCountMax 6" \ -o "ConnectTimeout=10" \ -R 0.0.0.0:8080:127.0.0.1:80 \ root@server
执行完 autossh 命令之后,系统就会一直维护这条映射条目,您就可以高枕无忧了!哪怕是网络不稳定,或者说服务器宕机重启了,在恢复正常的时候映射都会自动恢复。
启动 autossh 之后,可以看到一个
ssh 的子进程,这个子进程才是真正在执行端口映射的 SSH 进程。
如果想立即终止 autossh 并取消端口映射,可以使用
kill -9 命令直接杀死 autossh 进程。
如果想配置开机自动映射,直接把一整条命令写进
/etc/rc.local 文件即可,非常方便。五、总结 & 注意事项
如果需要映射的端口不多,只有几个的话,autossh 确实是最佳选择。
但是如果需要映射大量端口,建议还是使用类似 Ngrok 的端口映射工具,毕竟这类工具拥有比较完善的管理功能。
⚠️ 还有一点非常重要 ⚠️
鉴于目前的网络形势,如果需要把端口映射到国外的云主机上,那么我强烈建议把 SSH 流量封装到其它安全的协议中!
否则,一旦 SSH 的流量稍微一大,就非常容易导致云主机的 IP 无法访问。
具体实现方法我这里就不说了,请大家自行摸索,免得惹麻烦。