Appearance
Homelab 终极远程访问架构:Cloudflare Tunnel + Nginx 统一接入层(多 SSH 主机版)

不只是 Web 服务,连 SSH 都能统一接入。通过 Nginx Stream 模块 + SNI 路由,一个隧道、一个端口,访问任意 SSH 主机。
为什么需要这套架构?
如果你已经尝试过 Cloudflare Tunnel,可能会遇到几个很实际的问题:
- 子域名泛滥:每新增一个服务就需要增加一个二级域名,DNS 记录越来越长。
- 协议受限:
cloudflared原生主要支持 HTTP/HTTPS,SSH 等 TCP 协议需要额外处理。 - 多 SSH 主机困境:当 Homelab 中有多台机器需要通过 SSH 访问时,简单的 TCP 隧道方案无法区分目标主机。
这套架构的核心目标就是解决这些问题:通过 Nginx 统一接入层,实现所有内网服务(包括多台 SSH 主机)的智能路由与统一管理。
终级方案整体架构
这套架构的核心思想是:在 Homelab 内部部署一个 Nginx 反向代理,作为所有内网服务的唯一入口。cloudflared 只需要将流量指向这个 Nginx,由 Nginx 根据请求的特征(域名、路径、协议)将流量路由到具体的后端服务。
text
公网用户
│
▼
Cloudflare 边缘节点 (HTTPS/TLS)
│ (加密隧道)
▼
cloudflared (Homelab 中的守护进程)
│ (将流量转发到 Nginx)
▼
Nginx 统一接入层 (核心路由)
│
├── HTTP/HTTPS 路由 (通过 location 路径)
│ ├── https://homelab-esxi.your-domain.com → ESXi (192.168.0.201:443)
│ ├── https://homelab-vcenter.your-domain.com → vCenter (192.168.0.202:443)
│ └── https://gitlab.your-domain.com → GitLab (172.16.0.10:80) [独立子域名]
│
└── TCP/SSH 路由 (通过 SNI)
├── dev.homelab.your-domain.com → 开发机 (172.16.0.10:22)
├── k8s.homelab.your-domain.com → K8s 主节点 (172.16.0.20:22)
└── storage.homelab.your-domain.com → 存储节点 (172.16.0.30:22)💡 注意
解决 esxi 及 vcenter 路由重写添加 /ui 问题,必须使用独立二级域名
准备工作
在开始之前,确保你已经具备以下条件:
| 条件 | 说明 |
|---|---|
| Cloudflare 账号 | 你拥有的域名已在 Cloudflare 上托管 |
| 一台 Linux 虚拟机 | 用于运行 cloudflared 守护进程(如 Ubuntu 22.04 LTS) |
| 内网服务信息 | ESXi、vCenter 等服务的 IP 地址和端口 |
前置操作
登录到您的 Linux 虚拟机,完成以下操作。
1. 配置本地 HOST DNS 解析
查看 Linux 虚拟机的静态 IP(例如 192.168.0.212),执行以下命令将其写入 /etc/hosts 文件,以便在 Nginx 配置文件中使用 homelab.local 主机名:
bash
echo "192.168.0.212 homelab.local" | sudo tee -a /etc/hosts2. 安装 cloudflared
bash
# 下载并安装 cloudflared
wget -q https://github.com/cloudflare/cloudflared/releases/download/2026.7.2/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
# 验证安装
cloudflared --version3. 安装 NGINX
bash
sudo apt update
sudo apt install nginx -y第一部分:NGINX 端配置
1.1 创建 SSL 配置目录并上传证书
bash
sudo mkdir -p /etc/nginx/ssl将 Cloudflare Origin CA 证书(.pem)和私钥(.key)文件上传到 /etc/nginx/ssl/ 目录下。
证书说明:在 Cloudflare Dashboard → SSL/TLS → Origin Server 中创建证书,选择「Let Cloudflare generate a private key and CSR」,有效期建议设置为最长(15 年)。下载后上传至服务器即可。
1.2 配置 Nginx HTTP/HTTPS 路由(Web 服务)
我们使用 /etc/nginx/conf.d/ 目录,按服务拆分配置文件。首先删除默认的 sites-enabled/default,然后为每个服务创建独立的配置文件。
bash
sudo rm -f /etc/nginx/sites-enabled/default1.2.1 统一入口配置:homelab.conf
nginx
# /etc/nginx/conf.d/homelab.conf
server {
listen 443 ssl http2;
server_name homelab.your-domain.com;
ssl_certificate /etc/nginx/ssl/your-domain.com.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
access_log /var/log/nginx/homelab-access.log;
error_log /var/log/nginx/homelab-error.log;
# Cloudflare IP 地址范围(用于获取真实客户端 IP)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
# 路径路由:通过 location 区分不同服务
# location /esxi/ {
# proxy_pass https://192.168.0.201:443/;
# proxy_ssl_verify off;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
# location /vcenter/ {
# proxy_pass https://192.168.0.202:443/;
# proxy_ssl_verify off;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
location / {
return 404;
}
}1.2.2 ESXi 限定前缀域名配置 homelab-esxi.conf
nginx
# /etc/nginx/conf.d/homelab-esxi.conf
server {
listen 443 ssl http2;
server_name homelab-esxi.your-domain.com;
ssl_certificate /etc/nginx/ssl/your-domain.com.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
access_log /var/log/nginx/esxi-access.log;
error_log /var/log/nginx/esxi-error.log;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
location / {
proxy_pass https://192.168.0.201;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}1.2.3 vCenter 限定前缀域名配置 homelab-vcenter.conf
nginx
# /etc/nginx/conf.d/homelab-vcenter.conf
server {
listen 443 ssl http2;
server_name homelab-vcenter.your-domain.com;
ssl_certificate /etc/nginx/ssl/your-domain.com.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
access_log /var/log/nginx/vcenter-access.log;
error_log /var/log/nginx/vcenter-error.log;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
location / {
proxy_pass https://192.168.0.202;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}1.2.4 GitLab 独立域名配置:gitlab.conf
nginx
# /etc/nginx/conf.d/gitlab.conf
server {
listen 443 ssl http2;
server_name gitlab.your-domain.com;
ssl_certificate /etc/nginx/ssl/your-domain.com.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
access_log /var/log/nginx/gitlab-access.log;
error_log /var/log/nginx/gitlab-error.log;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
location / {
proxy_pass http://172.16.0.10:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}1.2.5 default.conf(默认 404 规则)
nginx
# /etc/nginx/conf.d/default.conf
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /etc/nginx/ssl/your-domain.com.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
return 404;
}1.2.6 http-redirect.conf(HTTP 强制跳转 HTTPS)
nginx
# /etc/nginx/conf.d/http-redirect.conf
server {
listen 80;
server_name homelab.your-domain.com gitlab.your-domain.com;
return 301 https://$host$request_uri;
}1.3 配置 Nginx TCP/SSH 多主机路由(核心)
编辑 /etc/nginx/nginx.conf,在 events 模块之后添加 Stream 配置:
nginx
stream {
map $ssl_preread_server_name $ssh_backend {
dev.homelab.your-domain.com 172.16.0.10:22;
k8s.homelab.your-domain.com 172.16.0.20:22;
storage.homelab.your-domain.com 172.16.0.30:22;
default 172.16.0.10:22;
}
server {
listen 2222;
proxy_pass $ssh_backend;
proxy_connect_timeout 1s;
proxy_timeout 3s;
ssl_preread on;
}
}配置完成后,执行 sudo nginx -t 检查配置语法是否正确,然后执行 sudo systemctl reload nginx 重新加载配置。
第二部分:cloudflared 端配置
2.1 认证并绑定域名
bash
cloudflared tunnel login命令执行后会返回一个授权链接。在浏览器中打开该链接,选择你的 Cloudflare 域名,完成授权。
提示:授权成功后,
cloudflared会在你的虚拟机中生成一个cert.pem证书文件,用于后续的身份验证。
2.2 创建隧道
bash
cloudflared tunnel create homelab执行后会生成一个 Tunnel UUID 和一个 credentials 文件(如 <Tunnel UUID>.json),请妥善保管。
2.3 配置隧道路由规则
创建 ~/.cloudflared/config.yml 配置文件:
yaml
tunnel: <你的 Tunnel UUID>
credentials-file: /root/.cloudflared/<Tunnel UUID>.json
ingress:
- hostname: homelab.your-domain.com
service: https://homelab.local:443
originRequest:
noTLSVerify: true
httpHostHeader: homelab.your-domain.com
- hostname: homelab-esxi.your-domain.com
service: https://homelab.local:443
originRequest:
noTLSVerify: true
httpHostHeader: homelab-esxi.your-domain.com
- hostname: homelab-vcenter.your-domain.com
service: https://homelab.local:443
originRequest:
noTLSVerify: true
httpHostHeader: homelab-vcenter.your-domain.com
- hostname: gitlab.your-domain.com
service: https://homelab.local:443
originRequest:
noTLSVerify: true
httpHostHeader: gitlab.your-domain.com
- hostname: ssh.your-domain.com
service: tcp://homelab.local:2222
- service: http_status:404ingress 参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
hostname | string | 是(除默认规则外) | 匹配的外部域名。支持精确匹配或通配符。 |
service | string | 是 | 转发目标地址,格式为 协议://地址:端口。支持 http://、https://、tcp://、unix:// 或 http_status:code。 |
originRequest | object | 否 | 定义转发时的请求行为。 |
originRequest.noTLSVerify | bool | 否(建议 true) | 是否跳过对后端服务 TLS 证书的验证。 |
originRequest.httpHostHeader | string | 否 | 覆盖转发请求中的 Host 头。 |
originRequest.originServerName | string | 否 | 指定后端服务器的 SNI。 |
originRequest.tcpKeepAlive | int | 否 | 设置 TCP Keep-Alive 探测间隔(秒)。 |
2.4 配置 DNS 记录
bash
cloudflared tunnel route dns homelab homelab.your-domain.com
cloudflared tunnel route dns homelab homelab-esxi.your-domain.com
cloudflared tunnel route dns homelab homelab-vcenter.your-domain.com
cloudflared tunnel route dns homelab gitlab.your-domain.com
cloudflared tunnel route dns homelab ssh.your-domain.com设计说明:
homelab.your-domain.com作为所有 Web 服务的统一入口,通过路径(如/esxi、/vcenter)区分后端服务。GitLab 和 SSH 因协议或管理需要,保留独立子域名。这种设计在保持子域名收敛的同时,为特殊服务保留了灵活性。
第三部分:负载相关服务
3.1 启动 Nginx
bash
sudo systemctl start nginx
sudo systemctl enable nginx3.2 启动 cloudflared 隧道
bash
cloudflared tunnel --loglevel=debug run homelab💡 注意
该方式为测试时,使用的终端模式,测试完成后必须通过使用 cloudflare service 管理服务,可以参考
3.3 验证 HTTP/HTTPS 访问
bash
curl -k https://homelab-esxi.your-domain.com
curl -k https://homelab-vcenter.your-domain.com
curl -k https://gitlab.your-domain.com3.4 验证 SSH 多主机访问
bash
# 访问开发机
ssh -o ProxyCommand="cloudflared access ssh --hostname %h" [email protected]
# 访问 K8s 主节点
ssh -o ProxyCommand="cloudflared access ssh --hostname %h" [email protected]
# 访问存储节点
ssh -o ProxyCommand="cloudflared access ssh --hostname %h" [email protected]如果觉得每次都要输入 ProxyCommand 很麻烦,可以在 ~/.ssh/config 中添加配置:
ssh-config
Host *.homelab.your-domain.com
ProxyCommand cloudflared access ssh --hostname %h之后只需要执行 ssh [email protected] 即可。
第四部分:生产环境 cloudflared 服务管理
在测试内网穿透过程中我们可以使用以下命令进行调试
bash
cloudflared tunnel --loglevel=debug run homelab调试完成之后我们就需要通过 systemd 的方式来管理 cloudflared 了,cloudflared 提供了一个 service 子命令
bash
cloudflared service help具体的命令信息如下
NAME:
cloudflared service - Manages the cloudflared system service
USAGE:
cloudflared service [global options] command [command options] [arguments...]
COMMANDS:
install Install cloudflared as a system service
uninstall Uninstall the cloudflared service
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help (default: false)4.1 生成 cloudflared systemd 服务定义文件
要生成 cloudflared systemd 服务定义文件,只需要执行以下命令即可
bash
cloudflared service install具体的执行日志如下
2026-07-22T17:09:05Z INF Using Systemd
2026-07-22T17:09:07Z INF Linux service for cloudflared installed successfully它将会在 /etc/systemd/system 生成一个名为 cloudflared.service 的服务定义文件
ini
[Unit]
Description=Cloudflare Tunnel client
After=network-online.target
Wants=network-online.target
[Service]
TimeoutStartSec=15
Type=notify
ExecStart=/usr/bin/cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target4.2 验证 cloudflared 服务状态
在上面一步执行 cloudflared service install 时,cloudflared 软件包就会生成 systemd 服务定义文件并且启动 cloudflared 服务,我们可以执行以命令来验证
bash
systemctl status cloudflared将会输出类似如下的服务状态信息
● cloudflared.service - Cloudflare Tunnel client
Loaded: loaded (/etc/systemd/system/cloudflared.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2026-07-22 17:09:07 UTC; 4min 34s ago
Main PID: 36946 (cloudflared)
Tasks: 10 (limit: 9383)
Memory: 15.7M
CPU: 1.438s
CGroup: /system.slice/cloudflared.service
└─36946 /usr/bin/cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF | SUMMARY: Environment is healthy. cloudflared will use 'quic' as p>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF +------------------------------------------------------------------->
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="DNS Resolution" details="DNS Resolved successful>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="DNS Resolution" details="DNS Resolved successful>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="UDP Connectivity" details="QUIC connection succe>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="UDP Connectivity" details="QUIC connection succe>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="TCP Connectivity" details="HTTP/2 connection suc>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="TCP Connectivity" details="HTTP/2 connection suc>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck component="Cloudflare API" details="API is reachable" run_i>
Jul 22 17:09:13 cloudflared cloudflared[36946]: 2026-07-22T17:09:13Z INF precheck complete hard_fail=false run_id=a55be9f4-82cb-4244-a61e-c3a>
lines 1-20/20 (END)并且,已经设置为开机自动启动。这样,即使你服务重启,cloudflared 服务也会被 systemd 守护进行拉起,是不是非常的丝滑!这就可以愉快的内网穿透了,你开心吧~
配置文件结构总览
text
/etc/nginx/
├── conf.d/
│ ├── homelab.conf # 统一入口路径路由(核心)
│ ├── homelab-esxi.conf # exsi
│ ├── homelab-vcenter.conf # vcenter
│ ├── gitlab.conf # GitLab 独立子域名
│ ├── default.conf # 默认 404 规则
│ └── http-redirect.conf # HTTP 强制跳转 HTTPS
├── ssl/
│ ├── your-domain.com.pem
│ └── your-domain.com.key
└── nginx.conf # 主配置文件(包含 Stream 模块配置)架构优势总结
| 特性 | 说明 |
|---|---|
| 子域名收敛 | Web 服务统一使用 homelab.your-domain.com,通过路径区分服务 |
| 多 SSH 主机支持 | 通过 Nginx Stream + SNI 路由,一个端口支持无限 SSH 目标 |
| 统一接入层 | 所有流量集中经过 Nginx,配置统一 |
| 扩展性强 | 新增服务只需在 Nginx 中添加 location 块 |
| 配置清晰 | 每个服务独立配置文件,便于维护 |
工作原理简述
HTTP/HTTPS 路径路由:
- 用户访问
https://homelab.your-domain.com/esxi/ - Cloudflare 通过隧道转发给
cloudflared cloudflared转发给 Nginx- Nginx 根据
location /esxi/代理到 ESXi 后端
SSH SNI 路由:
- SSH 客户端连接
dev.homelab.your-domain.com cloudflared将 TCP 流量转发到 Nginx 的2222端口- Nginx 通过
ssl_preread读取 SNI,根据map规则转发到对应后端
需要注意的细节
- 路径尾随斜杠:
proxy_pass中的尾随斜杠会影响 URL 重写,请确保配置正确。 - Nginx SSL 证书:使用 Cloudflare Origin CA 证书,上传至
/etc/nginx/ssl/目录。 - Cloudflare IP 地址范围:需与 Cloudflare 官方 IP 列表保持一致,建议定期更新。
- 本地 DNS 解析:
/etc/hosts中定义homelab.local指向 Nginx 内网 IP。 - Stream 模块位置:Stream 配置只能放在
nginx.conf主配置文件中。
结语
这套架构,本质上是在 Homelab 内部构建了一个企业级的反向代理接入层 + TCP 智能路由层。它使得你的内网服务不再孤立,而是通过一个统一的入口对外提供访问。当你未来添加新服务时,只需在 Nginx 配置中增加一条 location 规则,整个流程保持简洁、可控。
相关文档
- 系列首篇:Homelab 从零到一实战系列
- 基础篇:从零构建 Homelab (一):在 macOS 中制作 ESXi 8.0 启动 U 盘
- 运维篇:Homelab 异常断电修复实录:从半夜关机到 vCenter 重生
- 基础远程访问:最优雅的 Homelab 远程访问方案:Cloudflare Tunnel 零信任实践
附录:相关 IP 地址参考
- ESXi 管理地址:
https://192.168.0.201 - vCenter 管理地址:
https://192.168.0.202 - Nginx 统一接入层:
https://homelab.local(Homelab 内部访问) - SSH 目标主机示例:
- 开发机:
172.16.0.10:22 - K8s 主节点:
172.16.0.20:22 - 存储节点:
172.16.0.30:22
- 开发机:
