SSH隧道详解
场景一:本地端口转发实现跳板机
场景描述
想象一个业务场景,我的电脑想访问host03的9000端口,但是网络不通,但是我有host02的ssh登录权限,并且host02能够访问host03的9000端口,那么能不能通过host02作为跳板机直接让我的电脑访问host03:9000呢?
其实这个就是ssh本地端口转发的典型应用,可以实现跳板机的功能,如上图所示。
实验验证
准备3台主机,host02作为跳板机,最终想要实现 host01:8000 代理 host03:9000,通过访问 host01:8000 即可访问 host03:9000。
在host03监听9000端口:
1 | nc -lk 9000 |
在host01执行命令:
1 | ssh -g -L 8000:host03:9000 root@host02 |
在我的电脑访问host01:8000:
1 | nc host01 8000 |
实验成功!
场景二:远程端口转发实现内网穿透
场景描述
我在自己的电脑开发了一个应用,想通过把它暴露在公网请远端的朋友帮忙查看和调试,能不能实现呢?场景如下:
答案是肯定的。我们不仅可以把本机的普通端口暴露到公网,而且可以把ssh登录端口暴露,以实现外网登录内网的目的。
实验验证
内网穿透到普通端口
1)在我的电脑启动挂件绑定到我电脑的8081端口(这里以web应用为例)
2)在我的电脑执行如下命令实现远程转发
1 | ssh -g -R 8000:localhost:8081 user@pub-ip-server |
3)远程主机的安全组和防火墙放开8000端口允许外部访问
4)通过任意主机浏览器访问 pub-ip-sever:8000 进行验证
1 | curl http://pub-ip:8000 |
实验成功!
内网穿透暴露内网登录权限
1)我的电脑执行将登录权限暴露到公网
1 | ssh -g -R 10022:localhost:22 user@pub-ip-server |
2)外部任意主机通过登录 pub-ip-server 登录到我的电脑
1 | ssh myuser@pub-ip-server -p 10022 |
实验成功!
场景三:本地 socks5 实现访问代理
场景描述
在 HostA 的本地 10080 端口启动一个 socks5 服务,通过本地 socks5 代理的数据会通过 ssh 链接先发送给 HostB,再从 HostB 转发送给远程主机。
1 | HostA$ ssh -D localhost:10080 userb@HostB |
或者更优化地:
1 | HostA$ ssh -fN -D 0.0.0.0:10080 userb@HostB |
其中:
- 选项 -D 类似于选项为 -L 和 -R 的静态端口转发。像那些一样,我们可以让客户端只监听本地请求或从其他节点到达的请求,具体取决于我们将请求关联到哪个地址:
-D [bind_address:] port
。 - 在静态端口转发中可以看到,我们使用选项 -R 进行反向端口转发,而动态转发是不可能的。我们只能在 SSH 客户端创建 SOCKS 服务器,而不能在 SSH 服务器端创建。
- 注意
1080
是 SOCKS 服务器的典型端口,正如 8080 是 Web 代理服务器的典型端口一样,不过这里我们用的端口是10080。 - 选项 -N 防止实际启动远程 shell 交互式会话。当我们只用 ssh 来建立隧道时很有用。
- 选项 -f 会使 ssh 停留在后台并将其与当前 shell 分离,以便使该进程成为守护进程。如果没有选项 -N(或不指定命令),则不起作用,否则交互式 shell 将与后台进程不兼容。
- 对于通过 SOCKS 服务器访问另一个网络的应用程序,如果应用程序提供了对 SOCKS 服务器的特别支持,就会非常方便(虽然不是必需的),就像浏览器支持使用代理服务器一样。作为一个例子,如 Firefox 或 Internet Explorer 这样的浏览器使用 SOCKS 服务器访问另一个网络的应用程序。
实验验证
那么在 HostA 上面,浏览器配置 socks5 代理为 127.0.0.1:10080,看网页时就能把数据通过 HostB 代理出去,类似 ss/ssr 版本,只不过用 ssh 来实现。
实验前(chrome浏览器):
实验后(chrome浏览器):
使用优化
为了更好用一点,ssh 后面还可以加上:-CqTnN
参数,比如:
1 | root@host01$ ssh -CqTnN -L 0.0.0.0:8000:host03:9000 root@host02 |
其中 -C
为压缩数据,-q
安静模式,-T
禁止远程分配终端,-n
关闭标准输入,-N
不执行远程命令。此外视需要还可以增加 -f
参数,把 ssh 放到后台运行,即最佳实践:
1 | root@host01$ ssh -f -N -g -L 8000:host03:9000 root@host02 |
注意,ssh 代理没有短线重连功能,链接断了命令就退出了,所以需要些脚本监控重启,或者使用 autossh
之类的工具保持链接。
正向代理(-L)的第一种用法可以用 iptable 的 port-forwarding 模拟,iptable 性能更好,但是需要 root 权限,ssh -L 性能不好,但是正向代理花样更多些。反向代理(-R)一般就作为没有安装 frp/ngrok/shootback 时候的一种代替,但是数据传输的性能和稳定性当然 frp 这些专用软件更好。
socks5 代理(-D)其实是可以代替 ss/ssr 的,区别和上面类似。所以要长久使用,推荐安装对应软件,临时用一下 ssh 挺顺手。