通过代理服务器ssh连接内网服务器
经常遇到的场景是,公司或学校的服务器都不能直接在外网进行访问,但是存在一个代理服务器,这时候就可以利用代理服务器来访问内网服务器。
使用跳板机访问内网服务器的场景和姿势都很多,我这里只介绍我碰到的场景。
网络状态
场景为,有客户端pc A,代理服务器P,目标服务器T,我们的最终需求是直接ssh到T上。
1 | +------+ +----------+ +----------+ |
目前我们可以做到的是,先在A上ssh连接到代理服务器P,然后继续ssh连接到T,可以简单描述为在A上执行下面的过程。
1 | ssh userP@hostnameP -p portP |
这种最朴素的方法需要两步ssh,但是能不能一步ssh就可以连接到T呢?
首先要问,为什么要一步ssh到T?
第一,这样可以简化ssh到目标服务器的过程,不需要两步ssh。第二,有些基于ssh的服务无法两步ssh,例如使用FileZilla等通过ssh浏览文件的软件,还有vscode Remote SSH 这样的插件,都需要一步ssh到目标服务器。
笔者面临的更复杂的场景
1 | +------+ +------------+ +----------+ +----------+ |
其中Firewall
F是一台网关服务器,而当执行ssh userP@hostnameF -p portF
时,会连接到P,这说明服务器P的ssh监听端口portP已经被转发到了网关F的portF上,但是笔者所在的网络中,这里的网关服务器不是用的简单的端口转发,而是使用了一个程序监听的portF,验证身份后再将该tcp的连接的内容转发到P的portP上。因此很多简单的方法都失效了。但是我在服务器P有一个端口portM,上可以建立从服务器P的端口portM到Firewall的连接,这是我最终能够实现目标的关键。
好了,下面介绍几种我尝试过的方法。前几种都是在描述的简单的场景下有用的方法,最后一种方法是对我的场景也有用的方法。
使用ssh的Proxy Jump功能
其实目前大部分系统自带的OpenSSH软件已经有了Proxy Jump功能,它exactly实现了我们第一个场景的需求。
在使用命令行直接ssh时,可以使用-J
参数来使用该功能。下面的命令描述了简单的使用方法,只要这样一行ssh命令就可以进行把服务器P当作ssh跳板的功能。
1 | ssh -J userP@hostnameP:portP userT@hostnameT:portT |
也可以在~/.ssh/config
中定义host来快捷地进行ssh访问
1 | # ssh config file |
这样只要使用
1 | ssh serverT |
就可以ssh到server
T上了。另外这里只用了一步跳转,其实还可以进行更多步跳转,如下面的~/.ssh/config
设置
1 | # ssh config file |
这样执行ssh serverT
后,会发生2步跳转,先经过服务器P,再经过服务器Q,最后到服务器T
使用ssh的ProxyCommand功能
老旧的OpenSSH程序是不支持ProxyJump功能的,这时候可以使用OpenSSH的ProxyCommand功能,也可以达到一样的效果。
1 | ssh -o ProxyCommand="ssh -W %h:%p userP@hostnameP:portP" userT@hostnameT:portT |
上面的一行命令也可以通过Server P进行跳转ssh
当然,这种方法也是可以通过~/.ssh/config
配置简单设置的。
使用ssh的-tt参数
如果OpenSSH老到连ProxyCommand都不支持,那么还可以通过-tt
参数实现。
1 | ssh -tt userP@hostnameP:portP ssh -tt userT@hostnameT:portT |
-tt
可以在ssh建立后立刻执行下一命令
使用端口转发
如果不是像笔者一样属于第二个情景,即ServerP以外还有一个防火墙,那么前面描述的方法已经完全够用了。但是笔者的情景必须要使用另外的方法。
之前提到,在笔者的场景中,服务器P上有额外的端口portM,而我们可以将Server T的portT端口转发到Server P的portM上,这样我们访问hostnameP:portM就相当于访问hostnameT:portT
端口转发有多种实现方法,笔者在网关服务器F上没有操作权限,但是可以在ssh时使用LocalForward功能进行端口转发
例如,我们可以将Server P的端口portM转发到pc A的本地端口portA上,只需要在pc A上运行
1 | ssh -L portA:localhost:portM userP@hostnameF:portF |
这样一来,我在pc
A上访问localhost:portA
就相当于访问hostnameP:portM
,下一步就是将hostnameT:portT
转发到hostnameP:portM
上。
不过要注意的是,LocalForward转发默认转发后只在本地监听端口,例如现在进行了一步转发后,pc
A的sshd进程会监听localhost:portA
,但是别的计算机是不能访问hostnameA:portA
的。
但是由于我们需要转发hostnameP:portM
,意味着portM是对其他ip也都可见的端口,那么就需要使用-g
(GatewayPorts)选项,这代表着会监听所有监听来自所有ip的对portM的请求。
在Server P上运行
1 | ssh -L portM:localhost:portT -g userT@hostnameT:portT |
就可以在Server
P上将hostnameT:portT转发到hostnameP:portM上,且整个内网都可以访问hostnameP:portM
这样一来,链条就打通了,首先在Server P上将hostnameT:portT转发到hostnameP:portM上,然后在pc A上将hostnameP:portM转发到localhost:portA上,之后只需要在pc A上运行
1 | ssh userT@localhost:portA |
就可以ssh到Server T上了
上面的命令都可以写入~/.ssh/config
中简化配置,在pc
A上,等效的config配置为
1 | Host serverP |
在Server P上,config配置为
1 | Host serverT |
配置完成后,在Server P上运行
1 | ssh serverT |
在pc A上运行下面的命令进行端口转发
1 | ssh serverP |
再在pc A上运行
1 | ssh serverT |
就可以直接ssh到Server T了,配置同样可以直接在vscode Remote SSH中使用。相对于使用vim写代码,个人更习惯通过vscode在serverT上写代码。
如果你的情境中Server P和Server T是一体的,即只是存在Firewall
F和Server T,那么只需要把原Server P上的config配置放在Server
T上,并在Server T上运行ssh serverT
,在pc
A的config设置也都改成server T的相关配置即可。
另外,笔者的环境下,使用vscode Remote SSH时必须在pc
A上指定IdentifyFile文件进行ssh serverT
- 文章链接: https://renzibei.com/2020/09/05/通过代理服务器ssh连接内网服务器/
- 版权声明: 本网站所有文章(包含文字、图片等内容)除特别声明外,均系作者原创,采用 CC BY-NC-ND 4.0 许可协议。引用与转载时请遵守协议、注明出处。