Tailscale Funnel 是 Tailscale 提供的网关工具,和 Cloudflare Tunnel 类似,支持将流量从公网路由到 Tailscale 节点设备的服务上,如 Web 服务、静态文件、SSH 等
不过 Tailscale Funnel 当前的功能并不完善,只支持路由到一个目标地址,也不支持自定义路由;如果想路由到其他服务,需要在 Funnel 后面部署一个网关服务;在 Traefik 3.1 的版本中已经支持使用 Tailscale 作为 TLS 证书的提供方,用于将 Tailscale 域名作为 Traefik 的入口
配置 Traefik
在 Tailscale 的节点上使用 docker-compose 部署 traefik
- 创建网络
为了方便能通过 Docker 自动发现服务路由,创建一个容器共用的网络,用于 Traefik 路由到对应服务
docker network create traefik
- docker-compose.yaml
需要在 docker-compose 指定网络,并挂载 /var/run/docker.sock
,用于自动获取路由规则
networks:
traefik:
external: true
services:
traefik:
image: traefik
container_name: traefik
hostname: traefik
restart: always
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
command:
- "--configFile=/etc/traefik/traefik.yml"
ports:
- "81:80"
- "8443:443"
- "8080:8080"
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/etc/traefik/traefik.yml"
networks:
- traefik
environment:
- TZ=Asia/Shanghai
- traefik.yml
配置中定义了一个 certificatesResolvers,名称是 default
,由 tailscale 提供;同时指定了 tls 的 certResolver 名称是 default
,这样就会由 tailscale 提供 TLS 证书;完整的配置如下:
api:
dashboard: true
insecure: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
http:
tls:
certResolver: default
certificatesResolvers:
default:
tailscale: {}
ping: {}
log:
level: DEBUG
providers:
docker:
exposedByDefault: false
network: traefik
- 启动 traefik
通过 docker compose 启动容器
docker compose up -d
启动后通过 IP:PORT 访问 traefik,如 http://localhost:8080可以正常访问,说明 traefik 已经正确配置
启动 Tailscale Funnel
Funnel 相关介绍参考 Tailscale Funnel;因为 traefik 的 443 端口映射到了宿主机的 8443 端口,所以需要将请求转发到 localhost:8443,忽略证书验证,所以配置的转发地址是 https+insecure://localhost:8443
tailscale funnel --bg https+insecure://localhost:8443
启动后会提示 funnel 监听的域名以及转发的地址
Available on the internet:
https://homelab.xxx.ts.net/
|-- proxy https+insecure://localhost:8443
Funnel started and running in the background.
To disable the proxy, run: tailscale funnel --https=443 off
- 查看状态
tailscale serve status
funnel 开启,并转发到 https://localhost:8443
# Funnel on:
# - https://homelab.xxx.ts.net
https://homelab.xxx.ts.net (Funnel on)
|-- / proxy https+insecure://localhost:8443
因为还没有配置路由规则,所以此时访问 https://homelab.xxx.ts.net 会返回 404;如果 8443 端口没有监听,则会返回 502
curl -i https://homelab.xxx.ts.net
HTTP/2 404
content-length: 0
date: Sun, 13 Oct 2024 12:34:45 GMT
添加路由规则
配置证书后需要添加路由规则,将 Tailscale 的 tailnet 域名添加到路由规则中
路由到 traefik
通过给 Docker 容器添加 label 的方式配置路由规则,将根路径路由到 Traefik 的 Dashboard
- docker-compose.yaml
添加 label 配置,Host 是 Tailscale 的节点路由,格式是 节点名称+Tailnet名称
;如节点是 homelab,Tailnet名称是 xxx.ts.net
;同时还需要将 /var/run/tailscale/tailscaled.sock
挂载到容器中,用于 Tailscale 节点通信
networks:
traefik:
external: true
services:
traefik:
image: traefik
container_name: traefik
hostname: traefik
restart: always
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
command:
- "--configFile=/etc/traefik/traefik.yml"
ports:
- "81:80"
- "8443:443"
- "8080:8080"
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`homelab.xxx.ts.net`)"
- "traefik.http.routers.traefik.entrypoints=web,websecure"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=default"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/etc/traefik/traefik.yml"
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
networks:
- traefik
environment:
- TZ=Asia/Shanghai
启动后就可以在日志中看到使用了 tailscale 作为证书提供,并添加了对应的路由
...
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/provider/tailscale/provider.go:254 > Fetched certificate for domain "homelab.xxx.ts.net" providerName=default.tailscale
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{},"tcp":{},"tls":{},"udp":{}} providerName=default.tailscale
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/tls/certificate.go:131 > Adding certificate for domain(s) homelab.xxx.ts.net
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/stripprefix/strip_prefix.go:32 > Creating middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal middlewareType=StripPrefix routerName=dashboard@internal
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_stripprefix@internal routerName=dashboard@internal
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:17 > Creating middleware entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/redirect/redirect_regex.go:18 > Setting up redirection from ^(http:\/\/(\[[\w:.]+\]|[\w\._-]+)(:\d+)?)\/$ to ${1}/dashboard/ entryPointName=traefik middlewareName=dashboard_redirect@internal middlewareType=RedirectRegex routerName=dashboard@internal
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/observability/middleware.go:33 > Adding tracing to middleware entryPointName=traefik middlewareName=dashboard_redirect@internal routerName=dashboard@internal
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:22 > Creating middleware entryPointName=traefik middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:267 > Creating load-balancer entryPointName=websecure routerName=websecure-traefik@docker serviceName=traefik@docker
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/server/service/service.go:309 > Creating server entryPointName=websecure routerName=websecure-traefik@docker serverName=da81a48dc1e3586e serviceName=traefik@docker target=http://172.18.0.2:8080
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:22 > Creating middleware entryPointName=websecure middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/middlewares/recovery/recovery.go:22 > Creating middleware entryPointName=web middlewareName=traefik-internal-recovery middlewareType=Recovery
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/server/router/tcp/manager.go:237 > Adding route for homelab.xxx.ts.net with TLS options default entryPointName=web
traefik | 2024-10-13T18:41:34+08:00 DBG github.com/traefik/traefik/v3/pkg/server/router/tcp/manager.go:237 > Adding route for homelab.xxx.ts.net with TLS options default entryPointName=websecure
- 访问
此时访问 https://homelab.xxx.ts.net 域名,可以正常访问 traefik 的 dashboard,说明配置正确 (因为 Tailscale Funnel 的公网入口部署在美国,所以访问很慢,使用同一个 Tailscale 子网下的其他设备访问会更快)
路由到其他服务
如果服务在同一个 docker network 下的容器,可以直接通过 label 的方式配置路由规则;如果不在同一个网络下,可以通过文件的方式配置
启动 whoami 服务
以 whoami 服务为例,通过 docker 容器启动该服务
- docker-compose
services:
whoami:
image: "traefik/whoami"
restart: unless-stopped
container_name: "whoami"
hostname: whoami
ports:
- "8081:80"
启动后访问 8081 端口,能正常返回信息
curl -i 192.168.31.2:8081
HTTP/1.1 200 OK
Date: Sun, 13 Oct 2024 12:49:33 GMT
Content-Length: 167
Content-Type: text/plain; charset=utf-8
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 172.18.0.3
RemoteAddr: 192.168.31.2:42734
GET / HTTP/1.1
Host: 192.168.31.2:8081
User-Agent: curl/8.5.0
Accept: */*
创建路由规则
- router/whoami.yml
创建 router 文件夹,用于存放路由规则,并在 router 路径下配置 whoami 服务的路由
http:
routers:
whoami:
entryPoints:
- "websecure"
- "web"
rule: "Host(`homelab.xxx.ts.net`) && Path(`/whoami`)"
service: "whoami"
tls:
certResolver: default
services:
whoami:
loadBalancer:
servers:
- url: "http://192.168.31.2:8081"
挂载路由规则
修改 traefik 的配置文件,将 router 路径挂载到容器中
- docker-compose.yaml
services:
traefik:
image: traefik
# ...
volumes:
# ...
- "./router:/etc/traefik/router"
# ...
- traefik.yml
添加文件路由规则的配置
# ...
providers:
docker:
exposedByDefault: false
network: traefik
# 添加文件配置
file:
directory: /etc/traefik/router
watch: true
debugLogGeneratedTemplate: true
此时的路由规则如下:
访问
重新启动 traefik 容器,访问 https://homelab.xxx.ts.net/whoami
curl -i https://homelab.xxx.ts.net/whoami
可以正确转发并返回访问信息,说明路由规则配置正确
HTTP/2 200
content-type: text/plain; charset=utf-8
date: Sun, 13 Oct 2024 13:03:11 GMT
content-length: 364
Hostname: whoami
IP: 127.0.0.1
IP: 172.20.0.5
RemoteAddr: 192.168.31.2:43242
GET /whoami HTTP/1.1
Host: homelab.xxx.ts.net
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.18.0.1
X-Forwarded-Host: homelab.xxx.ts.net
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik
X-Real-Ip: 172.18.0.1