ssh端口转发-本地端口转发和远程端口转发的具体用法实例
ssh端口转发
在一开始学习ssh端口转发的时候,总是被本地端口转发和远程端口转发的区别搞得头大,缠斗数日无果,沉思一番之后,决定逐个攻破,先揪住一个掰开揉碎,另一个再如法炮制,如此一来,迷惑可解矣。
ssh一般会涉及到3个角色:两个端之间建立ssh连接,还有一个角色则是转发的目的地,为避免A、B、C这样一看便觉枯燥的指代名词,我们请出在东方大陆妇孺皆知的取经队伍
本地端口转发
某一日悟空又和三藏闹矛盾了,三藏赌气不理悟空了,这时悟空想要和三藏建立联系就需要八戒来当传声筒。
现在利用三台虚拟机来具象化这个例子
悟空:192.168.1.1(CentOS7.8)
八戒:192.168.1.2(CentOS7.8)
三藏:192.168.1.3(Ubuntu20.04)
192.168.1.3的防火墙设置拒绝来自192.168.1.1的访问
ufw deny from 192.168.1.1
这时,192.168.1.1想要和192.168.1.3建立连接,就可以通过192.168.1.2来建立一个隧道,在192.168.1.1上执行ssh -L 30000:192.168.1.3:22 root@192.168.1.2
,这样隧道就建立起来了。这里-L
即Local
,指本地端口转发;30000:192.168.1.3:22
指把对192.168.1.1的30000端口的访问转发到192.168.1.3的22端口(在做转发之前应该先检查192.168.1.1的30000端口是否已经被占用了,可以使用lsof -i:port
来进行检查);最后面的root@192.168.1.2
要和最前面的ssh
放在一起看,也就是说,192.168.1.1先执行了ssh root@192.168.1.2
,依据这个ssh连接建立起了 一个本地端口转发的隧道,隧道实现了 30000:192.168.1.3:22
所指的转发工作。
当完成了上面所说的建立隧道的工作,就可以测试隧道的连通性了,在任意一个192.168.1.1的终端执行telnet localhost 30000
进行测试,如果测试通过(输出结果为已连接,而不是连接被拒绝),说明隧道没问题,下一步可以尝试使用ssh通过隧道连接到192.168.1.3了,在任意一个192.168.1.1的终端执行ssh root@localhost -p 30000
,即可以ssh登录到192.168.1.3,在192.168.1.3看来,该ssh会话的来源是192.168.1.2。
有个小细节,在192.168.1.1上执行ssh -L 30000:192.168.1.3:22 root@192.168.1.2
之后,终端会打开192.168.1.1到192.168.1.2的会话,但是这样原来192.168.1.1的终端就被占用了,为了节省出这个终端,执行端口转发可以加上-Nf
参数,-N
表示不执行命令,使得该ssh连接只起到隧道的作用,-f
表示在后台运行该ssh连接。还有一点要注意,-f
参数不能单独使用,因为在没有 -N
特别指明的情况下,无法把没有指定登录后要执行的命令的ssh会话放到后台。
除了在192.168.1.1执行端口转发,192.168.1.1要和192.168.1.3建立会话,在192.168.1.2做一个本地端口转发也能达到同样的目的。在192.168.1.2执行:ssh -NfL 30000:192.168.1.3:22 root@localhost
,这里192.168.1.2与自身建立了一个会话,把对自己的30000端口的访问转发到了192.168.1.3的22端口。但是这样还不能达到目的,这是因为OpenSSH有一个限制,默认情况下,ssh只监听来自本机的回环地址的对于本地转发端口的访问,在这里也就是只有192.168.1.2自己才能访问30000这个本地转发端口。想要达到目的,还需要一个参数:-g
,-g
能够取消这个限制,使得其他主机也能够访问这个本地转发端口,所以192.168.1.2应该执行的完整的命令是:ssh -g -NfL 30000:192.168.1.3:22 root@localhost
。此外,如果192.168.1.2开启了iptables,iptables的INPUT链中相关的 target 为 REJECT 的规则也会阻止其他主机访问本地转发端口。
远程端口转发
某一日悟空又和三藏闹矛盾了,三藏把悟空赶走了,悟空于是回到花果山,开始过逍遥自在的日子。没过多久,三藏那边取经形式日益严重,路上遇到的妖怪八戒和沙僧解决起来力有不逮,唐僧于是责怪八戒能力不足,八戒开始怀念大师兄在的日子,但是此时唐僧和猴哥都不愿说软话,八戒没得办法,只得主动去找大师兄,陪着笑脸,意在沟通唐僧和悟空彼此思念之情。
仍然以三台虚拟机来创建实例
悟空:192.168.1.1(CentOS7.8)
八戒:192.168.1.2(CentOS7.8)
三藏:192.168.1.3(Ubuntu20.04)
192.168.1.3的防火墙设置拒绝来自192.168.1.1的访问
ufw deny from 192.168.1.1
这一回,192.168.1.1无意主动通过192.168.1.2建立隧道,反过来,192.168.1.2要主动与192.168.1.1建立连接,在192.168.1.1和192.168.1.3之间建立一个隧道。为此,192.168.1.2需要执行:ssh -NfR 30000:192.168.1.3:22 root@192.168.1.1
,-R
即remote
,30000:192.168.1.3:22
同样是指把对192.168.1.1的30000端口的访问转发到192.168.1.3的22端口,注意不要把这里的转发端口当成是执行端口转发命令的一方。最后的root@192.168.1.1
的解释参考本地端口转发部分。
测试隧道的连通性同本地端口转发部分,在任意一个192.168.1.1的终端执行telnet localhost 30000
进行测试。
容易产生歧义的
还有一件事,来看这种情况:192.168.1.1执行 ssh -L 30000:localhost:22 root@192.168.1.2
,这里不考虑转发的合理性,只关注一件事:命令中的localhost
指的是谁,根据实际测试,上面命令中的localhost
指的是192.168.1.2;而如果192.168.1.1执行的是 ssh -R 30000:localhost:22 root@192.168.1.2
,情况是否相同呢?根据实际测试,这种情况下的localhost
指的是192.168.1.1。也就是说,如果是本地端口转发,localhost
指的是接受ssh连接的一方;如果是远程端口转发,localhost
指的是发起ssh连接的一方(也就是执行端口转发命令的一方)。更统一的说法是:不论本地端口转发还是远程端口转发,localhost
都是指连通隧道两端的那个主机。
参考资料
[1] SSH, the Secure Shell, 2nd Edition (Barrett, Daniel J)-Chapter 9. Port Forwarding and X Forwarding
[2] Ubuntu 防火墙命令(查看,关闭,启动) 博客园
[3] 如何列出和删除防火墙规则UFW myfreax