SSH端口转发命令的理解

以以下字符画情景为例, AB/BC两对电脑互相连接而AC电脑之间没有连接.

                    +--------------------+                          +-------------------------------------------+
                    |                    |                          |                                           |
                    |                    |                          |                                           |
                    | +----------------+ |                          |                                           |
                    | |                | |                          |                                           |
                    | +----------------+ |                          |                                           |
                    |                    |     ping IPA             |                                           |
                    |                    |   +---------------->     |                                           |
                    |    +----+          |                          |                                           |
                    |    |    |          |   <----------------+     |                                           |
                    |    |    |          |          ping IPB       XX-------------------------------------------X
                    |    |    |          |                         X                                           X
                    |    |    |          |                        XX                                         XX
                    |    |    |          |                      XX                                           X
                    |    |    |          |                     XX                                           XX
                    |    |    |          |                    XX                                           XX
                    |    |    |          |                   XX                                           XX
                    |    |    |          |                  XX                                          XXX
                    |    |    |          |                  +------------------------------------------+X
                    |    |    |          |
                    |    +----+          |                    电脑A                 +
                    |                    | 电脑B              IPA                   |
                    |                    | IPB                                  XX  |     XX
                    +--------------------+                                        X |  X
                                                                                   XXX   电脑A与C之间不连接, 无法互ping
                                 ^ +                                              XX|X
                        ping IPB | | ping IPC                                   XXX |  X
                                 | |                                           XX   |    XX
                                 + v                                                +

                   +--------------------------------------------------------------------+
                   |      ||              电脑C                                  ||     |
                   |      ||              IPC                                    ||     |
                   |      ||          +-------------------------------+          ||     |
                   |      ||          +-------------------------------+          ||     |
                   |      ||                                                     ||     |
                   |      ||                                                     ||     |
                   +--------------------------------------------------------------------+

静态转发

  • A为了让A能够连上C, 以B为跳板将C的端口转发至A, 在A中执行:
ssh -CN -L A的端口(portA):C的IP(IPC):C的端口(portC) B的用户名@B的IP(IPB) &

随后A访问自己的portA, 即可发现获取到了访问C的portC才能得到的数据. 换句话说, A在访问自己的portA时, 实际上将访问信息给了B, 请B代为访问C的portC端口并将结果返回给A的portA端口.

其中A的portA与B(的ssh端口)之间的数据传输被ssh加密, 但是B与C的portC之间的数据传输没有. 如果希望B与C的portC之间的数据传输也被加密, 则需要将命令改成:

# 在B中运行
ssh -CN -L B的临时端口(portB):C的本地IP(localhost):C的端口(portC) C的用户名@C的IP(IPC) &

# 在A中运行
ssh -CN -L A的端口(portA):B的本地IP(localhost):B的临时端口(portB) B的用户名@B的IP(IPB) &

如果有一台只与A连接的电脑D想与C连接, 可以使用参数-g允许D连接A的端口portA, 即ssh -CNg -L …. 当然D与A的portA的连接没有加密, 如果需要加密, 那还得在D中运行ssh -CN -L D的端口(portD):A的本地IP(localhost):B的端口(portA) A的用户名@A的IP(IPA) &

如果网速够快, 可以不用对数据进行压缩, 即不需要-C参数, 将命令变为ssh -N -L ….

  • B为了让A连上C, 以B为跳板将C的端口转发至A, 在B中执行:
ssh -CN -R A的端口(portA):C的IP(IPC):C的端口(portC) A的用户名@A的IP(IPA) &

可以发现不管是-L还是-R类型的转发, 后面接的都是目标机端口:源机IP:源机端口 需认证机用户名@IP. 需认证机有两类, 一类是跳板机, 一类是目标机.

-L类转发是在目标机上执行的命令, 因此需要认证跳板机; -R类转发是在跳板机上执行的命令, 因此需要认证目标机.

动态转发

在上述静态转发中, 每次A访问localhost:portA(本机portA端口)时都只能到达C的端口portC. 内不能不指定目标IP与端口从而让A访问到B能够访问到的所有资源? 换句话说让B作为A的网络代理并且A/B之间的通讯为加密通讯. 命令见下:

ssh -CN -D A的端口(portA) B的用户名:B的IP(IPB) &

这样能够创建一个A/B之间的socks v5隧道. 在支持socks v5网络代理的软件(比如火狐浏览器)中就能够通过配置A的网络代理为socks://localhost:portA实现B所在的网络的访问.

应用举例

加密数据连接

命令

我的Rstudio server是通过http来连接服务器的, 传输过程是明文, 容易被人监听, 而申请SSL/TLS证书”又贵又繁琐”, 故考虑使用SSH转发目标端口到本地后使用.

ssh -N -L 本地端口:localhost:服务器上的目标端口 服务器用户名@服务器IP

随后在浏览器里面打开http://localhost:本地端口即可, Firefox也不会总是跳出来连接不安全的提示了.

顺便可以用这种方法加密一下VNC远程桌面的连接.

脚本

对于GNU/Linux我写了一个脚本来方便进行ssh端口转发. 建议将这个脚本放在/usr/local/bin/ssh-forward.

#! /bin/zsh
REMOTEPORT=`echo $1 | cut -d':' -f2`
if [ -z ${REMOTEPORT} ]; then
    echo "Usage: ssh-forward username@remote-IP:remote-port local-port"
    return 1
fi
LOCALPORT=$2
if [ -z ${LOCALPORT} ]; then
    LOCALPORT=${REMOTEPORT}
    echo "Warning: local-port is omitted and remote-port is used as local-port"
fi
ssh -N -L ${LOCALPORT}:localhost:${REMOTEPORT} `echo $1 | cut -d':' -f1`

内网穿透

我们连接的网络大多不是真正的公网, 而是经过NAT层层转发连接起来的内网. 所以我们能够访问公网上的网站, 公网上的主机却无法访问我们自己.

假设A处在公网中国, B在NAT后面的内网中, B能够访问A, A无法访问B. 通过-R类型的转发可以让A访问到B或者与B连接互ping的主机.

如果有一台公网的GNU/Linux或者BSD服务器, 可以用下述命令实现穿透.

ssh -CNg -R 公网服务器端口:内网机器IP(localhost):内网机器端口 公网服务器用户名:公网服务器IP &

通过socks代理连接网络

动态转发中实现了A访问B所处的网络. 如果B能上公网而A不能上公网, 则可通过该种方法让A连入Internet, 实现网上冲浪. 注意必须在软件中指明使用socks v5时代理DNS查询, 否则使用域名的网站都无法打开.

如果A在中国, B在美国, 那么不就实现了”科学上网”了.

反过来, 如果A在公网, B为内网与公网的连接点, 这可以使用这种方式实现类似反向VPN的效果.

Shell socks代理设置

在shell中运行的命令比如git/apt等并不会主动走socks代理(电脑B), 为了迫使它们使用代理上网, 可以使用proxychains. 下载最新的源码包, 解压, 进入其目录, 使用./configure && make && sudo make install && sudo cp ./src/proxychains.conf /etc/完成安装. 将/etc/proxychains.conf中的socks4 127.0.0.1 9050修改为socks5 127.0.0.1 portA即完成配置. 使用时在命令前加上proxychains4即可迫使命令利用代理上网.

为了方便使用可以将/usr/local/bin/proxychains4软链接为/usr/local/bin/p, 在命令前直接加上p即可使用代理.

整理一下proxychain的安装配置代码见下:

portA=****  # 设置portA
wget https://github.com/rofl0r/proxychains-ng/archive/refs/tags/v4.14.zip
unzip v4.14.zip
cd proxychains-ng-4.14
./configure
make
sudo make install
sudo sed "s/socks4  127\.0\.0\.1 9050/socks5  127\.0\.0\.1 ${portA}" ./src/proxychains.conf > /etc/proxychains.conf
cd ..
rm -r proxychains-ng-4.14
rm v4.14.zip
cd /usr/local/bin
sudo ln -s proxychains4 p


server software GNU/Linux

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!