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.com127.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.45192.168.1.222 端口发送到本地的 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)

image.png

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 sshESCAPE CHARACTERS

假设已经在一个 ssh 连接中了,这时如果需要新增一个端口转发,可以通过 Escape Character 提供的命令进行操作

~# 查看当前存在的连接(已经连接的才算,只是开了通道但是没有连接不算)

~C 打开 ssh 自身的命令行,在这里可以添加或者取消转发

例如(第三次什么也没输入,多按了一个回车。二四次是 ~C 进行的操作,进入 ssh shell

image.png

完整 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.
如果觉得我的文章对你有用,请随意赞赏