SSH实现多跳代理

Posted YuanZiming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SSH实现多跳代理相关的知识,希望对你有一定的参考价值。

背景

我们实验室有两台服务器,其中一台无法从外网访问,另一台设置了内网穿透。我平常在无法从外网访问的那台服务器上做实验,所以如果在实验室外要连接那台服务器就需要将设置了内网穿透的服务器作为跳板,通过两次ssh登进实验服务器。

最近我的实验需要联网下载包,但是服务器要联网需通过登录认证服务网页进行认证,而服务器又没有安装远程桌面,直接通过命令行访问认证网页实在是力不从心。所以我需要想办法设置代理,使得我可以通过自己电脑上的浏览器通过实验服务器访问认证网页,从而登录上网。

总结一下需求,就是网络中有A、B、C、D四个节点,其中A和B互联,B和C互联,C和D互联,如何设置使得A能访问D。

方法

考虑简单情况,如果B和D是互联的,那么就可以直接通过ssh的Socks代理实现功能,具体步骤如下:

  1. 在A运行:ssh -N -D 127.0.0.1:Aport Busername@Bhostname。其中Aport可以是A上任意闲置的端口。
  2. 在A中打开浏览器,设置浏览器的代理为socks5://127.0.0.1:Aport,然后输入D的网址,即可访问认证网页。

实际上,相当于A对D的请求被ssh代理转发到B上,由B的sshd进程访问D。所以如果B服务器要上网,通过这种方法在D上认证后能够上网的就是B。那么如何将A对D的请求转发到C上,由C访问D呢?

失败方法

我一开始考虑的方法是利用ssh的端口转发功能,端口转发实现的是将远程服务器的端口映射到本地端口。这么说是不是很抽象,然而大部分博客就是这样说的,能看懂就见鬼了。以我的需求做例子,在B上运行命令:ssh -L Bport:Dhostname:Dport Cusername@Chostname,其中Bport可以是B上任意闲置的端口,实现的功能就是可以通过访问Bport端口透过C访问D了。那么理论上我就可以通过前面所说的Socks代理方法用A访问D:

  1. 在A运行:ssh -N -D 127.0.0.1:Aport Busername@Bhostname。其中Aport可以是A上任意闲置的端口。
  2. 在A中打开浏览器,设置浏览器的代理为socks5://127.0.0.1:Aport,然后输入https://127.0.0.1:Bport,通过B代理透过C访问D。

为了测试,我在B上运行curl -I https://127.0.0.1:Bport,连是能连,但返回了证书错误,说明如果验证网页是http,方法应该是可行的。可惜认证网页是https,让我不得不去寻找其他方法。

顺带一提,ssh的端口转发功能分为两种,一种是本地端口转发,一种是远程端口转发,前面使用参数-L的方法是本地端口映射,如果要使用远程端口映射,则在C上运行命令ssh -R Bport:Dhostname:Dport Busername@Bhostname,其中Bport可以是B上任意闲置的端口,实现的功能和本地端口映射完全等价,不过本地端口转发是B上的ssh负责监听,转发给C上的sshd负责请求;远程端口转发是B上的sshd负责监听,转发给C上的ssh负责请求,ssh和sshd互换了身份,当然对于我的需求,两种方法都不满足。

成功方法

我在网上搜相关资料的时候,意外搜到一个给ssh本身添加代理的方法,顿时有了灵感。如果我能通过某种方法,使A能够直连到C,这样我就可以将C作为Socks5代理连到D了。那么如何使A直连到C呢?给ssh本身添加代理就是解决方案!具体步骤如下:

  1. 在A运行:ssh -N -D 127.0.0.1:Aport1 Busername@Bhostname,其中Aport1可以是A上任意闲置的端口。
  2. 在A运行:ssh -o ProxyCommand=\'C:\\Program Files\\Git\\mingw64\\bin\\connect.exe -S 127.0.0.1:Aport1 %h %p\' -p 20022 -N -D 127.0.0.1:Aport2 Cusername@Chostname,其中Aport2可以是A上任意和Aport1不一样的闲置端口。
  3. 在A中打开浏览器,设置浏览器的代理为socks5://127.0.0.1:Aport2,然后输入D的网址,即可访问认证网页。

注意A是Windows系统,所以给ssh添加代理的方法是使用connect.exe,Linux下使用的是Netcat,网上有很多相关资料。另外就是这个connect.exe似乎是Git自带的一个程序,我刚好电脑里有Git就用上了,不知道有没有别的办法。

这个方法的意思是先建立一个B服务器的代理,然后建立一个通过B代理连接C服务器的代理,再通过C代理连接D。从A出发的对D的请求,先通过第二条ssh指令转变为往C发送,而往C发送的请求通过第一条ssh指令转变为往B发送,B的sshd收到以后往C发送,C的sshd收到以后往D发送,就实现了功能。

扩展

基于该方法,不难将其扩展到任意跳服务器的代理。比如将我需求中的两跳服务器改为三跳,即有A、B、C、D、E五个节点,A和B互联,B和C互联,C和D互联,D和E互联,怎么从A连到E。参考前面方法,只需要在A执行三个ssh代理命令即可:

ssh -N -D 127.0.0.1:Aport1 Busername@Bhostname
ssh -o ProxyCommand=\'C:\\Program Files\\Git\\mingw64\\bin\\connect.exe -S 127.0.0.1:Aport1 %h %p\' -p 20022 -N -D 127.0.0.1:Aport2 Cusername@Chostname
ssh -o ProxyCommand=\'C:\\Program Files\\Git\\mingw64\\bin\\connect.exe -S 127.0.0.1:Aport2 %h %p\' -p 20022 -N -D 127.0.0.1:Aport3 Dusername@Dhostname

三条命令在不同的命令行窗口下执行。用浏览器设置Socks5代理端口为Aport3,就能访问E了。再多跳数也是如此,跳数多少就执行多少条ssh代理命令。

SSH通过代理连接

从目前网上的资料来看,通过代理来连接ssh有2种方式:

先来介绍一下Corkscrew

 1.解压并编译安装:

https://pkgs.org/download/corkscrew  下载deb软件包地址

安装deb软件包 dpkg -i xxx.deb

wget http://www.agroman.net/corkscrew/corkscrew-2.0.tar.gztar -xzvf corkscrew-2.0.tar.gz
cd corkscrew
./configure
make install

2.添加配置到ssh用户目录,/root/.ssh/config (编辑该配置文件)写入:

Host *
ProxyCommand corkscrew http-proxy.example.com 8080 %h %p

注意http-proxy.example.com为http代理地址(例如proxy.cmcc或者ip)根据实际更改,后面的8080为http代理端口。

如果HTTP代理需要用户名/密码验证,则需要写上代理验证文件。假设代理服务器是192.168.0.1:808。用户名密码是name:pass,打算存放在~/.ssh/proxyauth。则有

ProxyCommand corkscrew 192.168.0.1 808 %h %p ~/.ssh/proxyauth

新建/roo/.ssh/proxyauth文件,写上:

name:pass

接下来就可以直接连接了:

ssh root@112.33.9.154  -p 22

在第二次登录时就可看到上次登录ip,从而判断是否是通过代理连接了。

 

参考:

http://www.cnblogs.com/JoJosBizarreAdventure/p/5892355.html

http://bolg.malu.me/html/2011/1809.html

http://blog.csdn.net/asx20042005/article/details/7041294

 

以上是关于SSH实现多跳代理的主要内容,如果未能解决你的问题,请参考以下文章

带有密码提示 C# 的多跳 SSH 问题

在多跳 SSH 连接上重新建立交互式 shell

使用ssh隧道+privoxy实现Linux全局代理

使用 SSH 隧道实现端口转发SOCKS 代理

SSH通过代理连接

AUTOSSH设置ssh隧道,实现反向代理访问内网主机