余味YUWEI
网络实践ARTICLE / 2026

双 Traefik 架构的公网穿透与内网隔离实践

通过双 Traefik 与 Tailscale 实现公网穿透、HTTPS 后端转发和内网服务访问隔离。

2026.06.11 阅读约 10 分钟 Traefik · Tailscale · HomeLab · 网络安全

1. 背景与网络架构

在 HomeLab 或企业边缘计算场景中,一种非常经典的架构是:利用一台廉价的公网云服务器 A 作为入口,与本地高性能的内网服务器 B 通过 Tailscale 组建虚拟局域网(SDN)。

  • 服务器 A(公网):内存小、带宽有限,主要运行轻量级容器,负责接收公网流量。域名 *.example.com 解析至 A 的公网 IP。
  • 服务器 B(内网):内存大、性能强(IP: 100.88.0.9),运行 GitLab、Harbor、Nacos、Code-Server 等重载服务。
  • 网关控制:两台服务器均使用 Traefik 作为反向代理。服务器 A 负责申请 *.example.com 的通配符证书,并通过自动化脚本同步至服务器 B。

2. 核心痛点:Harbor HTTPS 无限重定向

在初始配置中,服务器 A 采用 HTTP 泛解析路由将流量透传给内网 B 的 80 端口:

# 服务器 A 初始错误配置片段
services:
  ganzhou-service:
    loadbalancer:
      servers:
        - url: "http://100.88.0.9:80"

问题现象

当内网 B 的 Harbor 配置了强制 HTTPS 时,公网访问 harbor.example.com 会陷入 ERR_TOO_MANY_REDIRECTS(重定向次数过多) 的死循环;而内网机器修改 Host 直连 B 时却完全正常。

原因分析

  1. 客户端 (HTTPS) 发起请求。
  2. 服务器 A (Traefik) 接收请求,卸载 TLS 证书(TLS Termination),根据配置转为 HTTP 协议转发给内网 B(100.88.0.9:80)。
  3. 服务器 B (Harbor/Traefik) 收到 HTTP 请求,触发内部安全策略,返回 302 Redirect 要求跳转到 HTTPS。
  4. 客户端收到 302 后再次发起 HTTPS 请求,无限循环形成。

3. 全自动化全穿透解决方案

为了彻底解决重定向问题,同时实现“在内网 B 起新服务,完全无需动服务器 A 配置文件”的无感自动化体验,最终演进出 HTTP 全局兜底 + HTTPS 后端转发 的架构。

核心逻辑

服务器 A 负责公网层面的证书解密,但在通过 Tailscale 隧道转发给 B 时,不再降级为 HTTP,而是直接走 HTTPS(443端口)

方案配置实现

1. 公网服务器 A:动态/静态配置文件(一劳永逸)

http:
  serversTransports:
    # 必须信任内网自签或因域名不字面匹配的证书
    secure-backend-transport:
      insecureSkipVerify: true

  routers:
    # 全局兜底路由:优先级设为最低(priority: 1)
    # 确保 A 本地通过 Docker Label 发现的轻量小服务优先匹配
    global-fallback-to-b:
      rule: "HostRegexp(`^[a-z0-9-]+\\.yuweinfo\\.com$`)"
      entryPoints:
        - websecure
      priority: 1
      service: backend-b-https-service
      tls:
        certResolver: aliresolver

  services:
    backend-b-https-service:
      loadbalancer:
        serversTransport: secure-backend-transport
        servers:
          # 关键点:直接打到内网 B 的 HTTPS 443 端口
          - url: "https://100.88.0.9:443"

2. 内网服务器 B:应用部署(以 Harbor/GitLab 为例)

内网 B 正常运行 Traefik 并挂载同步过来的证书。后续新增任何服务,直接在 docker-compose.yml 中写 Label 即可:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.my-app.entrypoints=websecure"
  - "traefik.http.routers.my-app.rule=Host(`my-app.example.com`)"
  - "traefik.http.routers.my-app.tls=true"

架构优势

  • 公网访问:流量经 A 解密后,以 HTTPS 甩给 B,完美避开 302 重定向循环。

  • 内网拉取(大流量):内网机器修改 Host 将 harbor.example.com 指向 100.88.0.9,流量彻底绕过公网 A。利用本地同步的证书直接进行 TLS 握手,直接跑满内网千兆/万兆带宽。

4. 进阶安全:内网专属服务的“防偷渡”隔离

越权安全隐患

当在内网 B 部署了私有服务(如 nacos.example.comcode.example.com)并将其 DNS 解析到内网 IP 100.88.0.9 时,理论上外网无法解析。
但是,由于 *.example.com 依然解析在公网,攻击者只需在公网通过构造特定的请求头:

curl -k -H "Host: code.example.com" https://<服务器A的公网IP>:443

由于服务器 A 存在全局兜底路由,A 会忠实地将该请求“偷渡”转发给内网 B。内网 B 识别到 Host 匹配,直接放行,导致内网服务无防备暴露在公网

安全闭环:Traefik IP 白名单拦截

坚守“不改动公网 A”的原则,在内网服务器 B 实施拦截。利用 Traefik 的 X-Forwarded-For 信任机制与 IPAllowList 中间件,实现精准防御。

步骤一:配置内网 B 的 Traefik 识别真实客户端 IP

修改内网 B 的 Traefik 静态配置文件(traefik.yml),信任来自 Tailscale 网段的转发头:

entryPoints:
  websecure:
    address: ":443"
    forwardedHeaders:
      trustedIPs:
        - "100.64.0.0/10" # Tailscale 官方标准网段

步骤二:在动态文件 middlewares.yml 中定义白名单

http:
  middlewares:
    # 限制仅允许 Tailscale 虚拟内网 IP 访问
    tailscale-only:
      ipAllowList:
        sourceRange:
          - "100.64.0.0/10"

    # 其他常用中间件,如大文件上传限制
    code-max-body-limit:
      buffering:
        maxRequestBodyBytes: 524288000

步骤三:在 Docker Compose 中跨 Provider 调用中间件

在部署私有服务(如 code-server)时,通过 @file 后缀链式调用文件供应商中的中间件:

version: '3.8'

services:
  code-server:
    image: codercom/code-server:latest
    container_name: code-service
    labels:
      - "traefik.enable=true"
      # 1. 正常配置路由
      - "traefik.http.routers.code-web.entrypoints=websecure"
      - "traefik.http.routers.code-web.rule=Host(`code.example.com`)"
      - "traefik.http.routers.code-web.tls=true"
      
      # 2. 核心:链式调用外部 file 里的【内网白名单】与【文件大小限制】中间件
      - "traefik.http.routers.code-web.middlewares=tailscale-only@file,code-max-body-limit@file"
      
      - "traefik.http.services.code-web-service.loadbalancer.server.port=8080"
    networks:
      - traefik-net

networks:
  traefik-net:
    external: true

5. 防御效果验证与测试填坑

在测试“偷渡”防御时,若直接执行 curl -H "Host: code.example.com" https://<公网IP>,会遇到 SEC_E_UNTRUSTED_ROOT 证书不受信任报错。
这是因为 TLS 握手阶段的 SNI 扩展 与 HTTP 请求头中的 Host 相互独立。未指定 SNI 时,服务器 A 无法匹配证书,会返回默认的 Traefik 自签证书导致连接中断。

正确的测试与防御判定命令

使用 curl--resolve 参数模拟最真实的 DNS 劫持欺骗:

curl -v --resolve code.example.com:443:<公网服务器A的IP> https://code.example.com
  • 防御前:请求成功穿透 A 抵达 B,正确返回 200 OK 或服务页面。
  • 防御后:流量虽然通过 A 转发到了 B,但内网 B 的 Traefik 识别出 X-Forwarded-For 里的真实客户端 IP 为外部公网 IP,中间件直接进行安全拦截,无情返回 403 Forbidden

至此,一个兼顾高性能大流量拉取全面自动化免维护高级内网隔离安全的双 Traefik 混合网络架构完美闭环。