2657 字
13 分钟
SSH反向隧道
2026-10-09

介绍下ssh反向隧道:ssh -R…

3:15 PM

SSH 反向隧道 -R#

与本地转发 -L 方向相反,用于让外网访问内网机器。

对比理解#

本地转发 -L反向隧道 -R
发起方你(内网)你(内网)
监听端口开在你本地远端服务器
用途访问远端服务让远端访问你本地

格式#

bash

Terminal window
ssh -R [远端端口]:[目标主机]:[目标端口] [用户名]@[SSH服务器]

典型场景:内网穿透#

你的开发机在公司内网,没有公网IP,想让外网朋友访问你本地的服务:

bash

Terminal window
ssh -R 8080:localhost:3000 user@公网服务器
```
- 你的机器发起 SSH 连接到公网服务器
- 公网服务器开放 `8080` 端口
- 外网朋友访问 `公网服务器:8080`,流量反向穿透回你本地的 `3000` 端口
```
外网用户 → 公网服务器:8080 → SSH隧道 → 你的内网机器:3000
```
## 让远端端口对外可访问
默认情况下 `-R` 开的端口**只监听 127.0.0.1**,外网访问不到。需要在服务端 `/etc/ssh/sshd_config` 加一行:
```
GatewayPorts yes

然后重启 sshd:

bash

Terminal window
sudo systemctl restart sshd

实用参数组合#

bash

Terminal window
ssh -R 8080:localhost:3000 -N -f user@公网服务器

你的实际用途#

结合你的 Ali ECS(cypherx.top),可以这样用:

bash

Terminal window
# 在家里/公司内网机器上运行
ssh -R 9000:localhost:8080 -N -f gx@cypherx.top

这样任何人访问 cypherx.top:9000 就能到达你内网机器的 8080,完全不需要内网机器有公网IP,也不需要配置路由器端口转发。这其实就是 frp、ngrok 这类工具的核心原理。

gx@Ali-ECS:~$ sudo netstat -tunlp [sudo] password for gx: Sorry, try again. [sudo] password for gx: Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN 1221/mysqld tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 29215/systemd-resol tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 18823/nginx: master tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1029/sshd: /usr/sbi tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 1221/mysqld tcp 0 0 127.0.0.1:9009 0.0.0.0:* LISTEN 81635/sshd: gx tcp 0 0 127.0.0.1:10808 0.0.0.0:* LISTEN 928/xray tcp6 0 0 :::21115 :::* LISTEN 43741/hbbs tcp6 0 0 :::21119 :::* LISTEN 42998/hbbr tcp6 0 0 :::21118 :::* LISTEN 43741/hbbs tcp6 0 0 :::21117 :::* LISTEN 42998/hbbr tcp6 0 0 :::21116 :::* LISTEN 43741/hbbs udp 0 0 0.0.0.0:51820 0.0.0.0:* - udp 0 0 127.0.0.53:53 0.0.0.0:* 29215/systemd-resol udp 0 0 172.31.28.241:68 0.0.0.0:* 29200/systemd-netwo udp 0 0 127.0.0.1:323 0.0.0.0:* 1119/chronyd udp6 0 0 :::51820 :::* - udp6 0 0 :::21116 :::* 43741/hbbs udp6 0 0 :::41012 :::* 43741/hbbs udp6 0 0 ::1:323 :::* 1119/chronyd

Show more

3:35 PM

可以看到反向隧道已经生效了:

tcp 0 0 127.0.0.1:9009 0.0.0.0:* LISTEN 81635/sshd: gx

这就是你从内网机器建立的 -R 隧道,sshd 在 ECS 上监听 9009 端口,进程归属 gx 用户。


但注意它绑定在 127.0.0.1:9009,只有 ECS 本机能访问,外网访问不到。

如果你想让外网也能访问,两种办法:

方法一:sshd_config 加 GatewayPorts

bash

Terminal window
sudo vim /etc/ssh/sshd_config
# 加上或改为:
GatewayPorts yes
sudo systemctl restart sshd

然后重新建立 -R 隧道,端口就会绑在 0.0.0.0:9009 上了。

方法二:在 ECS 上用 nginx 反代 127.0.0.1:9009 不改 sshd 配置,用 nginx 把某个域名/路径转发到 127.0.0.1:9009,更灵活,也更安全。

你现在建这个隧道的目的是什么?对外暴露服务还是自己用?

gx@Ali-ECS:~ curl localhost:9009 gx@Ali-ECS:\~

3:36 PM

curl 没有任何输出,说明连接成功了但服务没有返回任何内容,或者内网那端的服务没有正常响应。

排查#

1. 内网机器上的服务还在跑吗?

去内网机器上确认服务是否在监听:

bash

Terminal window
netstat -tunlp | grep [端口]
# 或者
curl localhost:[内网端口]

2. 反向隧道是怎么建的?

把你建隧道的命令发一下,比如:

bash

Terminal window
ssh -R 9009:localhost:XXXX user@cypherx.top

看看 XXXX 那个内网端口对不对。

3. 隧道进程还活着吗?

在内网机器上:

bash

Terminal window
ps aux | grep ssh

curl 无输出通常是连上了但对面没数据返回,大概率是内网服务挂了或者端口映射写错了。

[gx@nas ~]$ curl localhost:8080

Show more

3:37 PM

NAS 上的服务是正常的,localhost:8080 有响应。

问题在这段 JS:

javascript

location.href = pr + "://" + location.hostname + pt + redirect_suffix;

它拿到 location.hostname 后做了跳转,通过隧道访问时 hostname 是 localhost,它就跳转到 http://localhost/redirect.html?count=xxx,但 ECS 的 localhost 上没有这个服务,所以 curl 拿不到任何内容。


这是个前端重定向问题#

这个 NAS 的 Web UI 设计上期望用真实 IP 或域名访问,用 localhost 访问时重定向会出问题。

解决思路:给隧道绑一个域名

在 ECS 上用 nginx 反代,加上正确的 Host header:

nginx

server {
listen 9090;
server_name _;
location / {
proxy_pass http://127.0.0.1:9009;
proxy_set_header Host nas.cypherx.top; # 或者NAS的内网IP
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

然后访问 http://cypherx.top:9090,nginx 把 Host 改成 NAS 期望的值再转给隧道。

你的 NAS 平时在内网是用什么地址访问的?192.168.x.x:8080 还是有个域名?

加上GatewayPorts yes后浏览器访问http://cypherx.top:9009成功打开家里NAS页面了(8080端口)了。这个参数起什么作用?

3:45 PM

GatewayPorts 的作用#

没加之前,-R 建立的隧道端口只绑定在:

127.0.0.1:9009 ← 只有ECS本机能访问

加了 GatewayPorts yes 之后,绑定变成:

0.0.0.0:9009 ← 所有网卡、所有来源都能访问

为什么默认不开放?#

安全考虑。SSH 的设计哲学是:

反向隧道默认只给建立隧道的那个用户自己用,不应该自动暴露给整个互联网。

毕竟任何一个能 SSH 进来的用户,都可以用 -R 在服务器上开端口,如果默认对外,相当于任何用户都能把服务器变成自己的跳板,风险很大。


你现在的效果#

外网浏览器 → cypherx.top:9009 → SSH反向隧道 → 家里NAS:8080

本质上就是一个简单版的 frp/ngrok 内网穿透,而且全程加密,不错👍

唯一要注意的是 9009 端口现在对全互联网开放,如果 NAS 登录没有保护,建议在阿里云安全组里限制一下来源 IP。


frp/ngrok是什么

4:47 PM

frp / ngrok 是什么#

它们都是内网穿透工具,解决的问题和你刚才用 SSH 反向隧道做的事情完全一样:

让没有公网 IP 的内网机器,能被外网访问到。


和你刚才做的对比#

你的方案:
家里NAS → SSH反向隧道 → Ali ECS → 外网用户
frp的方案:
家里NAS(frpc) → frp隧道 → 公网服务器(frps) → 外网用户

原理一模一样,只是 frp 是专门为这个场景设计的工具。


那它们比 SSH 反向隧道好在哪里?#

SSH -Rfrp/ngrok
配置复杂度简单稍复杂
断线自动重连❌ 断了就断了✅ 自动重连
多端口管理每个端口一条命令配置文件统一管理
支持 HTTP 域名❌✅ 可以按域名路由
流量统计面板❌✅
稳定性一般好

简单说#

  • ngrok:商业产品,有免费额度,不需要自己有服务器,开箱即用,适合临时用
  • frp:开源免费,需要自己有公网服务器(比如你的 Ali ECS),适合长期稳定使用

你已经有 Ali ECS 了,如果以后想长期稳定穿透 NAS,部署一个 frp 比 SSH 反向隧道更靠谱,断网自动重连是最大的优势。


frp和wireguard干的事情有什么区别

4:59 PM

好问题,很多人容易混淆,但它们解决的是不同层次的问题。

核心区别#

frpWireGuard
本质端口转发/反向代理VPN(虚拟私有网络)
工作层应用层网络层
暴露的是指定的端口/服务整个网络
访问方式公网IP+端口像在同一局域网内

用你家NAS举例#

frp 的方式:

外网 → cypherx.top:9009 → NAS的8080端口

只能访问 NAS 的 8080 端口,其他设备、其他端口都访问不到,精确暴露。

WireGuard 的方式:

外网设备加入VPN后 → 直接访问 192.168.3.x 整个内网

就好像你人在家里一样,NAS、路由器、打印机、其他电脑全都能访问。


你的 ECS 上已经有 WireGuard 了#

从刚才的 netstat 可以看到:

udp 0.0.0.0:51820 ← 这就是WireGuard

所以你其实已经可以用 WireGuard 连回家里内网了(如果家里也配了的话)。


怎么选#

  • 只想暴露某个服务给别人用 → frp
  • 自己出门在外想访问家里所有设备 → WireGuard
  • 临时测试/给别人演示 → ngrok

你 ECS 上的 WireGuard 是连家里还是连其他地方的?

如何搭建frp

5:01 PM

先看你的需求,搭法会不一样:

Q: 你想穿透哪台机器? A: 家里NAS

Q: 穿透的目的? A: 都有

5:04 PM

好,目标明确:Ali ECS 做服务端(frps),家里 NAS 做客户端(frpc),对外暴露 NAS 服务。


第一步:ECS 上安装 frps#

bash

Terminal window
# 下载最新版(查看最新版本号:github.com/fatedier/frp/releases)
wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz
tar -xzf frp_0.61.0_linux_amd64.tar.gz
cd frp_0.61.0_linux_amd64
# 只需要服务端文件
sudo cp frps /usr/local/bin/

创建配置文件:

bash

Terminal window
sudo mkdir /etc/frp
sudo vim /etc/frp/frps.toml

toml

bindPort = 7000 # frp通信端口
auth.token = "你自己设一个密码" # 客户端连接时验证用

创建 systemd 服务:

bash

Terminal window
sudo vim /etc/systemd/system/frps.service

ini

[Unit]
Description=frp Server
After=network.target
[Service]
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=always
[Install]
WantedBy=multi-user.target

bash

Terminal window
sudo systemctl enable frps --now

阿里云安全组放行端口: 7000(frp通信用)


第二步:NAS 上安装 frpc#

先确认 NAS 是什么架构:

bash

Terminal window
uname -m
# x86_64 → amd64
# aarch64 → arm64

下载对应版本,以 amd64 为例:

bash

Terminal window
wget https://github.com/fatedier/frp/releases/download/v0.61.0/frp_0.61.0_linux_amd64.tar.gz
tar -xzf frp_0.61.0_linux_amd64.tar.gz
cd frp_0.61.0_linux_amd64
sudo cp frpc /usr/local/bin/

创建配置文件:

bash

Terminal window
sudo mkdir /etc/frp
sudo vim /etc/frp/frpc.toml

toml

serverAddr = "cypherx.top"
serverPort = 7000
auth.token = "你自己设一个密码" # 和frps一致
# 暴露NAS的Web界面给所有人
[[proxies]]
name = "nas-web"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8080
remotePort = 9009 # 外网访问 cypherx.top:9009
# 暴露SSH给自己运维用
[[proxies]]
name = "nas-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 9022 # 外网 ssh -p 9022 user@cypherx.top

同样创建 systemd 服务自启:

bash

Terminal window
sudo vim /etc/systemd/system/frpc.service

ini

[Unit]
Description=frp Client
After=network.target
[Service]
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

bash

Terminal window
sudo systemctl enable frpc --now

阿里云安全组放行端口: 9009、9022


验证#

bash

Terminal window
# ECS上看frps日志
sudo journalctl -u frps -f
# 看到类似这行说明NAS已连上:
# [I] [service.go] client login info: ip [xxx] version [0.61.0] hostname [] os [linux]

然后浏览器访问 http://cypherx.top:9009 就能打开 NAS 页面了。


和你之前 SSH -R 方案的区别#

  • NAS 断网重连后 frpc 会自动重连,不需要手动重建隧道
  • 多个端口在一个配置文件里统一管理
  • 更稳定,适合长期运行

你的 NAS 是什么系统?群晖、威联通还是自建的?

Claude is AI and can make mistakes. Please double-check responses.

SSH反向隧道
http://cypherx.top/posts/cs/ssh反向隧道/
作者
Cypherx
发布于
2026-10-09
许可协议
CC BY-NC-SA 4.0