Docker Swarm模式下Traefik容器IP转发致白名单失效求助
首先,你遇到的日志里显示source-IP 10.255.0.2被拒绝的问题,核心原因是Traefik没有获取到真实的公网客户端IP,而是拿到了Swarm集群内部的IP(比如节点IP或者ingress网络的内部IP),导致白名单匹配的是内部IP而非你的81.x.x.200,自然就被拦截了。
下面给你一步步的解决方案:
1. 先确保Traefik能正确拿到真实客户端IP
这是关键前提,否则白名单永远不会匹配到你的公网IP。根据Traefik版本和部署方式调整配置:
如果你用的是Traefik v2.x
在Traefik的静态配置文件(比如traefik.yml)里,给entrypoints添加forwardedHeaders配置,信任Swarm内部的网段(以及外部负载均衡的IP,如果有的话):
entryPoints: web: address: ":80" forwardedHeaders: trustedIPs: - "10.0.0.0/8" # Swarm overlay网络常用网段 - "172.16.0.0/12" - "192.168.0.0/16" # 如果用了云厂商的负载均衡,把LB的IP也加进来,比如: # - "10.1.0.0/24" websecure: address: ":443" forwardedHeaders: trustedIPs: - "10.0.0.0/8" - "172.16.0.0/12" - "192.168.0.0/16"
这个配置告诉Traefik:来自这些网段的请求里的X-Forwarded-For头是可信的,应该把这个头里的IP当作真实客户端IP。
如果你用的是Traefik v1.x
在Traefik的启动命令或者配置文件里,添加--forwardedheaders.trustedips参数,比如:
command: - "--api" - "--docker" - "--docker.swarmmode" - "--forwardedheaders.trustedips=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
2. 正确配置容器的白名单标签
根据Traefik版本调整标签:
Traefik v1.x
直接在Portainer和Visualizer的容器部署标签里保留:
- traefik.frontend.rule=Host:portainer.mydomain.com - traefik.frontend.whitelistSourceRange=81.x.x.200
- traefik.frontend.rule=Host:visualizer.mydomain.com - traefik.frontend.whitelistSourceRange=81.x.x.200
Traefik v2.x
v1的标签已经废弃,需要用中间件的方式配置:
给Portainer容器添加这些标签:
- traefik.enable=true - traefik.http.routers.portainer.rule=Host:portainer.mydomain.com - traefik.http.routers.portainer.entrypoints=websecure # 建议用HTTPS - traefik.http.routers.portainer.middlewares=portainer-ipwhitelist@docker # 定义白名单中间件 - traefik.http.middlewares.portainer-ipwhitelist.ipwhitelist.sourcerange=81.x.x.200
同理给Visualizer容器配置:
- traefik.enable=true - traefik.http.routers.visualizer.rule=Host:visualizer.mydomain.com - traefik.http.routers.visualizer.entrypoints=websecure - traefik.http.routers.visualizer.middlewares=visualizer-ipwhitelist@docker - traefik.http.middlewares.visualizer-ipwhitelist.ipwhitelist.sourcerange=81.x.x.200
3. 验证真实IP是否被正确获取
可以部署一个whoami容器来测试:
version: '3.8' services: whoami: image: traefik/whoami deploy: labels: - traefik.enable=true - traefik.http.routers.whoami.rule=Host:whoami.mydomain.com - traefik.http.routers.whoami.entrypoints=web
访问whoami.mydomain.com,你会看到类似这样的返回:
Hostname: 8a9d7f6a0b12 IP: 127.0.0.1 IP: 10.0.0.5 RemoteAddr: 10.255.0.2:34567 GET / HTTP/1.1 Host: whoami.mydomain.com User-Agent: Mozilla/5.0... Accept: text/html... X-Forwarded-For: 81.x.x.200 X-Forwarded-Host: whoami.mydomain.com X-Forwarded-Port: 80 X-Forwarded-Proto: http X-Forwarded-Server: traefik-abc123 X-Real-Ip: 81.x.x.200
如果X-Forwarded-For和X-Real-Ip显示的是你的公网IP 81.x.x.200,说明Traefik已经正确获取到真实IP了,此时白名单应该就能正常工作。
其他备选的域名级访问限制方案
如果Traefik的白名单配置还是有问题,你可以考虑:
- 在外部负载均衡层配置白名单:比如你用了云厂商的LB(阿里云SLB、AWS ELB等),直接在LB上针对
portainer.mydomain.com和visualizer.mydomain.com这两个域名,设置仅允许81.x.x.200访问。这种方式更高效,因为请求在到达Traefik之前就被过滤了。 - 结合Basic Auth+IP白名单:如果需要双重保障,可以给Portainer和Visualizer同时配置IP白名单和账号密码认证,进一步提升安全性。比如Traefik v2里添加
basicauth中间件,和IP白名单中间件一起绑定到路由上。
内容的提问来源于stack exchange,提问作者mehmatrix

