ssh 端口转发相关说明
ssh 本身除了 shell (命令行)和作为外壳(SCP/SFTP)等,自己还带了诸如端口转发、SOCKS 代理等多种功能。
常见的有如下三种方式
- Local Port Forwarding(-L)
- Remote Port Forwarding(-R)
- Dynamic Port Forwarding(Socks5 Proxy)(-D)
区别的话,Local 和 Remote 是转发某个端口,其中,Local 模式最终是在本地开放监听,而 Remote 是在远程开放监听(这样要好记,即 Local 从远程发到本地,而 Remote 从本地发到远程)
SSH 端口转发
Local Port Forwarding
远程的端口发到本地(实际开的端口是本地端口)
常用的形式是 ssh -L [bind_host:]bind_port:remote_host:remote_port user@host
,例如 ssh -L 2222:127.0.0.1:22 user@example.com
,将 example.com
的 127.0.0.1:22
转发到本地的 2222
端口
bind
部分是相对于本机说的,即绑定的 IP 和端口,可以指定一个 IP 或者是使用0.0.0.0
或*
表示全部接口。如果不提供则根据本地config
内的GatewayPorts
进行设定,默认是只限定127.0.0.1
访问而
remote
部分相对的是远程主机,即比如127.0.0.1
是远程的127.0.0.1
,也就是是后面登录的服务器
常见的需求比如
- 在 ssh 下工作,但是需要本地连接远程服务器特定端口进行调试
- 只开放了 ssh,但是需要其他协议连接(比如 VNC/RDP/其他本地服务如 MySQL)
- 远程能到达但是本地不能到达的网络
对于场景一
服务器开启一个服务器,例如 python -m http.server 8000
即可开启一个监听在 0.0.0.0
和 ::1
的 HTTP 服务器
假定这个不能直接访问,那么可以通过 ssh -L 88:127.0.0.1:8000 user@xxx.com
将远程的 8000 端口转发到本地。这样通过 127.0.0.1:88
就可以访问了
对于场景二
例如只放行了 SSH,而远程是 Windows 主机(Windows 10/11 内置了可选功能 Openssh-server)
那么就可以 ssh -L 3390:127.0.0.1:3389 user@123.123.123.123
将远程的 3389
端口开放到本地的 3390
当然,这里还有一种方法,使用 127.0.0.1/8
均被划分成了本地回环的特性
ssh -L 127.0.0.2:3389:127.0.0.1:3389 user@123.123.123.123
将远程的 3389 端口绑定到本地的 127.0.0.2:3389
端口,这样就可以直接通过 127.0.0.2
进行访问了(SMB 协议,即 Windows 自带的文件共享似乎不支持这么做。折腾黑群晖的经验)
对于场景三
比如一个很经典的校园网络,172.17.0.0/16
是互通的校园网环境
而你在寝室有一个路由器,其 IP 是 172.17.23.45
,是将 ssh 暴露在校内的(即 WAN 口允许 ssh 登录)
路由器使用的网段是 192.168.1.1/24
,其下有一个设备是 192.168.1.2
这时,可以通过 ssh -L 2222:192.168.1.2:22 root@172.17.23.45
将 192.168.1.2
的 22
端口发送到本地的 2222
端口
Remote Port Forwarding
本地的端口发往远程(实际上是在远程开的端口)
常见的形式是 ssh -R [bind_addr:]bind_port:local_addr:local_port user@host
bind
部分是相对于远程主机说的,默认只允许127.0.0.1
访问,可以通过0.0.0.0
或*
指定任意 IP(但是这样需要首先在config
内修改GatewayPorts
改为yes
,否则即使指定也仅本机访问)而
local
部分则是本地主机为基准的地址
常见场景如
- frp (但是建议临时使用,长时间仍然更推荐专门的 frp 等工具)
- 端口穿透(类似 frp,不过我想的是应用在路由器的。没试过能不能这么干,主要取决于 openwrt 自带的 dropbear 是否支持了)
- shell 反弹(也不完全是标准的 shell 反弹(即 stdio - netio),比如想象一个场景,你同学在配置 ubuntu 出了问题想你求助,你想帮他操作,但是没有办法访问他的设备,在有 ssh 的情况下,可以将 22 端口临时开到某个共哦
三个场景实际类似,以场景三为例 ssh -R 2222:127.0.0.1:22 user@host
,将本地的 22
端口开放到远程的 2222
端口
之后取决于 GatewayPorts
设置,是本地或者公网访问了
Dynamic Port Forwarding(SOCKS5 Proxy)
本质上是一个 SOCKS5 代理
使用 SOCKS5 代理协议,发往某个主机的数据,会被代理到远程发送,应该也算是一种动态的端口转发了吧
使用很简单,ssh -D [addr:]port user@host
即可。在 [addr:]port
上监听一个 SOCKS5 代理,数据发往远端
常见场景
- openwrt 访问内网
临时梯子
因为和任何代理是一样的,发往代理的流量会发送到代理服务器后发出,因此用这个方式可以直接到达本不能到达的地方
例如 Local Port Forwarding
内场景三,如果需要访问部署在局域网内的服务(尤其是 HTTP 服务)
可以直接 ssh -D 1080 root@172.17.23.45
,然后浏览器配置 SOCKS5 代理,且不忽略局域网地址(即局域网地址,如 192.168.1.1/24
同样走代理)的情况下,可以直接通过例如 192.168.1.1
访问路由器,或者其他地址访问局域网内的 web 服务(我很长一段时间就是这么干的)
(利用这个特性,同样可以比如作为临时的梯子使用)
题外话
虽然确实套了一层壳,有一层加密,但是过墙的有效性以及安全性(指被 ban IP)暂持怀疑态度,这种方式临时用用就行了,不建议作为主要方式使用,毕竟挂一个服务器成本还是挺高的
因为有一次我去 Google
ssh -D 流量特征
,出来的第一篇文章是 方滨兴:如何从SSH加密流量中区分出ssh,scp,sftp,tunnel等流量 | 藤桥 (free-hoster.net)
ssh 跳板
首先,三种转发方式,在应用支持的情况下,都是可以作为广义的跳板的(即通过某个 ssh 连接到一个无法到达的 ssh 服务器)
假设自己是 A,能到达某设备 B,B 后面有一个 C 无法从 A 直接访问
比如利用 ssh -L
利用 B 能访问 C 的特性,将 C 的 ssh 端口转发到自己的某个端口,然后自己直接连接到该端口
或者,利用 ssh -R
,让 C 在 B 上开一个端口,之后自己连接到 B
再或者,ssh -D
直接形成 socks5 代理,之后通过代理进行 ssh
当然除此之外,ssh 本身是提供了跳板的功能的。比如
ssh zsh2517@192.168.9.11 -J root@172.20.3.3,root@192.168.9.1
通过 172.20.3.3
的路由器和 192.168.9.1
的路由器,连接到最后一跳(192.168.9.1
)到达的 192.168.9.11
上
使用 -J
进行跳板的时候,仍然可以加 -L/-R/-D
的端口转发。这时仍然是本机和登录上去的远程服务器
动态的开放端口转发
详见 man ssh
的 ESCAPE CHARACTERS
节
假设已经在一个 ssh 连接中了,这时如果需要新增一个端口转发,可以通过 Escape Character
提供的命令进行操作
~#
查看当前存在的连接(已经连接的才算,只是开了通道但是没有连接不算)
~C
打开 ssh 自身的命令行,在这里可以添加或者取消转发
例如(第三次什么也没输入,多按了一个回车。二四次是 ~C
进行的操作,进入 ssh shell
)
完整 man ssh
文档
ESCAPE CHARACTERS
When a pseudo-terminal has been requested, ssh supports a number of functions through the use of an escape char‐
acter.
A single tilde character can be sent as ~~ or by following the tilde by a character other than those described
below. The escape character must always follow a newline to be interpreted as special. The escape character
can be changed in configuration files using the EscapeChar configuration directive or on the command line by the
-e option.
The supported escapes (assuming the default ‘~’) are:
~. Disconnect.
~^Z Background ssh.
~# List forwarded connections.
~& Background ssh at logout when waiting for forwarded connection / X11 sessions to terminate.
~? Display a list of escape characters.
~B Send a BREAK to the remote system (only useful if the peer supports it).
~C Open command line. Currently this allows the addition of port forwardings using the -L, -R and -D op‐
tions (see above). It also allows the cancellation of existing port-forwardings with
-KL[bind_address:]port for local, -KR[bind_address:]port for remote and -KD[bind_address:]port for dy‐
namic port-forwardings. !command allows the user to execute a local command if the PermitLocalCommand
option is enabled in ssh_config(5). Basic help is available, using the -h option.
~R Request rekeying of the connection (only useful if the peer supports it).
~V Decrease the verbosity (LogLevel) when errors are being written to stderr.
~v Increase the verbosity (LogLevel) when errors are being written to stderr.