SSH 动态端口转发允许你通过 SSH 隧道创建一个 SOCKS5 代理,所有流量通过远程服务器转发,实现安全上网和内网访问。本文详细介绍其工作原理和实战配置。

什么是动态端口转发

动态端口转发将本地端口转变为 SOCKS5 代理服务器。当应用连接到这个本地端口时,SSH 会将流量转发到远程服务器,由远程服务器代为访问目标地址。

1
2
3
4
5
6
7
8
本地主机                              SSH 服务器                          目标网站
+--------+                           +-----------+                        +----------+
| 浏览器  | ---> SOCKS ---> SSH ---> |           | ---> 目标网站          |          |
| 应用    |     (1080)    隧道        | 远程主机   |                      | 目标服务器|
+--------+                           +-----------+                        +----------+
    |                                      |                                 |
    +-------- 本地 1080 端口 -------------+                                 |
                               加密隧道                                        |

典型应用场景:

  • 在公共 WiFi 下保护上网流量(加密传输)
  • 访问内网受限服务
  • 绕过网络限制
  • 隔离环境下的安全上网

基础命令

启动 SOCKS 代理

1
ssh -D 1080 -N -f user@example.com

参数说明:

  • -D 1080:在本地监听 1080 端口作为 SOCKS 代理
  • -N:不执行远程命令,仅进行端口转发
  • -f:将 SSH 进程放入后台运行

验证代理是否生效

1
2
3
4
5
# 使用 curl 测试(通过 SOCKS5 代理访问 ip 检测网站)
curl --socks5 localhost:1080 https://ifconfig.me

# 返回结果应该是 SSH 服务器的公网 IP
# 47.76.159.216

停止代理

1
2
3
4
5
# 查找 SSH 进程
ps aux | grep ssh

# 杀掉对应进程
kill <PID>

进阶配置

保持连接(防止断开)

在长时间运行的代理中,SSH 会话可能因网络波动断开。添加以下参数保持连接:

1
2
3
4
5
ssh -D 1080 -N -f \
    -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=3 \
    -o TCPKeepAlive=yes \
    user@example.com

参数说明:

  • ServerAliveInterval=60:每 60 秒发送一次心跳
  • ServerAliveCountMax=3:最多重试 3 次
  • TCPKeepAlive=yes:启用 TCP 保活

指定绑定地址

默认只监听本地回环地址(127.0.0.1),只能本地使用。如果需要局域网其他设备访问:

1
2
3
4
5
# 监听所有接口
ssh -D 0.0.0.0:1080 -N -f user@example.com

# 或仅监听特定网卡
ssh -D 192.168.1.100:1080 -N -f user@example.com

注意:这样配置会接受来自其他机器的请求,需确保防火墙允许。

使用密钥连接

1
2
3
ssh -D 1080 -N -f \
    -i ~/.ssh/id_ed25519 \
    user@example.com

多跳串联

如果需要通过多台服务器中转:

1
2
3
4
# 方式一:直接在命令中指定多跳
ssh -D 1080 -N -f -J user@jumper.example.com user@target.example.com

# 方式二:使用 SSH Config 配置多跳(见下文)

SSH Config 简化配置

每次输入完整命令很麻烦,可以在 ~/.ssh/config 中添加配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# SOCKS 代理配置
Host proxy
    HostName example.com
    User your_username
    Port 22
    DynamicForward 1080
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes
    IdentityFile ~/.ssh/id_ed25519

配置后只需一条命令启动代理:

1
2
3
4
5
6
7
8
# 启动代理(前台运行,可看到日志)
ssh -N proxy

# 或后台运行
ssh -fN proxy

# 停止
ssh -O exit proxy

应用配置

浏览器配置

Chrome/Edge: 设置 → 高级 → 系统 → 打开代理设置 → SOCKS5 代理填写 127.0.0.1:1080

Firefox: 设置 → 搜索"代理" → 设置 → 手动配置 → SOCKS 主机填写 127.0.0.1,端口填写 1080

命令行工具

大部分 CLI 工具支持 SOCKS5 代理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# curl
curl --socks5 localhost:1080 https://example.com

# wget
wget -e use_proxy=yes -e socks_proxy=127.0.0.1:1080 https://example.com

# git
git config --global http.proxy socks5://127.0.0.1:1080
git config --global https.proxy socks5://127.0.0.1:1080

# npm
npm config set proxy socks5://127.0.0.1:1080
npm config set https-proxy socks5://127.0.0.1:1080

环境变量

某些应用支持通过环境变量使用代理:

1
2
export ALL_PROXY=socks5://127.0.0.1:1080
export all_proxy=socks5://127.0.0.1:1080

与 ProxyJump 结合

假设你有一台可访问内网的跳板机:

1
2
3
4
5
# 内网结构:本地 -> 跳板机 -> 内网服务器
# 目标:让本地浏览器通过跳板机访问内网服务

# 启动代理,指定通过跳板机访问
ssh -D 1080 -N -f -J user@bastion.example.com user@internal-server.internal

或在 SSH Config 中配置:

1
2
3
4
5
6
7
Host internal-proxy
    HostName internal-server.internal
    User your_username
    ProxyJump user@bastion.example.com
    DynamicForward 1080
    ServerAliveInterval 60
    ServerAliveCountMax 3

安全注意事项

1. 限制绑定范围

在公共网络中,避免监听 0.0.0.0:

1
2
# 推荐:仅本地使用
ssh -D 127.0.0.1:1080 -N -f user@example.com

2. 使用密钥而非密码

1
2
3
4
5
# 生成 ED25519 密钥(推荐)
ssh-keygen -t ed25519 -C "SOCKS proxy key"

# 将公钥添加到服务器
ssh-copy-id user@example.com

3. 定期更换密钥

定期生成新密钥并更新到服务器:

1
ssh-keygen -t ed25519 -f ~/.ssh/socks_proxy_key

4. 日志审计

定期检查服务器的 SSH 日志:

1
2
3
4
5
# 查看登录记录
lastlog

# 查看认证日志
sudo tail -f /var/log/auth.log | grep sshd

5. 使用 fail2ban 防止暴力破解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 安装 fail2ban
sudo apt install fail2ban

# 配置 SSH 保护
sudo vim /etc/fail2ban/jail.local

# 添加以下内容
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3

管理脚本

创建一个便捷的管理脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/bin/bash

# ~/.local/bin/socks-proxy

SOCKS_PORT=1080
SSH_HOST="user@example.com"
PID_FILE="/tmp/socks-proxy.pid"

start() {
    if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
        echo "SOCKS proxy is already running (PID: $(cat $PID_FILE))"
        return 1
    fi
    
    ssh -D 127.0.0.1:$SOCKS_PORT -N -f \
        -o ServerAliveInterval=60 \
        -o ServerAliveCountMax=3 \
        -o TCPKeepAlive=yes \
        "$SSH_HOST"
    
    echo $! > "$PID_FILE"
    echo "SOCKS proxy started on port $SOCKS_PORT"
}

stop() {
    if [ ! -f "$PID_FILE" ]; then
        echo "SOCKS proxy is not running"
        return 1
    fi
    
    kill $(cat "$PID_FILE") && rm -f "$PID_FILE"
    echo "SOCKS proxy stopped"
}

status() {
    if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
        echo "SOCKS proxy is running (PID: $(cat $PID_FILE))"
        
        # 测试代理是否工作
        if curl --socks5 localhost:$SOCKS_PORT -s -o /dev/null -w "%{http_code}" https://ifconfig.me | grep -q "200"; then
            echo "Proxy is working correctly"
        else
            echo "Proxy may not be working properly"
        fi
    else
        echo "SOCKS proxy is not running"
    fi
}

case "$1" in
    start) start ;;
    stop) stop ;;
    status) status ;;
    *) echo "Usage: $0 {start|stop|status}" ;;
esac

赋予执行权限:

1
chmod +x ~/.local/bin/socks-proxy

使用方式:

1
2
3
socks-proxy start   # 启动
socks-proxy stop    # 停止
socks-proxy status  # 状态

常见问题

Q: 代理启动后无法连接

排查步骤

  1. 检查 SSH 会话是否正常:ssh user@example.com
  2. 检查端口是否监听:netstat -tlnp | grep 1080
  3. 检查防火墙:sudo iptables -L -n

Q: 代理速度慢

可能原因

  • 网络延迟高
  • 带宽受限
  • 服务器负载高

优化建议

  • 选择延迟低的服务器
  • 使用 ChaCha20-Poly1305 加密(更快)
  • 考虑使用 WireGuard 等专用隧道

Q: 某些网站无法访问

原因:SOCKS 代理不处理 HTTP CONNECT 请求以外的协议

解决:对于需要完整代理的情况,考虑使用 ssh -L 转发特定端口到 HTTP 代理

总结

SSH 动态端口转发是一个强大且安全的工具:

  • 无需额外软件,仅需 SSH
  • 加密隧道保护流量
  • 灵活的客户端支持
  • 可与跳板机串联访问内网

合理使用可以显著提升网络安全性,特别适合在公共网络环境下工作或需要安全访问内网资源的场景。


参考来源