相关参数

-C  压缩数据传输
-f  ssh 连接成功后,转入后台运行(关闭连接时可以使用 kill )
-N  不打开远程 shell
-T  不为这个连接分配 tty(与 -N 一起使用,表示这个 ssh 只用来传递数据,不执行远程操作)
-g  允许打开的端口让其他主机访问(见本地转发)
-L  本地端口转发(local)
-R  远程端口转发(remote)
-D  动态转发(dynamic)
-p  指定 ssh 端口

Local Forwarding

将本地主机某个端口流量转发到远程主机的指定端口

host1(local-host)<----->host2(middle-host)<----->host3(target-host)

host1 是本地主机,host3 是远程主机(host1 与 host3 无法连通),host2 可以同时连接 host1 和 host3;通过 host2,连接 host1 和 host3。

  • host1: local-host
  • host2: middle-host
  • host3: target-host

在 local-host 上执行:

$ ssh -L [bind_local_address]:<local-host-port>:<target-host>:<target-host-port> <middle-host>

指定本地 ssh client 监听 local-host-port,通过 middle-host 的 ssh server 将所有数据,转发到 target-host 的 target-host-port

比如在 host1 上执行:

$ ssh -L 1080:host3:21 host2

$ ftp localhost:1080

当使用 ftp 请求 localhost:1080 时,请求内容会通过 host2 转发给 host3:21(21为ftp默认端口),也就是说,使用 ftp 访问 localhost:1080 就等同与访问 host3:21。对应的连接关系如下:

host1(local-host)        host2(middle-host)       host3(target-host)
1080 - ssh client <----->   ssh server    <-----> 21

在选择端口号时要注意非管理员帐号是无权绑定 1-1023 端口的,所以一般是选用一个 1024-65535 之间的并且尚未使用的端口号即可

示例:反向代理

服务内网中有 2 台机器:

  1. host3:只能内外访问,运行 tcp 服务,端口 5432
  2. host2:和 host3 在同一内网下,同时有自己的外网IP

内网外有一台机器:

  1. host1:host1 与 host3, host2 不在同一内网中,host1 上的应用需要访问 host3 上的服务。

方法1:在 host2 上执行(必须加 -g 参数才能使 ssh client 监听的端口可以被其他主机访问):

$ ssh -C -f -N -g -L 5432:host3:5432 root@host2

这时可以在 host1 上访问 host2:5432。对应连接关系如下:

host1(local-host) <--              host2(middle-host)                 host3(target-host)
                     \--->  5432 - ssh client <--> ssh server <-----> 5432

方法2:在 host1 上执行:

$ ssh -C -f -N -L 5432:host3:5432 root@host2

这时可以在 host1 上访问 localhost:5432。对应连接关系如下:

host1(local-host)        host2(middle-host)       host3(target-host)
5432 - ssh client <----->   ssh server    <-----> 5432

Remote Forwarding

host1(remote-host)<------host2(local-host)<----->host3(target-host)

当 host1 无法连接 host2 和 host3,而 host2 可以连接 host1 与 host3 时,可以在 host2 上使用远程端口转发来完成 host1 和 host3 的连接。

  • host1: remote-host
  • host2: local-host
  • host3: target-host

在 local-host 上执行:

$ ssh -R <remote-host-port>:<target-host>:<target-host-port> <remote-host>

使 remote-host 监听 remote-host-port 端口,然后将所有数据经由 local-host 转发到 target-host 的 target-host-port

比如在 host2上执行:

$ ssh -R 1080:host3:21 host1

在 host1 上执行:

$ ftp localhost:1080

在 host1 上使用 ftp 请求 localhost:1080 时,请求内容会通过 host2 转发到 host3:21

对应连接关系:

host1(remote-host)        host2(local-host)       host3(target-host)
1080 - ssh server <----->   ssh client    <-----> 21

示例:内网穿透

访问内网中 windows 远程桌面:

  1. 内网中 windows 主机开启远程桌面访问,端口 3389
  2. 有 ssh 登录权限的一台拥有外网 IP 的主机,外网IP 152.32.253.99
  3. 在内网 windows 主机上执行:

     $ ssh -R 3389:127.0.0.1:3389 ubuntu@152.32.253.99
    
  4. 其他机器可以通过 152.32.253.99:3389 连接远程桌面

对应的连接关系如下:

    other host <---     ubuntu@152.32.253.99               windows
                   \-->  3389 - ssh server   <------> ssh client - 3389

此时 local-host 与 target-host 是同一台机器,内网中的 windows

ssh 远程转发默认只能绑定远程主机的本地端口,即127.0.0.1,如果想监听来自所有主机的连接,需要修改远程主机的 /etc/ssh/sshd_config 配置文件,将 GatewayPorts 项的对应值改为 yes,重启 ssh 后生效。

动态转发

动态转发不指定目标 host 与目标端口:

$ ssh -D <local-port> <middle-host>

比如:

$ ssh -D 1080 middle-host

指定本地 ssh client 监听 1080 端口,将本地 1080 端口的所有请求通过 middle-host 的 ssh server 发送到目标机器端口,之后我们可以使用 localhost:1080 作为正常的 socks5 代理来使用

保持持久连接

client 端配置 /etc/ssh/ssh_config 修改以下内容:

Host *
    # tcp 长连接
    TCPKeepAlive yes
    # 客户端每隔 60 秒向服务端发送空数据
    ServerAliveInterval 30

server 端配置 /etc/ssh/sshd_config 修改以下内容:

# 服务端每隔 60 秒向客户端发送一个空数据包
ClientAliveInterval 60
# 服务端最多发送 720 次,60 * 720 秒 = 12 小时
ClientAliveCountMax 1

参考

[SSH 端口转发教程 三点水](https://lotabout.me/2019/SSH-Port-Forwarding/)