SSH端口转发(SSH隧道)

SSH端口转发(SSH隧道)

本地端口转发

场景

有mysql使用经验的童鞋应该都知道,你们公司的mysql是不允许直接通过ip或域名去连接它的(一般都配置成只允许127.0.0.1localhost去连接它,也就是只能本地连接,不能远程连接),如果是使用mysql图形界面客户端连接,一般都是通过在图形界面客户端(如navicat)里使用SSH Tunnel(即SSH隧道)来连接的。

那如果我是直接用mysql命令行客户端呢?比如,在远程主机10.37.129.5上安装有mysql并且只允许本地访问,则我在自己电脑上想用以下命令去连接10.37.129.5上的mysql肯定是连接不上的:

mysql -h 10.37.129.5 -P 3306 -uroot -p

但肯定有解决方法,这就是下边要介绍的“ssh本地端口转发”。

本地端口转发

用以下命令可以建立一个本地端口转发进程,用于把发往本地端口3307中的数据转发到10.37.129.53306端口中

ssh -f -N -L localhost:3307:localhost:3306 [email protected]

这样,我们就可以使用以下命令在本地电脑上来连接远程服务器10.37.129.5中的mysql

mysql -h 127.0.0.1 -P 3307 -uroot -p

虽然mysql连接的是本机的3307,但因为有前面的端口转发ssh进程,它会把3307端口中的数据通过10.37.129.5这台机转发到localhost:3306中,而对10.37.129.5这台机来说,localhost刚好是它自己,于是这个数据最终被转发到10.37.129.5这台机的3306端口中(3306端口就是mysql默认端口)。

需要注意的是,如果你像下边这样写,可以转发但mysql是无法登录的

ssh -f -N -L localhost:3307:10.37.129.5:3306 [email protected]

可以看到我把localhost替换成了10.37.129.5,也就是说mysql服务器会最终识别中间那个冒号的后面部分是localhost:3306还是10.37.129.5:3306

另外需要注意的是,假如我写成下边这样还是有可能无法登录远程的mysql

ssh -f -N -L localhost:3307:127.0.0.1:3306 [email protected]

按前面的规则,10.37.129.5上的mysql识别到的登录地址是127.0.0.1:3306,这已经属于本地,按道理来说不应该不能登录呀,是的,按道理是这样的。

但关键就在于mysql允许登录的方式不是简单的允许“本地登录”或允许“远程登录”,而是严格的控制用户及ip/域名,在你的mysql中运行这条命令可查看允许登录的用户和ip:

select `Host`,`User` from user;

这是我的mysql查询结果

127.0.0.1   root
127.0.0.1   xiebruce
localhost   mysql.infoschema
localhost   mysql.session
localhost   mysql.sys
localhost   root

在上述结果中,我们可以看到有127.0.0.1 rootlocalhost root两条记录,假如我把127.0.0.1 root这条记录删除,那么就会造成前面所说的用127.0.0.1:3306无法登录的情况,这是特例吧,在其它服务中一般不会有这种限制。


  • -f 后台运行(f我个人认为应该是forward的首字母,开启后台转发模式)
  • -N 不执行远程命令(N可以理解为Not),后面有执行远程命令的例子
  • -f-N一般都是一起用,可以合在一起写成-fN-Nf
  • -L 指定转发规则,L表示Local,本地转发就是这么来的,-L后面的localhost:3307:localhost:3306就是它的参数,-L的参数的基本规则是这样的[bind_address:]port:host:hostport,其中前面的[bind_address:]port为转发源地址,后面的host:hostport为转发目标地址。

根据命令行参数通用规则,方括号括住的表示可以省略不写,也就是说上述端口转发命令可以写成(此时它默认就是127.0.0.1,而不会是localhost,因为localhost只是一个本地域名,它对应的ip就是127.0.0.1)

ssh -f -N -L 3307:localhost:3306 [email protected]

如果不写-f,它就不会进入后台,一直占用着终端

ssh -N -L 3307:localhost:3306 [email protected]

-w549

当不写-N时,即写成这样

ssh -f -L 3307:localhost:3306 [email protected]

上述命令会报错“Cannot fork into background without a command to execute.”。

前面说过,-N表示“不执行远程命令”,那么现在没有-N了,是不是就代表“要执行远程命令”了呢?没错,就是这样,所以上边命令才会报“Cannot fork into background without a command to execute.”,意思是“因为没有要执行的命令,所以无法fork一个进程到后台执行”!

执行远程命令可以这样写

ssh -f -L 3307:localhost:3306 [email protected] 'echo 111'

按道理,执行上述命令后,屏幕上就会打印出111,然后进入后台,开启一个端口转发进程,但实际上只会打印出111,而不会开启进程,把-f去掉也一样

ssh -L 3307:localhost:3306 [email protected] 'echo 111'

既然上述命令不会开启转发端口进程,其实去掉-L的所有参数,直接写成这样也是一样的

ssh [email protected] 'echo 111'

该命令其实就是使用ssh执行远程命令了(即在不登录进去目录服务器的情况下,执行目录服务器的命令)。

echo 11110.37.129.5机器上的命令,我只不过是拿echo 111举个例子,你可以把echo 111替换成任何10.37.129.5上可以执行的命令,例如查询端口netstat -tlunp | grep 3306

-f-N都不用,即写成下面这样时,会直接登录到10.37.129.5机器上

ssh -L 3307:localhost:3306 [email protected]

也就是说,在没有-f-N的情况下,单独的-L是不起作用的,于是上述命令就相当于直接使用ssh登录命令

ssh [email protected]

所以可以得出结论:启动本地端口转发进程必须使用-N,而且也肯定会使用-f,虽然不用它也可以,但不用它的话会导致进程一直在占用终端,无法后台执行,关闭终端就会结束,显然实际使用肯定没有人会这么做。

另外我们还可以把文章开头的命令写成这样(其中10.37.129.2是本地电脑的ip)

ssh -f -N -L 10.37.129.2:3307:localhost:3306 [email protected]

也就是把原来的localhost(或127.0.0.1)换成了本机ip10.37.129.2,这样就是监听本机ip为10.37.129.2的网卡的3306端口(之所以这样说,是因为一台电脑有可能有多个网卡,每个网卡一个ip)。

但是像上述命令这样只是监听本机单个ip,怎样监听所有ip呢?加个-g即可(g我不知道是gateway或global的意思,都可以吧)

ssh -g -f -N -L 3307:localhost:3306 [email protected]

需要注意的是,加了-g就不能写本地监听地址了(即3307不能写成localhost:3307或者10.37.129.5:3307了),否则-g不会起作用。

前面说过-L的参数的基本规则是这样的[bind_address:]port:host:hostport,其中前面的[bind_address:]port为转发源地址,后面的host:hostport为转发目标地址,这是不是意味着,转发目标地址未必是localhost或者或者机的地址呢?是的,它可以是其它地址。

比如还有另一台机10.37.129.6,那么本地电脑上的数据通过10.37.129.5转发到10.37.129.6中的3306端口,可以这么写:

ssh -f -N -L localhost:3307:10.37.129.6:3306 [email protected]

当然,根据前面所述,localhost:部分是可以省略不写的。

另外,本地端口转发,有时候会叫成“端口映射”,也就是说,前面的例子就相当于是把10.37.129.5中的localhost:3306映射到本地,这样我们就可以去连接它。

更多ssh各参数选项的作用,我们可以使用man ssh来查看。

远程端口转发

远程转发其实就是把-L(Local,即本地)改成-R(Remote,即远程)

ssh -f -N -R 9003:localhost:9002 [email protected]

它跟本地端口转发的区别是:

  • 远程端口转发模式会在远程服务器开启一个端口监听(但没有进程,可用netstat -tulnp | grep -v "grep" | grep 端口号查看);
  • 本机(执行这句命令的机器)则不会监听端口,但会有一个进程;
  • 远程服务器可以通过它上边监听的端口来反向访问到你本机(因此-R的R也可理解为Reverse),这就相当于反向穿透了,因为正常来说,你自己的电脑没有外网ip,服务器是不可能访问到你的电脑的,但是用这种方式就可以;
  • 此时本机的进程充当服务器角色,而远程服务器反而充当客户端角色;
  • -R 9003:localhost:9002参数格式跟-L的相同,即[bind_address:]port:host:hostport,其中前面的[bind_address:]port为转发源地址,后面的host:hostport为转发目标地址;
  • 关闭本机的ssh进程,则服务器那边的端口监听会自动关闭。

现在问题来了,哪个是源,哪个是目标?答案是,服务器那边是“源”,本地电脑为“目标”,所以在本例中,服务器那边监听的端口会是9003,而最终连接时,localhost:9002表示访问本地电脑的9002端口(因为此时本地电脑才是“远程”),所以本地电脑必须有一个服务监听9002端口用来接收服务器那边传过来的数据,而这个服务就看你具体的业务需要了。

最后提一点,其实端口转发是要在sshd配置文件/etc/ssh/sshd_config中开启AllowTcpForwarding yes的,它默认是注释的,不过注释里的默认值就是真正的默认值(即默认yes),所以我们可以不打开注释。


参考资料:ssh端口转发:ssh隧道

打赏

订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

2 评论
内联反馈
查看所有评论
123
123
1 年 前

文章写的很好

2
0
希望看到您的想法,请您发表评论x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

SSH端口转发(SSH隧道)