SSH 代理设置

2016-07-26 linux security ssh

通过 ssh 的端口转发或者隧道 (tunneling) 功能,可以实现: A) 加密 SSH Client 端至 SSH Server 端之间的通讯数据;B) 突破防火墙的限制完成一些之前无法建立的 TCP 连接。

简介

首先看下,对于转发有些比较有用的参数:

  • -N 告诉 SSH 客户端,这个连接不需要执行任何命令,也就是说不需要打开远程 shell,仅仅做端口转发;
  • -T 不为这个连接分配 TTY,通过 -N -T 两个参数表示这个 SSH 连接只用来传数据,不执行远程操作;
  • -f 告诉SSH客户端在后台运行,要关闭这个后台连接,就只有用 kill 命令去杀掉进程;
  • -L 做本地端口转发,需要注意被冒号分割的三个部分含义,下面做详细介绍;
  • -R 做远程端口转发,与本地端口转发类似,同样需要注意被冒号分割的三个部分含义;
  • -g (GatewayPorts) 默认只转发本地发送的数据,如果要转发其它服务器的客户端请求,则需要添加该参数。
  • -C 压缩数据传输;

注意,使用时需要修改服务端的如下配置。

AllowTcpForwarding yes
AllowAgentForwarding no
GatewayPorts yes

接下来,看看具体的使用场景,以及配置方式。

本地转发

在本地启动一个监听端口,将访问该端口的请求通过 SSH 转发到服务端可以访问的服务,命令参数如下。

----- 本地转发方式的参数设置
$ ssh -N -f -L [local port]:[remote service host]:[remote service port] [user@remote ssh host]

相当于利用了 SSH 加密通道,将请求从客户端发送到了服务端的服务上。

转发到本地服务

ssh local port forwarding local service

假设在服务器上部署了 MySQL 服务 (也包括 SSH 服务),为了防止被攻击,通常在进行防火墙配置时,会尽可能减小打开的端口,例如只开启 80/443 服务端口,因此如果需要访问 mysql(3306) 服务只能从本地访问,此时就可以使用本地转发功能。

可以执行如下命令,然后访问本地的端口 7000 即可。

----- 本地执行如下命令,然后访问localhost:7000即可
$ ssh -N -f -L 7000:localhost:3306 10.12.34.100

----- 访问远程的MySQL服务
$ mysql -P7000 -hlocalhost

注意,localhost 表示的是部署 SSH 所在机器访问 MySQL 时的地址;另外,因为非管理员只能使用 1024~65535 的端口,在此选择 7000 端口。

数据在传输时将会通过如下的四步:A) 将数据发送到本地的 7000 端口;B) 本地的 SSH Client 将 7000 收到的数据加密后发送到 SSHD 端,也就是 MySQL Server 所部署的机器;C) SSHD 端收到数据后解密,并发送给本地的 3306 端口;D) 从 MySQL Server 上返回的数据按照原路返回。

转发到其它服务器

ssh local port forwarding remote service

另外一种场景如下,A 尝试访问 D 提供的 MySQL 服务,但是由于防火墙导致不能直接访问,也就是说 ABD 链路不通。但是 ABC 的 SSH 链路是通的,此时就可以利用隧道技术建立 ABCD 链路。

也就是说,中间服务器 C 部署着 sshd 服务,用于建立 ssh 连接;D 的 3306 端口提供 MySQL 服务,然后就可以通过如下方式建立隧道。

----- 本地执行如下命令,然后访问localhost:7000即可
$ ssh -N -f -L 7000:10.12.34.136:3306 10.12.34.100

----- 访问远程的MySQL服务
$ mysql -P7000 -hlocalhost

可以看到,上述两种唯一的区别在于中间的地址,只要 C 机器能够访问的服务,都可以通过这种方式转发。

总结

本地转发就是在客户端监听本地端口,然后将请求通过 SSH 发送到 SSHD 所能链接的服务端,转发过程是透明的,SSH 不会关注具体的协议内容。

如果本地开启了 GatewayPorts 选项,那么只要可以链接到 10.35.0.125 的机器都可以访问 7000/2121 端口的服务,也就是远端的 MySQL/FTP 服务了。

远程转发

ssh remote port forwarding

如上述的第二个场景,CD 通常是在一个内网,但是此时 C 没有公网 IP,但是 A 是有的,那么就意味着,通过 CBA 可以建立链接,但是 ABC 将无法建立链接,如果要在 A 访问 D 提供的 MySQL 服务,那么就需要远程转发了。

也就是利用一条已建立好的 CBA 方向的连接,来完成 ABCD 方向的访问,也就是先从 C 建立到 A 的链接,然后在 A 启动一个监听端口,这样只要能链接到 A 的机器就可以访问 D 提供的服务。

----- 参数列表,需要在提供服务的机器上执行ssh登录
$ ssh -R [remote port]:[local host]:[local port] [SSH hostname]

----- 在C上执行如下命令,与A建立SSH转发服务
$ ssh -N -f -R 7000:10.12.34.136:3306 10.35.0.125

----- 在A上通过7000端口访问D的MySQL服务
$ ssh -p 7000 localhost

这里是在 C 建立与 A 的 SSH 链接,并在 A 监听 7000 端口,对于 A 来说,C 是一台远程主机,所以这种情况就被称为 “远程端口绑定” 。

两者比较容易混淆,可以理解为,如果 SSH 服务器和应用都在同一端,则是本地转发,否则是远程转发。

动态转发

也就是通过 ssh 在本地建立一个 socks 代理服务,所有的本地网络访问都会通过该端口,然后转发到服务器,而应用程序决定使用那个端口。

可以用作隧道代理,其大致的工作如下:

tunneling sock

整个流程如下:

  1. 墙内的客户机跟墙外的代理服务器,建立好 SSH 连接,并设定动态绑定;
  2. 此时墙内客户机上的 SSH 会监听本地的一个端口 7001
  3. 访问 www.google.com 会将请求发送到 7001 端口,SSH 将此请求通过 SSH 加密连接发送到墙外服务器的 SSH 上;
  4. 由于建立的时候是动态绑定,服务器会将相应的请求发送到 www.google.com80 端口,并在收到回复后,原路返回给客户机的 SSH,客户机的 SSH 返回给应用程序。

所以在上述的模型中,客户机的 SSH 实际上就是实现了一个 SOCKS 代理的角色,这个 SOCKS 代理侦听了 7001 端口,并将所有的请求都代理给服务器的 SSH,并利用 SSH 动态绑定,让服务器进一步转发请求。

当然,使用时,需要在浏览器或者其他应用程序上设置 SOCKS 代理,实际设置 SOCKS-v4 就可以,而 SOCKS-v5 仅仅增加了鉴权功能,代理指向 127.1:7001 即可。

$ ssh -D [local port] [SSH Server]

$ ssh -N -f -D 1080 foobar@123.123.123.123          # 将端口绑定在127.0.0.1上
$ ssh -N -f -D 0.0.0.0:1080 foobar@123.123.123.123         # 将端口绑定在0.0.0.0上

上述的命令,建立一个通过 SOCK5 协议的 123.123.123.123 的 SOCKS 服务器。