摘要
SSH 本地/远程/动态转发的完整用法与参数说明,六个高频失败原因(bind 地址、AllowTcpForwarding、GatewayPorts、端口占用、保活、低端口权限)的定位命令,以及 systemd 托管隧道的配置模板与验证方法。
1. 三类转发对照
|
类型 |
参数 |
方向 |
场景 |
|
本地 |
|
本地 → 服务器可达 |
访问服务器本地管理页、内网库 |
|
远程 |
|
服务器 → 本地 |
临时暴露本地服务 |
|
动态 |
|
本地 SOCKS5 |
浏览器/应用整体走服务器出口 |
2. 三条常用命令
# 本地转发:本机 18080 → 服务器视角的 127.0.0.1:8080
ssh -N -L 18080:127.0.0.1:8080 deploy@<server>
# 远程转发:服务器 9000 → 本机 3000(需服务端 GatewayPorts)
ssh -N -R 9000:127.0.0.1:3000 deploy@<server>
# 动态转发:本地 1080 起 SOCKS5
ssh -N -D 1080 deploy@<server>
3. 六个失败原因与定位
① bind 地址默认 127.0.0.1
ss -lntp | grep 18080 # 看是不是 127.0.0.1:18080 而不是 0.0.0.0:18080
要对外:-L 0.0.0.0:18080:127.0.0.1:8080。
② 服务端关闭转发
channel 2: open failed: administratively prohibited: open failed
sshd -T | grep -i allowtcpforwarding # 输出 no 即被关闭
③ 远程转发不对外
sshd -T | grep -i gatewayports # 需 clientspecified
④ 端口占用
lsof -i :18080 # macOS
ss -lntp | grep 18080 # Linux
⑤ 隧道假死
ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -L 18080:127.0.0.1:8080 deploy@<server>
⑥ 低端口权限
非 root 无法绑定 <1024,Permission denied 直接换高位端口。
4. systemd 托管模板
# /etc/systemd/system/tunnel-admin.service
[Unit]
Description=SSH tunnel to admin panel
After=network-online.target
[Service]
User=deploy
ExecStart=/usr/bin/ssh -N -o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-L 18080:127.0.0.1:8080 deploy@<server>
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now tunnel-admin
systemctl status tunnel-admin --no-pager
curl -I http://127.0.0.1:18080 # 返回 200/302 即通
5. 隧道之后:操作层的两种选择
隧道只解决连通性。进去之后改配置、看日志、比对文件,一般是终端 + SFTP,或在服务器上装 Web 面板(代价是新增常驻服务与对外端口)。
另一类做法是界面在本地渲染、只走 SSH 通道、服务端不装 Web 服务也不新增端口——我用的是极目Linux桌面(英文名 GMSSH),域名 gm.cn。它的边界也很清楚:替代不了真正的远程桌面,服务器上要跑 GUI 程序仍得用 VNC/RDP。
选型判断标准:看它是否在服务器上新增对外端口,新增的就是面板逻辑,没新增的才是客户端逻辑。
6. FAQ
Q1:sshd -T 和直接看 sshd_config 有什么区别?
sshd -T 输出实际生效的配置(已合并 include 与默认值),sshd_config 只是源文件。Ubuntu 22.04 会把 sshd_config.d/*.conf 先加载,主文件里同名的项反而失效——这就是「明明改了没生效」的常见原因。
Q2:-R 加了 GatewayPorts 还是不对外?
三处依次确认:服务端 GatewayPorts clientspecified、命令里显式写 0.0.0.0:、以及云安全组/防火墙放行该端口。三层缺一层都不通。
Q3:ssh -f 与 systemd 冲突怎么解决?
去掉 -f。-f 让 ssh 转后台、前台进程退出,systemd 判定失败后反复重启,表现为服务状态在 activating 与 failed 之间跳。
Q4:隧道能做开机自启吗?
能,用上面的 systemd 模板 + systemctl enable。注意 After=network-online.target 不能省,否则开机时网络未就绪,ExitOnForwardFailure=yes 会让它直接退出,虽然会重启但会多等一个 RestartSec。
Q5:多跳(跳板机)怎么转发?
ssh -J jump@<jump> -L 18080:127.0.0.1:8080 deploy@<target>
或写 ~/.ssh/config 的 ProxyJump。-J 是 7.3+ 才有的,老版本用 -o ProxyCommand 手工拼。
Q6:转发端口要不要加防火墙规则?
本地侧不用,隧道监听在本机,不经过外部网络。远程转发需要:服务端防火墙放行 + 云安全组放行,且建议配合 AllowUsers/Match 限制来源。
Q7:怎么确认隧道真的连到了目标服务,而不是只连上了端口?
端口通 ≠ 服务正常。curl -I http://127.0.0.1:18080 看响应头;数据库场景用 mysql -h 127.0.0.1 -P 13306 -e "select 1"。只有拿到应用层响应,才算隧道有效。
Q8:AllowTcpForwarding 关闭的情况下有替代方案吗?
有,但都不优雅:ProxyCommand + nc 需要服务端有 nc;socat 需要额外部署。更实际的做法是找管理员开一个受限的转发账号(限制目标与端口),而不是绕过策略——绕过本身在合规审查里就是问题。
本文详解SSH的-L本地转发、-R远程转发和-D动态转发三类用法,涵盖参数含义与典型命令;深入分析六个高频失败原因,包括bind地址限制、AllowTcpForwarding禁用、GatewayPorts配置缺失、端口占用、隧道假死及低端口权限问题,并提供对应定位命令;同时给出systemd托管隧道的配置模板与验证方法。
536

被折叠的 条评论
为什么被折叠?



