最近思考内网穿透的问题,联想到ssh的反向连接,于是学习了一下相关的命令。网上ssh命令详解的文章很多也很专业,这里我想用通俗的方式再说一下自己对ssh命令的理解。
1 问题描述
我在A学校,小伙伴在B学校。小伙伴在配置Linux环境的时候遇到些问题,来请求我的帮助。我想直接登录他的系统查错,可学校A和学校B属于两个不同的内网,互相不通,这可咋办呢?
2 解决方法
2.1 思路
使用ssh反向连接。这里先简单描述一下ssh反向连接的效果,先有一个感性的认识,下文会进一步探究其原理。
ssh反向连接,顾名思义,反过来连接。
通俗点解释,就是我先ssh主动连接你,建立起一条通道,使你能通过这条通道反向连接回我。
那什么时候会用到ssh反向连接呢?
最常见的应用场景就是我在内网中,而你在公网中。你想连接我,正常情况下是做不到的。但是通过ssh反向连接,就可以满足这个需求。
但是现在的情况是我和我的小伙伴都在内网中啊,要怎么使得两个内网中的电脑互相连通呢?
让我们来曲线救国。我们需要借助一台公网电脑来做牵线红娘,这里我使用的是自己的vps。下面给出具体方案。
2.2 方案
分两步走
- 小伙伴使用ssh远程端口转发命令,和公网电脑建立通道,使公网电脑有途径反向连接回小伙伴电脑。
- 我再ssh连接公网电脑,然后操作公网电脑ssh连接小伙伴电脑。
2.3 具体命令
- 小伙伴需要执行的操作
a. 使用ssh远程端口转发命令,和公网电脑建立通道。
ssh -R 6666:localhost:22 <公网电脑>
-R 参数即表示远程端口转发。
这条命令的意思是,小伙伴与公网电脑约定,发往公网电脑6666端口的数据会原封不动地转发到小伙伴电脑的22端口上。
所以之后,在我连接到公网电脑后,只需要操作公网电脑,利用公网电脑的6666端口,即可连接回小伙伴的电脑。
这便是我上文说的那条能够反向连接回小伙伴的通道。
上面的马赛克是我vps的ip地址,即公网电脑的ip地址,大家替换成自己的就好。
一般在使用的时候,还会加上 -f -C -N 这3个参数。
-f 表示后台运行。不加这个参数的话,小伙伴的终端就被ssh命令占用,不能接收其他命令输入了。
-C 表示压缩数据。
-N 表示不执行远程命令。不加这个参数的话,小伙伴将连接公网电脑,登录公网电脑的终端。
注意
选择公网电脑端口的时候,注意不要选择已经使用的端口,不然之后反向连接的链路会建立失败。
- 我需要执行的操作
a. 先ssh连接公网电脑
常用命令,就不作过多解释了。执行完这条命令,将连接公网电脑,登录公网电脑的终端。
b. 然后操作公网电脑ssh连接小伙伴电脑
ssh -p 6666 baba@localhost
-p 参数表示指定端口。
这条命令的意思是使用ssh,访问localhost的6666端口,来连接baba这个用户。
结合上文远程端口转发的操作,发往公网电脑6666端口的数据会原封不动地转发给小伙伴电脑的22端口,也就是说在公网电脑上ssh访问localhost的6666端口,实际上是访问了小伙伴电脑的22端口,故这条命令的效果是远程登录小伙伴的电脑。
如上图所示,登录baba用户之后,查看ip地址,是内网地址,说明确实登录了内网中小伙伴的电脑。
至此,就解决了两台内网主机通信的问题。
3 命令理解
ssh反向连接命令本质上使用的是ssh的端口转发功能。
具体来说,反向连接使用的是远程端口转发功能(-R参数),这里我想和本地端口转发功能(-L参数)对比来理解。
先给出两种用法的命令格式:
远程端口转发:
ssh -R
: :
本地端口转发:ssh -L
: :
还记得第一次接触这两条命令的时候,把我整蒙了。使用的时候,local和remote,到底哪个是哪个;有时候local指自己,remote指别人,有时候又反过来,把我搞得晕头转向;还有这个SSH host与local和remote又是什么关系。太难了。
我查阅了不少资料,排列组合这些参数做了一些实验,思考了很久才把这几个参数理清楚。这里我想用通俗一些的方式,解释这几个参数,希望能给刚接触ssh端口转发命令的同学们一点帮助。
3.1 铺垫
首先强调一个小细节:
不要将 ssh反向连接 与 ssh远程端口转发 混为一谈。
ssh连接都是由自己发起,去连接别人,建立一条通道。这里 -L/R 参数约定的是这条通道的端口转发的方向。
当使用 -R 参数时, 这条通道是反向转发数据,别人的ssh连接请求可以借助这条通道转发回自己,实现反向连接的目的。
注意这里存在两个ssh连接,一个是ssh远程端口转发建立的连接,另一个是ssh反向连接建立的连接,不要混为一谈了。
注意到这个细节,对于理解命令是有很大帮助的。
在解释命令之前,让我们再约定几个词语:ssh端口转发的起点我们叫做local,终点我们叫做remote。
也就是说,local和remote的定义 与 是否是ssh端口转发命令的发起者,没有必然联系,而是根据端口转发的方向来定义的。
弄清楚local与remote的定义,命令理解起来就不那么难了。
我们来改写一下端口转发命令:
端口转发:
ssh -L/R <起点 port>:<终点 host>:<终点 port>
我更喜欢写成这种格式,逻辑更加清楚:
端口转发:
ssh
-L/R <起点 port>:<终点 host>:<终点 port>
这样命令的意思就比较清晰了:先通过ssh连接别人,再和别人约定好端口转发的起点和终点信息,建立起端口转发的通道。
铺垫完毕,下面举例辅助理解。
3.2 举例
- 远程端口转发
将公网电脑的6666端口的数据转发给自己的22端口。这里数据转发方向是由 公网电脑的端口 到 自己的端口,故命令为
ssh -R <公网电脑 port>:<自己 ip>:<自己 port> <公网电脑>
即
ssh -R 6666:localhost:22 <公网电脑>
- 本地端口转发
将自己6666端口的数据转发给公网电脑的22端口。这里数据转发方向是由 自己的端口 到 公网电脑的端口,故命令为
ssh -L <自己 port>:<公网电脑 ip>:<公网电脑 port> <公网电脑>
即
ssh -L 6666:<公网电脑 ip>:22 <公网电脑>
当然也可以写成
ssh -L 6666:localhost:22 <公网电脑>
这种写法可能很多人都有疑惑:这个localhost是什么鬼?不是公网电脑的ip吗,怎么还能写成localhost?
这条命令曾经也让我困惑了很久,所以我特地把它拎出来,和大家聊一聊我的理解。
大家可以先做一个简单的实验,上面<终点 ip>信息,可以写成 <公网电脑 ip>,可以写成 localhost ,可以写成 127.0.0.1 ,但是不能写成自己电脑的ip,否则进行端口数据转发的时候会失败。
这个现象说明,这里填 localhost 或 127.0.0.1 其实并不是指代自己电脑,而是指代公网电脑。也就是说,这个 localhost 是从公网电脑的角度来看的。
阶段性总结
我们再重新理一下ssh命令:
ssh -L/R <起点 port>:<终点 ip>:<终点 port>
我们先通过ssh连接别人,再和别人约定好端口转发的起点和终点信息。
怎么约定呢?对于本地端口转发(-L参数),转发方向由自己到别人,起点信息是从自己电脑的角度来看,终点信息是从
而对于远程端口转发(-R参数),转发方向由别人到自己,起点信息是从别人(
- 进阶例子
现在有A,B,C三台电脑。假设A和C不通,但A能和B互通,C也能和B互通。在这种情况下,A怎么和C建立通信呢?
让我们学以致用,使用端口转发命令,借助A-B这个通道,使A与C建立通信。
首先在A这台电脑上,执行这条命令:
ssh -L : :
有了上面的基础,这条命令应该不难理解了。A在终端执行这条命令,先通过ssh连接上B,然后和B约定转发的起点和终点信息,建立起端口转发的通道。
然后在A这台电脑上,再执行这条命令,使用刚刚建立的端口转发通道,就能连接上C电脑:
ssh -p C@localhsot
这里A直接使用本地端口转发就能连上C,是因为B和C能互通,所以不需要麻烦C去和B建立反向连接。如果B和C之间是单向通信,只能C连B的话,就需要使用 2.2 方案,先麻烦C通过远程端口转发去建立和B的反向连接,来帮助A连上C。
所以需要注意这点,这里的终点必须是B能访问的电脑,否则数据转发会失败。
- 最后一个例子
现在有A,B,C,D四台电脑。假设A和D不通,但A能和B互通,D能和C互通,B和C能通信。在这种情况下,A怎么和D建立通信呢?
让我们举一反三,使用端口转发命令,借助B-C这个通道,使A与D建立通信。
首先在B这台电脑上,执行这条命令:
ssh -g -L : :
这里使用了一个新的参数,-g 参数。如果不使用这个参数,这个端口的数据来源只能是B电脑,其他电脑往B电脑这个端口发的数据不会被接收。加上 -g 参数后,其他电脑就也能使用这个端口进行数据转发了。
然后在A这台电脑上,只需要执行这条命令,使用刚刚建立的端口转发通道,就能连接上D电脑:
ssh -p D@
4 实际应用
实际上,ssh端口转发命令除了能用于ssh连接,还能做很多事情。下面模拟一个应用场景:借助ssh端口转发,来远程连接mysql服务器。通过这个应用场景,完整地走一遍流程。
4.1 问题描述
类似 3.2 举例中的最后一个例子,现在有A,B,C,D四台电脑。A是我的电脑,D是我想连接的mysql服务器。假设A和D不通,但A能和B互通,D能和C互通,B和C能通信。在这种情况下,我(A)要怎么连接上mysql服务器(D)呢?
四台电脑的ip地址如上图所示。
4.2 流程步骤
至始至终,我只需要坐在A电脑前进行操作。
- 首先ssh连接B电脑。
ssh [email protected]
- 连上B电脑后,在B电脑上配置本地端口转发。
ssh -gfCNL 6666:192.168.12.104:3306 [email protected]
解释:将B电脑6666端口的数据 经由C电脑 转发到D电脑的3306端口上。
- 断开与B的ssh连接,在A的终端上输入命令,远程连接mysql服务器。
mysql -h 192.168.12.102 -P 6666 -uroot -p
解释:通过B电脑的6666端口连接mysql服务器。
至此,我便成功连接上了mysql服务器。
4.3 实际意义
可能有人会质疑,整得这么花里胡哨的,真的用得上吗。整这么麻烦,什么时候还需要通过端口转发去连接一个服务呢?
比如这种情况:
内网1是一个诈骗团伙窝点,D是存放各种信息的数据库,C(可以有多台)是骗子们的电脑。为了安全,C和D是主机模式,D只能和C互通,不能和外界通信。而B是这些骗子们访问外界资源的出口。
我(A),是正义的化身,要把诈骗团伙数据库中的数据拖出来,作为证据提供给警察叔叔,为人民除恶。
那么就可以用到这种技术。
醒醒……
端口转发技术当然有很多非常有价值的应用场景。不过本文的目的是和大家分享一下对ssh反向连接、端口转发命令的理解,就不对应用场景做过多描述了。
5 总结
再回顾一下ssh端口转发命令的格式:
ssh -L/R <起点 port>:<终点 ip>:<终点 port>
先通过ssh连接
最后用一个比喻作为本篇文章的总结:
- 通过ssh端口转发命令,建立起一条数据管道:
- 管道中数据可以想象成水流,当使用本地端口转发时(-L参数),管道中的水从cilent端流向server端;当使用远程端口转发时(-R参数),管道中的水从server端流向cilent端。
- 管道的起点有一张过滤网,终点有一个发射器。建立管道时,起点信息,是对水流入口说的,用来过滤数据;终点信息,是对水流出口说的,用于精准投放数据。
- 当<终点 ip>为localhost时,表示将数据投放给水流出口自己。对于本地端口转发(-L参数),就是指SSH server;对于远程端口转发(-R参数),就是指SSH client。