SSH 端口转发功能可以将其余 TCP 端口的网络数据经过 SSH 连接来转发,而且自动提供了相应的加密及解密服务。其实这一技术就是咱们经常据说的隧道(tunnel)技术,缘由是 SSH 为其余 TCP 连接提供了一个安全的通道来进行传输。
咱们知道,FTP 协议是以明文来传递数据的。可是咱们可让 FTP 客户端和服务器经过 SSH 隧道传输数据,从而实现安全的 FTP 数据传输。
更常见的状况是咱们的应用常常被各类防火墙限制。常见的有禁止访问某些网站、禁用某类软件,同时你的全部网络行为都被监控并分析!一样的经过 SSH 隧道技术咱们彻底能够规避这些限制。web
如上图所示,经过 SSH 的端口转发, 应用程序的客户端和应用程序的服务器端再也不直接通信,而是转发到了 SSH 客户端及 SSH 服务端来通信。这样就能够同时实现两个目的:数据的加密传输和穿透防火墙!
在具体的使用场景中,端口转发又被细分为本地端口转发、远程端口转发、动态端口转发等。本文将详细的介绍其技术原理及使用方法。浏览器
假设咱们有一台主机 B,上面运行着 smtp 服务器,监听的端口号为 25,可是只监听了 localhost 网络接口。也就是说只有运行在主机 B 上的邮件客户端才能与 smtp 服务器创建链接。此时另一台主机 A 上的邮件客户端若是想要经过主机 B 上的 smtp 服务器收发邮件该怎么设置呢?经过 SSH 的本地端口转发功能能够轻松的搞定这样的场景!
假设两台主机上都安装了 SSH,咱们可使用主机 A 上的 SSH 客户端向主机 B 上的 SSH 服务器发起请求,创建一条执行端口转发的隧道:安全
$ ssh -L 10025:localhost:25 HostB
此命令的运行原理以下图所示(此图来自互联网):服务器
运行上面的命令后,SSH 客户端程序在主机 A 上监听了 localhost:10025(你能够用 1024 - 65535 之间的任意端口代替 10025,只要不与已有端口冲突就行)。全部在主机 A 上发往 10025 端口的消息都会经过 SSH 隧道转发到主机 B 上的 25 端口。接下来须要配置主机 A 上的邮件客户端程序,让它把消息发送到 localhost:10025。完成以后主机 A 上的邮件客户端就能够经过主机 B 上的 smtp 服务器收发邮件了。具体的数据包的流向为:网络
1 邮件客户端把数据包发送到 localhost(主机 A) 的 10025 端口
2 SSH 客户端把数据包加密并从主机 A 发送到主机 B 的 SSH 服务器
3 SSH 服务器把数据包解密并发送到 localhost(主机 B) 的 25 端口
从 smtp 服务器返回的数据包则是沿着原路返回以完成数据的双向传递。并发
至此咱们已经完成了一个最基本的本地端口转发 demo 的介绍。接下来让咱们来聊一下究竟什么叫本地端口转发?
在上面的 demo 中咱们注意到一共有两对的客户端与服务器程序,分别是 smtp 应用的客户端和服务器与 SSH 的客户端和服务器。若是应用程序的客户端和 SSH 的客户端位于 SSH 隧道的同一侧,而应用程序的服务器和 SSH 服务器位于 SSH 隧道的另外一侧,那么这种端口转发类型就是本地端口转发。须要使用 -L 选项来建立。ssh
前面的 demo 中应用程序的客户端和 SSH 客户端位于同一台主机上,应用程序的服务器端和 SSH 的服务器端也位于同一台主机上,真实的状况每每不是这样的:网站
上图中的场景可能更符合真实状况(此图来自互联网)。应用程序的客户端和 SSH 客户端分别位于 SSH 隧道同一侧的两台不一样的主机上,而应用的服务器端和 SSH 服务器分别位于 SSH 隧道另外一侧的两台不一样的主机上。此时咱们须要使用下面的命令:ui
$ ssh -g -L P:HostS:W HostB
应用 -g 选项后主机 A 不只会监听 localhost 的 P 端口,还可以监听全部网络接口的 P 端口,因此主机 C 上的应用客户端就能够把消息发送到主机 A 的 P 端口。
接下来咱们必需要介绍本地端口转发的命令格式了:加密
ssh -L <local port>:<remote host>:<remote port> <SSH server host>
SSH server host 是 SSH 服务器所在的主机, remote host 和 remote port 则分别指应用程序服务器所在主机和监听端口。若是 remote host 指定为 localhost 则认为应用程序服务器和 SSH 服务器在同一台主机上。
在结束本地端口转发以前还须要介绍另外两个选项,它们是 f 和 N。上面的命令在建立隧道的同时登陆到远程主机,通常状况下咱们不须要这个登陆。何况一旦这个登陆退出,隧道也会随之关闭。咱们更指望的是可以建立在后台运行的隧道,这时就须要添加 f 和 N 选项。
咱们必须区别远程端口转发和本地端口转发,由于它们对应了不一样的应用场景,固然使用的命令行选项也是不同的。若是应用程序的客户端和 SSH 的服务器位于 SSH 隧道的同一侧,而应用程序的服务器和 SSH 的客户端位于 SSH 隧道的另外一侧,那么这种端口转发类型就是远程端口转发。远程端口转发的结构以下图所示(此图来自互联网):
因此,区分本地端口转发和远程端口转发主要是看 SSH 客户端与应用程序的哪一部分在 SSH 隧道的同一侧!远程端口转发的命令格式为:
ssh -R <local port>:<remote host>:<remote port> <SSH server host>
其它的细节二者基本也是同样的。可是远程端口转发不支持 -g 参数,这让咱们很难实现相似下面的用例:
内网中主机 A 上运行 Jenkins 服务器监听本机 8080 端口,并运行 SSH 客户端。
外网中的主机 B 上运行 SSH 服务器。
但愿能够经过远程端口转发的方式在主机 A 和 B 之间创建隧道,
而后外网的 Bitbucket 等代码管理服务能够经过 Webhook 的方式访问主机 B 从而触发 Jenkins 服务器中的 Build。
这个问题的根源在于咱们执行下面的远程端口转发命令后:
$ ssh -R 18080:localhost:8080 HostB
主机 B 只能监听 localhost 的 18080 端口:
如何让 HostB 监听本机全部网络接口的 18080 端口呢? 须要经过修改 SSH 服务器的配置来实现这个功能!在 SSH 服务器的配置文件 /etc/ssh/sshd_config 中添加一行:
GatewayPorts yes
保存后重启 SSH 服务器,而后从新创建隧道:
此时主机 B 已经能够接受外部 webhook 的调用了。
相对于动态端口转发,前面介绍的端口转发类型都叫静态端口转发。所谓的 "静态" 是指应用程序服务器端的 IP 地址和监听的端口是固定的。试想另一类应用场景:设置浏览器经过端口转发访问不一样网络中的网站(好比在家里链接公司内网中的站点,哈哈)。这类应用的特色是目标服务器的 IP 和端口是未知的而且老是在变化,建立端口转发时不可能知道这些信息。只有在发送 HTTP 请求时才能肯定目标服务器的 IP 和端口。在这种场景下静态端口转发的方式是搞不定的,于是须要一种专门的端口转发方式支持即 "动态端口转发"。SSH 动态端口转发是经过 Socks 协议实现的,建立动态端口转发时 SSH 服务器就相似一个 Socks 代理服务器,因此这种转发方式也叫 Socks 转发。
动态端口转发的命令格式为:
$ ssh -D <local port> <SSH Server Host>
例如:
$ ssh -D 11080 nick@xxx.xxx.xxx.xxx
注意,命令中不须要指定目标服务器和端口号。执行上面的命令后 SSH 客户端就开始监听本机 localhost 的 11080 端口。你能够把本机上浏览器网络配置中的 Socks 服务器指定为 localhost:11080。而后浏览器中的请求会被转发到 SSH 服务器端,并从SSH 服务器端与目标站点创建链接进行通讯。
SSH 端口转发是一项很是实用的技术,灵活的使用它不只能够解决工程项目中繁杂的网络问题,还可以给咱们的生活添加乐趣!