用 Cloudflare Tunnel 从本地 SSH 到无公网 IP 的服务器
以下内容为 AI 生成,未实际验证。
基于 2026 年 7 月 Cloudflare 官方最新文档撰写。本教程只讲一个场景:目标服务器没有公网 IP,运行 cloudflared 主动连到 Cloudflare;你从本地开发机通过 Cloudflare 中转 SSH 过去。与这个场景无关的内容一律不涉及。参考链接统一列在文末。
你将搭出什么
- 服务器不需要公网 IP、不需要端口转发,防火墙可以关掉全部入站。
- 本地照常
ssh user@ssh.example.com,VSCode Remote-SSH、scp、rsync 都能直接用。 - SSH 之前先过一道身份认证(邮箱验证码或 SSO),陌生人连你的 SSH banner 都看不到。
- 全程免费(Cloudflare Zero Trust 免费档支持 50 个用户)。
整体数据流如下:
你的本地开发机 Cloudflare 边缘网络 目标服务器(无公网 IP)
┌──────────────────┐ ┌───────────────────────┐ ┌─────────────────────┐
│ ssh 客户端 │ │ Cloudflare Access │ │ cloudflared 守护进程│
│ ssh user@ssh. │ │ (校验身份:OTP/SSO) │ <== │ 主动向边缘拨出 │
│ example.com │ └──────────▲────────────┘ 4条 │ 加密长连接 │
└────────┬─────────┘ │ ① 先认证 长连接 │ QUIC/HTTP2 · 7844 │
│ ProxyCommand │ (出站) └──────────┬──────────┘
▼ │ │ 隧道内转发
┌──────────────────┐ ┌──────────┴────────────┐ ▼
│ cloudflared │ ===> │ Tunnel 边缘接入点 │ =============> ┌──────────────┐
│ access ssh │ │ 按主机名分发到 │ 多路复用 │ sshd (22端口)│
│ (认证+代理) │ 443 │ 对应隧道的活跃连接 │ │ 只监听内网 │
└──────────────────┘ └───────────────────────┘ └──────────────┘
客户端无监听端口暴露 服务器无需公网 IP / 端口转发,防火墙可全关入站
一、原理(5 分钟版)
为什么不需要公网 IP:传统内网穿透的思路是「从公网找入口把流量引进来」,Cloudflare Tunnel 反过来——服务器上的 cloudflared 进程主动向 Cloudflare 拨出加密长连接(默认 QUIC,UDP 7844 端口;UDP 被封会自动降级 HTTP/2,TCP 7844)。防火墙默认放行出站流量,所以服务器侧零改动。连接建立后通道是双向的:Cloudflare 边缘收到的 SSH 流量顺着这条已建立的连接送回你的服务器。
SSH 流量的完整路径:你执行 ssh user@ssh.example.com → ssh 根据 ~/.ssh/config 里的 ProxyCommand 调用本机的 cloudflared access ssh → 本机 cloudflared 先完成身份认证(首次弹浏览器,之后令牌缓存在本地)→ 向 Cloudflare 边缘发起 WebSocket 连接(443 端口)→ 边缘校验身份后,按主机名把流量转进服务器端 cloudflared 事先建立的隧道 → 服务器端 cloudflared 把流量代理到本机的 localhost:22(sshd)。之后就是正常的 SSH 密钥/密码登录——Access 认证和 SSH 认证是前后两道门,互不替代。
你只需要记住这几个概念:
| 概念 | 一句话解释 |
|---|---|
| Tunnel(隧道) | 一个持久的逻辑对象,有名字和 UUID;服务器上的 cloudflared 拿 token 启动后把它"激活" |
| Tunnel token | 一串 eyJ...,创建隧道时 Dashboard 给你,cloudflared 靠它运行隧道。谁拿到谁就能跑这条隧道,当密码保管 |
| Published application(路由) | 「ssh.example.com 这个主机名的流量 → 转发到本机 localhost:22」的一条映射,创建时 Cloudflare 自动帮你建 DNS 记录 |
| Access Application | 把 ssh.example.com 声明为受保护资源,挂上"谁能访问"的规则 |
| Policy(策略) | 那条规则本身:动作(Allow/Block/Bypass/Service Auth)+ 条件(比如邮箱等于你) |
为什么有的东西只能在 Web UI 上配:隧道有两种管理模式。你在 Dashboard 里点出来的隧道是"远程管理"模式——路由规则存在 Cloudflare 云端,服务器上的 cloudflared 只拿 token 启动、联网拉配置,所以路由只能用 Web UI(或 API/Terraform)改,没有本地配置文件可写。另一种"本地管理"模式(CLI 创建、本地写 config.yml)官方定位为遗留/测试用途,本教程不用它。你照某些旧教程在服务器上写 /etc/cloudflared/config.yml 配 ingress 却不生效,就是因为你的隧道是 Dashboard 建的远程管理模式,那份文件会被完全忽略。
二、准备工作
- 一个 Cloudflare 账号 + 一个已托管在 Cloudflare 上的域名(NS 已指向 Cloudflare)。
- 目标服务器:sshd 正常运行,能出网(443 + 7844 端口)。
- 本地开发机:有 OpenSSH 客户端(Win10+ 自带)。
两端都装 cloudflared(服务器和本地开发机各一份):
# Debian/Ubuntu
curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o /tmp/cf.deb
sudo dpkg -i /tmp/cf.deb
# macOS
brew install cloudflared
# Windows
winget install Cloudflare.cloudflared
三、服务器侧:创建隧道和路由(全部在 Web UI)
1. 创建隧道。 登录 Cloudflare Dashboard → Networking > Tunnels → Create a tunnel → 选 Cloudflared → 起个名字(如 home-nas)。
旧教程里的入口是「Zero Trust → Networks → Tunnels」。现在主控制台 Networking > Tunnels 和 Zero Trust 侧的 Networks > Connectors 管理的是同一批隧道,用哪个都行。
2. 在服务器上运行安装命令。 页面会给出一行命令:
sudo cloudflared service install eyJhIjoi...(一长串 token)
在目标服务器执行。它会装好 systemd 服务并写入 token。sudo systemctl status cloudflared 显示 active、Dashboard 隧道状态变为 Healthy,即隧道已通。
3. 添加 SSH 路由。 隧道详情页 → Routes 标签 → Add route → 选 Published application:
- Subdomain 填
ssh,Domain 选你的域名 → 得到ssh.example.com - Service 类型选 SSH,地址填
localhost:22(sshd 在另一台内网机器就填那台的IP:22)
保存后 Cloudflare 自动创建 CNAME 记录(ssh.example.com → <UUID>.cfargotunnel.com),这就是你的 SSH 入口。
到这里隧道已经能转发 SSH 了——但任何人知道域名都能来爆破密码,所以下一步必做。
四、加身份门:Access 应用和 Policy(全部在 Web UI)
1. 确认登录方式。 进 Zero Trust > Integrations > Identity providers。个人使用最省事的是添加 One-time PIN:访问时输入邮箱、收一个验证码即可登录,不需要接任何外部系统(也可以用 GitHub/Google 等 SSO,同一页面添加)。
2. 创建 Access 应用。 Zero Trust > Access controls > Applications → Create new application → 选 Self-hosted → public hostname 填 ssh.example.com(与上一步路由主机名严格一致)。
3. 配置 Policy。 在应用里新建策略:
- Action = Allow
- Include 规则:Emails = 你自己的邮箱
就这一条就够了。关键语义:Access 默认拒绝——不匹配 Allow 的人天然被挡,你不需要写"拒绝其他人"。Policy 里真正容易踩的坑只有一个:同一个规则里填多个值是 AND(同时满足)不是 OR;想"满足其一"就加多条 Include 规则。
保存后,任何指向 ssh.example.com 的流量先被 Access 拦下验明正身,通过后才进隧道。
五、本地侧:一行 ssh config
编辑本地 ~/.ssh/config(Windows 在 C:\Users\<你>\.ssh\config):
Host ssh.example.com
ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
cloudflared 路径因系统而异(macOS Homebrew 通常在 /opt/homebrew/bin/cloudflared,可用 which cloudflared 确认;Windows 写 exe 完整路径,含空格加引号)。
然后正常连接:
ssh ubuntu@ssh.example.com
首次连接会弹浏览器让你完成 Access 认证(收验证码或 SSO),令牌缓存后在有效期内不再弹窗,体验与直连公网服务器无异。scp、rsync、VSCode Remote-SSH 都直接可用。
六、CI/CD 场景(可选)
无人值守环境不能弹浏览器,用 Service Token:
- Zero Trust > Access controls > Service credentials > Service Tokens 创建令牌,得到 Client ID + Client Secret。
- 在 Access 应用上再加一条策略:Action = Service Auth,Include = 该 Service Token(与真人的 Allow 策略并存)。注意动作必须是 Service Auth,选 Allow 会导致退回浏览器认证、CI 直接卡死。
- CI 中连接:
cloudflared access ssh \
--hostname ssh.example.com \
--service-token-id "$CF_ACCESS_CLIENT_ID" \
--service-token-secret "$CF_ACCESS_CLIENT_SECRET"
提醒:CI 里安装 cloudflared 要锁定版本,不要每次拉 latest(2026.6.0 就有 Service Token 被忽略的回归 bug,遇到请用 2026.5.1)。
七、验证与加固
- Dashboard 隧道状态 Healthy
- 自己能
ssh登录;换一台未认证的设备访问https://ssh.example.com,应被 Access 登录页拦住 - 服务器防火墙拒绝所有入站(
sudo ufw deny incoming或安全组删光入站规则),SSH 只经隧道可达 - sshd 保持加固:
PasswordAuthentication no、PermitRootLogin no——隧道和 Access 是额外的门,不替代 sshd 自身安全 - tunnel token 没提交进 Git 仓库
八、排错速查
| 症状 | 原因与处置 |
|---|---|
| Error 1033 | 服务器端 cloudflared 没跑。journalctl -u cloudflared -f 看日志 |
日志报 Failed to dial a quic connection |
UDP 7844 被封。放行它,或接受自动降级 HTTP/2 |
| 首次连接反复弹浏览器 | Access 应用没建,或应用域名与路由主机名不一致 |
| OTP 提示验证码 already used | 企业邮件网关提前"消费"了验证码。重新请求,并把 noreply@notify.cloudflare.com 加白名单 |
| 改了本地 config.yml 不生效 | 远程管理隧道的路由只能在 Dashboard 改,本地配置文件会被忽略 |
CI 中报 banner exchange 超时 |
策略动作应为 Service Auth 而非 Allow;或 cloudflared 版本踩中回归,锁 2026.5.1 |
附:旧资料辨别(30 秒版)
这个产品改过好几次名和入口,搜资料时对不上很正常:
- Argo Tunnel = Cloudflare Tunnel 的旧名,2021 年已免费;带这名字的教程收费描述和截图都过期
- dash.teams.cloudflare.com / Zero Trust → Networks → Tunnels = 现在的 Networking > Tunnels(主控制台)或 Networks > Connectors(Zero Trust 侧)
- 让你写
config.yml配 ingress 的教程 = 本地管理模式,与 Dashboard 建的隧道不兼容 cloudflared access ssh-gen短寿命证书方案 = 已标记 legacy,个人场景直接用本教程的 Access 认证即可,别碰
本教程基于撰写时(2026 年 7 月)的官方文档整理,仅供学习参考。Cloudflare 产品迭代较快,操作前请以官方最新文档为准。
参考链接
官方文档(核心依据)
- Cloudflare Tunnel 概述(原理、出站连接模型):https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/
- 通过 Dashboard 创建远程管理隧道:https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/
- SSH over Tunnel(client-side cloudflared,本教程主线):https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-cloudflared-authentication/
- SSH 四种接入方式总览:https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/
- 本地管理 vs 远程管理隧道:https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/
- Access 自托管应用配置:https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/
- Access Policy 详解(Action / Include-Require-Exclude / Selector):https://developers.cloudflare.com/cloudflare-one/access-controls/policies/
- One-time PIN 身份提供商:https://developers.cloudflare.com/cloudflare-one/integrations/identity-providers/one-time-pin/
- Service Token:https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/
- 隧道与防火墙(出站端口 7844、积极安全模型):https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/
- 隧道排错:https://developers.cloudflare.com/tunnel/troubleshooting/
- Tunnel 进入主控制台(Networking > Tunnels)changelog:https://developers.cloudflare.com/changelog/product/tunnel/
- 旧 short-lived certificate 方案(legacy):https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/short-lived-certificates-legacy/
官方博客与社区
- Cloudflare Tunnel 免费化公告(Argo Tunnel 更名历史):https://blog.cloudflare.com/tunnel-for-everyone/
- Zero Trust 控制台导航调整说明(Tunnels 移至 Networks > Connectors):https://community.cloudflare.com/t/upcoming-changes-to-zero-trust-dashboard-and-navigation/856342
- Zero Trust 免费套餐(50 用户):https://controld.com/blog/cloudflare-zero-trust-pricing/
社区实践与已知问题
- cloudflared 2026.6.0 Service Token 回归 bug(GitHub Issue):https://github.com/cloudflare/cloudflared/issues/1674
- CI 场景使用 Service Token 的 SSH over Tunnel 指南:https://github.com/NX1X/cloudflare-tunnel-ssh-action/blob/main/GUIDE.md
- GitHub Actions 中通过隧道 SSH 的讨论:https://github.com/orgs/community/discussions/183786