暗色模式

SSH 端口转发:一条命令打通内网服务访问与穿透

技术教程
2026-08-17
16
0
本文要点
  • SSH 端口转发可以让只有本机才能访问的服务,通过一条 SSH 连接暴露给远端。
  • 本地转发 -L:把本地端口映射到远端主机的内网服务,适合访问只监听 127.0.0.1 的数据库、面板。
  • 动态转发 -D:在本地开一个 SOCKS 代理,所有流量从远端主机出口,适合临时科学上网、抓包溯源。
  • 反向转发 -R:把本机服务暴露到公网,是"内网穿透"的标准姿势(无需 frp)。
  • 配合 -fN 后台运行 + ExitOnForwardFailure 失败即退,sshd 会按 GatewayPorts 决定转发端口监听地址。

为什么需要端口转发

运维时经常会遇到这样的问题:服务器上装了一个服务(数据库、监控面板、内网工具),它为了安全只监听 127.0.0.1,外网完全够不着。你想在本机用它,又不想改它的监听地址、不想暴露到公网。

SSH 端口转发就是干这个的——它把一条 SSH 连接变成一个"加密隧道",把远端端口"搬"到本机(或反过来),连接全程走 SSH 加密通道,不额外开防火墙端口,也不用装任何客户端。

本文用一台 Ubuntu 24.04 服务器演示 SSH 的三种端口转发,所有命令都真实可复现。示例中服务器里跑了一个只监听 127.0.0.1:8000 的测试服务(echo_server.py),用它来演示"只能本机访问的服务"如何被隧道救活。

三种转发一览

转发类型参数用途典型场景
本地转发-L本地端口 → 远端服务访问内网数据库、面板
动态转发-D本地 SOCKS 代理,出口走远端临时改出口 IP、内网穿墙
反向转发-R远端端口 → 本地服务内网穿透,把家里/公司内网服务暴露公网

场景一:服务只监听本机

先看演示服务的状态:它只监听 127.0.0.1:8000,外部 IP 完全访问不到。

ss -tlnp | grep :8000
curl -s http://127.0.0.1:8000/ping

服务在服务器本机正常响应;但用公网 IP 或从外部访问就会连接失败。这种"只对本机开放"的设备(数据库、临时面板、内网服务)正是端口转发要对付的。

演示服务只监听本机

场景二:本地转发 -L(访问远端本机服务)

本地转发把本地某个端口映射到远端主机上的服务。命令在服务器上执行:

ssh -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -fN \
    -L 18000:127.0.0.1:8000 root@47.238.230.124

-L 18000:127.0.0.1:8000 的含义:本机(执行 ssh 的一方)监听 18000 端口,所有到本机 18000 的连接,透过 SSH 隧道转发到远端主机的 127.0.0.1:8000。此时访问本机 18000 就等于访问远端那个"只监听本机"的服务。

sleep 1
ss -tlnp | grep 18000
curl -s http://127.0.0.1:18000/local-forward-test

看到 ssh 进程监听了 127.0.0.1:18000,curl 到 18000 返回了服务的 JSON 响应——远端那个只能本机访问的服务,现在从本地可以直接用。

本地转发 -L 实测

场景三:动态转发 -D(SOCKS 代理)

动态转发不开固定端口映射,而是在本地起一个 SOCKS5 代理,所有发给这个代理的流量都从远端主机出去。命令在本机(Mac)执行:

ssh -f -N -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no \
    -D 1080 root@47.238.230.124 -i /Users/Astarry/.ssh/aliyun-test

-D 1080 表示本地 1080 端口开一个 SOCKS5 代理。之后本机任何程序把代理指到 127.0.0.1:1080,它的网络出口就变成远端服务器:

lsof -nP -iTCP:1080 -sTCP:LISTEN | sed -n 2p
curl -s --socks5-hostname 127.0.0.1:1080 http://api.ipify.org
echo "== curl via socks5 127.0.0.1:1080 (tunnel exit) =="

curl --socks5-hostname 把请求走代理,api.ipify.org 返回的出口 IP 正是远端服务器 IP(47.238.230.124)——本地直连和走代理的出口不同,证明流量确实从远端出去了。

动态转发 -D 实测

场景四:反向转发 -R(内网穿透)

反向转发反过来:把本机服务暴露到远端主机(有公网 IP 的一方)。命令在本机执行:

ssh -f -n -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no \
    -R 19000:127.0.0.1:8000 root@47.238.230.124 -i /Users/Astarry/.ssh/aliyun-test

-R 19000:127.0.0.1:8000 的含义:远端主机(47.238.230.124)监听 19000 端口,转发到本机的 127.0.0.1:8000。这样远端服务器上的程序访问 127.0.0.1:19000,就连到了你本机的服务。

ssh root@47.238.230.124 'ss -tlnp | grep 19000; curl -s http://127.0.0.1:19000/ | head -8'

远端看到 sshd 监听了 19000,curl 它返回了本机服务的 HTML("This page is served from the Mac (client) localhost:8000")——内网穿透完成。家里/公司内网的电脑,不用 frp、不用公网 IP,一条 SSH 就把服务暴露给了有公网的服务器。

反向转发 -R 实测

参数细节与注意事项

后台运行与失败即退

  • -f:连接建立后转到后台运行,隧道挂在后台不断开。
  • -N:不执行远端命令,只做转发(纯隧道,不打开登录 shell)。
  • -o ExitOnForwardFailure=yes:端口被占用/建立失败时立即退出而不是静默挂着,排查问题必备。
  • 想结束隧道:pkill -f "ssh.*-L 18000"pkill -f "ssh.*-D 1080"(注意进程匹配要精确,避免误杀自己)。

反向转发的监听地址

-R 的端口默认只监听在远端的 127.0.0.1。想让公网都能访问,需要远端 sshd_config 打开:

GatewayPorts yes

配合 -R 时把目标地址写成 0.0.0.0 形式的绑定:-R 19000:127.0.0.1:8000 会因 GatewayPorts 决定最终监听在 0.0.0.0 还是 127.0.0.1。没有特殊需求建议保持默认(只监听本机更安全)。

保活

隧道长时间闲置可能被防火墙/运营商掐断,加一行:

ServerAliveInterval 60
ServerAliveCountMax 3

小结

SSH 端口转发是运维工具箱里最实用的一招:不装新软件、不开多余端口、全程加密,一条命令就能解决"访问内网服务"和"暴露内网服务"两个方向的需求。本文演示的 -L(访问远端本机服务)、-D(SOCKS 出口代理)、-R(内网穿透)三个场景覆盖了绝大多数日常用途,改改端口号就能直接套用到自己的环境。

发表评论

暂无评论,快来抢沙发吧!