mirror of
https://github.com/beyondx/Notes.git
synced 2026-02-06 11:53:55 +08:00
88 lines
9.2 KiB
Plaintext
88 lines
9.2 KiB
Plaintext
Content-Type: text/x-zim-wiki
|
||
Wiki-Format: zim 0.4
|
||
Creation-Date: 2011-04-06T18:29:40+08:00
|
||
|
||
====== SSH tunnel tips ======
|
||
by pluskid, on 2009-08-14, in Tool 11 comments
|
||
http://blog.pluskid.org/?p=369
|
||
|
||
以前接触 SSH 的时候我就在文档上见过 SSH tunnel 相关的东西,然而当时没有怎么看明白,也就一直没有深究,直到最近需求越来越多了,才终于发现这个东西原来这么有用,于是记录在此。
|
||
|
||
** SSH tunnel** 主要有三种,一种是** dynamic application-level port forwarding **,可以用来作为 SOCKS proxy ,通常翻墙就是用的这种端口映射;一种是** Local Forwarding** ,主要用于提供常规的加密隧道,例如让 IMAP 协议通过这个加密隧道,避免密码在网络上被人监听到;一种是 **Remote Forwarding** ,可以用于逆向穿透 NAT 。其实,另外还有一种专门为 X 打造的 **X11 Forwarding** ,一般不会用到,Linux 下远程登录通常不会需要开启 X 程序,或者有其他更好用的方法,因此我们这里并不打算介绍这个。
|
||
|
||
首先我们来假定一个网络环境:有一个防火墙,在防火墙内部有一个机器 H ,通过 NAT 访问 Internet ,并且在防火墙外的 Internet 上的机器 T 上**有一个 ssh 帐号**,而另一台 Internet 上的机器 S 则是一台公共服务器,例如 Google 的服务器。如下图所示:
|
||
{{./ssh.png}}
|
||
ssh
|
||
首先来说 Local Forwarding ,相关的文档上都用的是阅读邮件这个经典的例子:用户在 T 上有一个 IMAP 服务器,本来他可以直接连接到 T:143 上阅读邮件的,但是这是未加密连接,如果你的 IMAP 服务器是用明文密码验证的方式的话,就很糟糕了,这样你的密码很容易被别人窃听到。这个时候 SSH 隧道就出来了,通过如下命令可以建立一个 SSH 隧道:
|
||
|
||
__ssh -L 9143:localhost:143 T__
|
||
|
||
#这里的local意思是,本地的ssh客服端程序把由本地127.0.0.1:9143端口发出的数据通过与T间的ssh加密隧道转发到远程的143端口,由于指定了localhost,故是T上的143端口。
|
||
|
||
首先不看参数,这个命令是发起一个 ssh 连接到主机 T ,然后选项 -L 表示 Local Forwarding ,**9143 表示本地(亦即 H)的端口,然后 localhost:143 则是远程(即 T)上所对应的主机和端口**。这样一来,你在 H 上连接端口 9143 ,就相当于你在 T 上连接 localhost:143 一样,SSH 建立的隧道会帮你在两个主机间传递信息。现在只要配置 H 上的邮件客户端去连接 localhost:9143 ,就能通过加密的方式访问到 T 上的 localhost:143 这个邮件服务器了。注意两个主机上的 localhost 的区别。
|
||
|
||
当然隧道并不限于 localhost(即远程的运行着sshd的服务器T) ,如果你愿意,可以做这样一个映射:
|
||
|
||
__ssh -L 9999:www.google.com:80 T__
|
||
#由于指定了www.google.com:,本地的ssh客户端进程会把发往本地127.0.0.1:9999端口的数据通过加密隧道传给T,然后T的sshd服务进程会把这个请求转发到www.google.com:80(sshd会根据传给他的应用层协议类型也就是目的端口号和IP或主机名来判断),并将返回的数据通过隧道传到H的127.0.0.1:9999端口。
|
||
|
||
这样,你可以在 H 的浏览器里输入 localhost:9999 访问到 Google 了。注意到 H 和 T 之间的数据传输是加密的,所以,如果 H 是处在伟大的局域网内,而 T 是在外部的话,可以通过这样的方法来访问 Google 而不受防火墙的限制。不过这种方法**每个端口只能映射一个网站**,很不方便。要用作代理通常还是使用 __dynamic application-level port__ forwarding 的方式,如下:
|
||
|
||
__ssh -D 9999 T__
|
||
|
||
#这时本地的ssh客户端进程担任了一个socks服务器的角色,他监听本地的127.0.0.1:9999端口,把从该端口发出的数据通过隧道传到T的sshd进程,然后sshd再根据请求数据中的信息(IP地址或主机名加端口号)发送相应的请求然后把返回的数据通过隧道传给客户端的127.0.0.1:9999
|
||
|
||
这样建立起来的隧道实际上是一个 __SOCKS 代理__,当然要使用 **SOCKS 代理需要客户端能够支持才行**,比如在 Firefox 中,只要在代理处填写 localhost:9999 并勾选 SOCKS5 代理即可。另外,还可以使用诸如 ProxyChains 之类的程序让本身不支持 SOCKS 代理的程序能够“透明地”使用 SOCKS 代理,不过我并没有尝试过,不知道稳定性如何。
|
||
|
||
最后要介绍的就是 Remote Forwarding 了,首先看命令吧:
|
||
|
||
__ssh -R 9923:10.13.21.88:23 T__
|
||
|
||
-R的意义是:在远程主机T上指定一个sshd监听的端口号如9923然后他向该端口发的数据会通过隧道被H的ssh客户端接收,然后ssh客户端会将请求数据转发给本地的主机和端口号。
|
||
|
||
这里 10.13.21.88 是 H 所在的局域网里的一台主机,23 是 telnet 默认的端口,其实这就是浙大飘渺水云间 bbs (亦即“88”)的校内地址了,由于是在局域网内,Internet 上的 T 是访问不到它的,现在 H 建立了一个到 T 的 Remote Forwarding ,对应之前的 Local Forwarding 的意思,应该能猜到了吧?现在在 T 上可以通过访问 localhost:9923 来上 88 了。 :D 换句话说,在 T 上访问 localhost:9923 相当于在 H 上访问 10.13.21.88:23 ,这成了一个反向的隧道。
|
||
综上,Local 和Remote都是相对与本地H的ssh客服端进程来说的,前者是将发往本地指定端口上的数据通过隧道发往sshd服务器,sshd再做处理。
|
||
后者是本地的ssh客户进程将远程sshd传过来的数据(sshd监听并转发特定端口)转发到相应的主机。
|
||
|
||
另外,上面三种 Forwarding 默认建立的 socket 都是监听本机的 **loopback** 地址的,这样外面的机器是不能访问这个端口映射的。对于最后那个飘渺水云间的隧道来讲,默认情况下,T 虽然可以通过 localhost:9923 来上 88 ,但是 S 却不能通过 T:9923 来访问到,而是会得到一个 Connection Refused 的错误,这样做也是为了安全起见,不过,如果确实想要让__这个隧道可以从其他主机访问到的话__,上面三个命令都可以在映射参数前面再加一个地址参数。例如飘渺水云间的那个映射其实等价于:
|
||
|
||
ssh -R localhost:9923:10.13.21.88:23 T
|
||
|
||
把 localhost 改成 * 就可以让其他机器也访问到了(注意加上引号以防止 shell 把星号给展开了):
|
||
|
||
__ssh -R '*:9923:10.13.21.88:23' T__
|
||
|
||
唔,还有一点要说明的是,对于 -L 和 -D 来说,都是这样就可以了,而 -R 则还需要远程的 sshd 配置一下__ GatewayPorts __选项,默认情况下是 no ,这样会无视掉客户端的请求,强制 bind 到 loopback 地址上,而 yes 则相当于强制 * ,如果设置成 clientspecified 则允许客户端来选择(注意,-R选项时的远程sshd监听的端口号是由ssh客户端在命令行上指定的)。因此在 /etc/ssh/sshd_config 文件里添加这样一行:
|
||
|
||
__GatewayPorts clientspecified__
|
||
|
||
再重启 ssh 服务即可。虽然学校提供了 RVPN 可以从校外访问到校内的资源,但是并没有提供 Linux 下的解决方案。在没有校外独立 IP 的情况下用这样的方法在外面访问学校的资源就变得非常有用了。要是 -D 也能有一个对应的 Reverse 的方案也许会更方便一些,不过也许可以用 -R 和 -D 桥接一个:
|
||
|
||
H$ ssh -D 9999 localhost
|
||
H$ ssh -R 9999:localhost:9999 T
|
||
|
||
在 T 上使用 localhost:9999 作为 SOCKS 代理,而该端口的数据会被 forward 到 H 上的 localhost:9999 ,那里是实际的 SOCKS 代理服务器。不过我没有实际测试过是不是真的可以用。 :p
|
||
|
||
最后再说一句,**FTP 是另一个经典的明文传输密码的协议,但是它却没法通过 SSH 隧道使用,因为它需要另外打开一个 socket 来传输数据**,这时就不在 SSH 的接管范围之内了,我想这也许是 SFTP 这个东西诞生的直接原因吧。其实使用了 -R 的端口映射之后,要 scp 回来是一件很容易的事,例如:
|
||
|
||
H$ ssh -R 9922:localhost:22 T
|
||
T$ scp foo.txt -P 9922 localhost:~/
|
||
|
||
可以将 T 上的 foo.txt 拷贝回 H 的主目录中。当然,如果只是为了做一个端口映射,可不必打开一个远程 shell ,只要再为 ssh 加上 -Nf 参数即可,具体是什么意思自己 man 吧。它会在后台运行,一直等待到对应隧道端口的连接。需要注意的是,在有了第一个连接之后,如果连接数降为零了,它会自动退出。所以我通常还是会打开远程 shell ,不过使用 screen 把它放在后台去,就不会妨碍了,需要的时候可以调出来,也可以方便地控制它什么时候退出。 :)
|
||
|
||
最后,在客户端的 ~/.ssh/config 里加入
|
||
|
||
ServerAliveInterval 180
|
||
|
||
这样可以防止长时间的 idle 导致 ssh 连接被自动断开。
|
||
|
||
不知道 iptables ,做端口转发?SSH 主要是做隧道了,要两台机器参与,端口转发的话是同一台机器双 IP 的情况吧?
|
||
|
||
关于用reversed tunneling做rvpn
|
||
往往情况是T也不是公网ip 例如在firewall后面
|
||
那H就不能ssh T了
|
||
|
||
结果是需要一个middleman,H,T都能访问到 那可以连上
|
||
可是谁能提供一个。。。
|
||
|