强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Dropbear SSH 完全指南 / 06 - 端口转发

第六章:端口转发

6.1 SSH 端口转发概述

SSH 端口转发(Port Forwarding)是 SSH 最强大的功能之一,它通过 SSH 加密通道转发任意 TCP 流量,本质上将 SSH 连接变为一条加密隧道。

端口转发类型

类型命令选项方向说明
本地转发 (Local)-L本地 → 远程将本地端口映射到远程服务
远程转发 (Remote)-R远程 → 本地将远程端口映射到本地服务
动态转发 (Dynamic/SOCKS)-D双向创建 SOCKS 代理
X11 转发-X远程 → 本地转发图形界面

数据流对比图

本地转发 (-L):
  [本地应用] → [本地端口] ═══SSH═══▶ [远程主机:端口]

远程转发 (-R):
  [远程应用] → [远程端口] ═══SSH═══▶ [本地主机:端口]

动态 SOCKS (-D):
  [本地应用] → [SOCKS代理] ═══SSH═══▶ [任意远程主机:端口]

6.2 本地端口转发(Local Forwarding)

本地端口转发是最常用的转发方式,它将本地的某个端口映射到通过 SSH 可达的远程服务。

基本语法

dbclient -L [bind_address:]local_port:destination_host:destination_port user@ssh_server

场景一:访问内网数据库

通过跳板机访问内网数据库服务器:

# 将本地 3307 端口映射到内网数据库服务器的 3306 端口
dbclient -L 3307:db-server.internal:3306 admin@jumphost

# 现在可以通过本地 3307 端口访问内网数据库
mysql -h 127.0.0.1 -P 3307 -u root -p
[你的电脑:3307] ═══SSH═══▶ [跳板机] → [db-server:3306]

场景二:访问内网 Web 服务

# 将本地 8080 映射到内网 Web 服务
dbclient -L 8080:intranet.company.com:80 admin@jumphost

# 浏览器访问 http://localhost:8080

场景三:安全访问嵌入式设备 Web UI

# 路由器管理界面通常使用 HTTP
# 通过 SSH 隧道加密访问
dbclient -L 8080:192.168.1.1:80 root@router-ssh

# 访问 https://localhost:8080

高级本地转发

# 绑定到所有接口(允许其他设备通过你的机器访问)
dbclient -L 0.0.0.0:8080:internal-web:80 admin@jumphost
# ⚠️ 安全风险:网络中的其他设备也可访问

# 绑定到特定接口
dbclient -L 127.0.0.1:8080:internal-web:80 admin@jumphost

# 多个转发规则
dbclient -L 3307:db:3306 -L 8080:web:80 -L 6380:redis:6379 admin@jumphost

# 后台运行
dbclient -f -N -L 8080:internal-web:80 admin@jumphost
# -f: 后台运行
# -N: 不执行远程命令

本地转发权限控制

Dropbear 服务端可以通过编译选项控制是否允许端口转发:

/* localoptions.h */
#define DROPBEAR_SVR_LOCALTCPFWD 1  /* 允许服务端本地转发 */
#define DROPBEAR_CLI_LOCALTCPFWD 1  /* 允许客户端本地转发 */

/* 设为 0 则禁用 */
# 服务端禁止本地端口转发
dropbear -j

6.3 远程端口转发(Remote Forwarding)

远程端口转发将远程服务器的端口映射到本地服务,常用于从外部访问内网设备。

基本语法

dbclient -R [bind_address:]remote_port:destination_host:destination_port user@ssh_server

场景一:暴露内网服务到公网

将内网的 Web 服务通过公网服务器暴露:

# 在内网设备上执行
dbclient -R 8080:localhost:80 admin@public-server

# 现在访问 public-server:8080 即可访问内网 Web 服务
[内网设备:80] ═══SSH═══▶ [公网服务器:8080] ← [外部用户]

场景二:IoT 设备远程调试

# 在 IoT 设备上建立反向隧道
dbclient -R 2222:localhost:22 admin@cloud-server

# 从云端连接到 IoT 设备
ssh -p 2222 root@localhost  # 在 cloud-server 上执行

场景三:内网穿透方案

#!/bin/sh
# /etc/init.d/reverse-tunnel.sh - IoT 设备反向隧道脚本

CLOUD_SERVER="cloud.example.com"
CLOUD_USER="tunnel"
REMOTE_PORT="2222"
KEEPALIVE=30

start_tunnel() {
    while true; do
        dbclient -y -R "$REMOTE_PORT:localhost:22" \
            -K "$KEEPALIVE" \
            -o "ExitOnForwardFailure yes" \
            "$CLOUD_USER@$CLOUD_SERVER"
        
        echo "隧道断开,30 秒后重连..."
        sleep 30
    done
}

case "$1" in
    start)
        start_tunnel &
        echo $! > /var/run/reverse-tunnel.pid
        ;;
    stop)
        kill $(cat /var/run/reverse-tunnel.pid 2>/dev/null) 2>/dev/null
        ;;
esac

远程转发的 GatewayPorts 配置

# 默认情况下,远程转发只绑定到 localhost
# 需要服务端配置 GatewayPorts

# Dropbear 使用 -a 选项允许任意主机连接到转发端口
dropbear -a  # 允许远程转发绑定到所有接口

# 客户端指定绑定地址
dbclient -R 0.0.0.0:8080:localhost:80 admin@server
# 注意:服务端也需要 -a 选项才能生效

6.4 动态 SOCKS 代理

动态转发将 SSH 连接变为 SOCKS 代理,可以代理任意 TCP 流量。

基本用法

# 创建 SOCKS5 代理,监听本地 1080 端口
dbclient -D 1080 admin@remote-server

# 后台运行
dbclient -f -N -D 1080 admin@remote-server

# 绑定到所有接口
dbclient -D 0.0.0.0:1080 admin@remote-server

配置应用使用 SOCKS 代理

# curl 使用 SOCKS 代理
curl --socks5 127.0.0.1:1080 https://example.com

# wget
wget -e "https_proxy=socks5://127.0.0.1:1080" https://example.com

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

# 环境变量
export ALL_PROXY=socks5://127.0.0.1:1080
export http_proxy=socks5://127.0.0.1:1080
export https_proxy=socks5://127.0.0.1:1080

浏览器 SOCKS 代理配置

Firefox:
  设置 → 常规 → 网络设置 → 手动代理配置
  SOCKS 主机: 127.0.0.1
  SOCKS 端口: 1080
  ☑ SOCKS v5

Chrome:
  启动参数: --proxy-server="socks5://127.0.0.1:1080"

嵌入式设备 SOCKS 代理

在嵌入式设备上建立 SOCKS 代理,用于安全访问受限网络:

# 通过嵌入式设备的 SOCKS 代理访问内网
dbclient -D 1080 admin@embedded-device

# 通过该代理访问内网服务
curl --socks5 127.0.0.1:1080 http://192.168.1.100/api/status

6.5 X11 转发

X11 转发允许在本地显示远程的图形界面应用。

前提条件

# 服务端需要编译时启用 X11 转发
# localoptions.h:
#define DROPBEAR_X11FWD 1

# 服务端需要 xauth 工具
which xauth

# 客户端需要 X 服务器(Linux/macOS 默认有)
echo $DISPLAY

基本用法

# 启用 X11 转发
dbclient -X user@remote-server

# 在远程执行图形程序
xeyes
firefox &
gedit /path/to/file.txt

安全的 X11 转发

# 使用 -X(受信任的转发)
dbclient -X user@remote

# 安全限制
# -X 受信任模式:远程应用可以完全访问本地 X 服务器
# -x 受限制模式:Dropbear 不支持(OpenSSH 支持)

安全警告: X11 转发允许远程应用捕获本地屏幕内容和键盘输入。仅在信任远程服务器时使用。


6.6 Agent 转发

Agent 转发已在第五章详细介绍,此处仅简要回顾其在端口转发中的角色。

# Agent 转发用于跳板机场景
dbclient -A admin@jumphost

# 在跳板机上,可以通过转发的 Agent 认证到目标服务器
dbclient admin@target-server
# 无需在跳板机上存储私钥

安全替代方案

# 使用 ProxyJump 替代 Agent 转发
dbclient -J admin@jumphost admin@target

# 使用 ProxyCommand
dbclient -o "ProxyCommand dbclient -W %h:%p admin@jumphost" admin@target

6.7 转发隧道管理

后台隧道

# 启动后台隧道
dbclient -f -N -L 8080:internal:80 admin@jumphost

# 查看隧道进程
ps aux | grep dbclient

# 终止隧道
kill $(pgrep -f "dbclient.*8080")

隧道保活

# 启用 keepalive 防止隧道超时断开
dbclient -K 30 -N -L 8080:internal:80 admin@jumphost

# 完整的可靠隧道脚本
#!/bin/sh
TUNNEL_CMD="dbclient -N -L 8080:internal:80 -K 30 admin@jumphost"

while true; do
    $TUNNEL_CMD
    echo "隧道断开,5 秒后重连..."
    sleep 5
done

多层隧道

[你的电脑] ══ SSH ══▶ [跳板机A] ══ SSH ══▶ [跳板机B] ══ SSH ══▶ [目标]
# 方法一:嵌套 ProxyCommand
dbclient \
    -o "ProxyCommand dbclient -W %h:%p admin@jumphost-A" \
    -o "ProxyCommand dbclient -W %h:%p admin@jumphost-B" \
    admin@target

# 方法二:链式 -J
dbclient -J admin@jumphost-A,admin@jumphost-B admin@target

# 方法三:嵌套隧道
# 终端 1:建立到跳板机 A 的隧道
dbclient -L 2222:jumphost-B:22 admin@jumphost-A

# 终端 2:通过隧道 A 连接到跳板机 B
dbclient -p 2222 admin@localhost

# 终端 3:通过跳板机 B 建立到目标的隧道
dbclient -L 3333:target:22 admin@jumphost-B

# 终端 4:连接到目标
dbclient -p 3333 admin@localhost

6.8 转发调试

常见问题排查

问题原因解决方案
Warning: remote port forwarding failed远程端口被占用更换端口或检查服务端配置
channel_setup_fwd_listener: bind: Address already in use本地端口被占用更换本地端口
Connection refused目标服务未运行检查目标服务状态
Connection timed out网络不通检查网络连通性
转发建立但无法连接服务端禁用了转发检查服务端 -j / -k 选项

调试命令

# 详细输出(调试转发问题)
dbclient -v -L 8080:internal:80 admin@jumphost

# 检查端口监听
ss -tlnp | grep 8080
netstat -tlnp | grep 8080

# 测试隧道连通性
curl -v http://localhost:8080

6.9 业务场景:安全远程运维

完整的远程运维方案

#!/bin/sh
# remote-ops.sh - 基于 SSH 隧道的远程运维方案

JUMPHOST="bastion.example.com"
JUMPUSER="ops"
DEVICE_IP="192.168.1.100"
DEVICE_SSH_PORT="22"
DEVICE_WEB_PORT="80"

# 1. 建立多服务隧道
echo "建立 SSH 隧道..."
dbclient -f -N \
    -L 2222:${DEVICE_IP}:${DEVICE_SSH_PORT} \
    -L 8080:${DEVICE_IP}:${DEVICE_WEB_PORT} \
    -K 30 \
    "${JUMPUSER}@${JUMPHOST}"

echo "隧道已建立:"
echo "  SSH: localhost:2222"
echo "  Web: http://localhost:8080"
echo ""
echo "使用方法:"
echo "  dbclient -p 2222 root@localhost"
echo "  curl http://localhost:8080"

# 2. 等待用户操作
read -p "按 Enter 关闭隧道..."
pkill -f "dbclient.*${JUMPHOST}"

6.10 本章小结

要点说明
本地转发 (-L)将本地端口映射到远程服务
远程转发 (-R)将远程端口映射到本地服务
动态 SOCKS (-D)创建 SOCKS 代理
X11 转发 (-X)远程图形界面转发
保活-K 30 防止隧道超时
后台运行-f -N 后台运行不执行命令
安全服务端可用 -j / -k 禁止转发

扩展阅读


上一章:认证方式 | 下一章:SCP 与 SFTP