Whitelist + swarm can't get real source ip

I have a 3 node swarm with one master running traefik v2.1.2. I'm trying to do an ip whitelist to restrict access to known source ips.

unfortunately the ip source in the header always shows an ip inside the swarm ingress network. Setting depth between 1-4 doesn't help, I can't even get to the docker host ip. Obviously if I allow that ip, any ip can get in - everything comes in the ingress network. So how do I pass the real source ip?

Upgrade-Insecure-Requests: 1
X-Forwarded-For: 10.0.0.2
X-Forwarded-Host: whoareyou.mydomain.net
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: b1b6db423c5f
X-Real-Ip: 10.0.0.2

and traefik says:

msg="rejecting request &{Method:GET URL:/favicon.ico Proto:HTTP/2.0 ProtoMajor:2 ProtoMinor:0 Header:map[Accept:[image/webp,image/apng,image/*,*/*;q=0.8] Accept-Encoding:[gzip, deflate, br] Accept-Language:[en-CA,en-GB;q=0.9,en-US;q=0.8,en;q=0.7] Authorization:[Basic dmluY2VudDpkdm90cHZ2cC85Mw==] Cache-Control:[no-cache] Cookie:[PHPSESSID=c243df0fa30e8ca685ca5cc775e50c65] Dnt:[1] Pragma:[no-cache] Referer:[https://traefik.mydomain.net/dashboard/] Sec-Fetch-Dest:[image] Sec-Fetch-Mode:[no-cors] Sec-Fetch-Site:[same-origin] User-Agent:[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.16 Safari/537.36] X-Forwarded-Host:[traefik.mydomain.net] X-Forwarded-Port:[443] X-Forwarded-Proto:[https] X-Forwarded-Server:[b2dd9c5d0a7d] X-Real-Ip:[10.0.0.2]] Body:0xc0002c32f0 GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:traefik.mydomain.net Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:10.0.0.2:50459 RequestURI:/favicon.ico TLS:0xc0003acfd0 Cancel:<nil> Response:<nil> ctx:0xc0002c37a0}: \"10.0.0.2\" matched none of the trusted IPs" middlewareType=IPWhiteLister middlewareName=me-only@docker

I read that in V2 the x-Forwarded headers are untrusted by default. So I added the expected source addresses into the entrypoints. that didn't help.

here's my complete config:

version: '3.7'

services:
  traefik:
    image: traefik:v2.1.2
    command:
      - "--log.level=DEBUG"
      - "--metrics.influxdb=false"
      - "--api.dashboard=true"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik-public"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entryPoints.web.forwardedHeaders.trustedIPs=10.0.0.0/24,10.0.2.0/24,192.168.100.0/24"
      - "--entryPoints.websecure.forwardedHeaders.trustedIPs=10.0.0.0/24,10.0.2.0/24,192.168.100.0/24"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencryptresolver.acme.email=blah@blah.blah"
      - "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - 80:80
      - 443:443
    volumes:
      - traefik-certificates:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - traefik-public
    deploy:
      placement:
        constraints:
          - node.role == manager
      labels:
#dashboard
        - "traefik.enable=true"
        - "traefik.http.routers.traefik.rule=Host(`traefik.mydomain.net`)"
        - "traefik.http.routers.traefik.service=api@internal"
        - "traefik.http.routers.traefik.tls.certresolver=letsencryptresolver"
        - "traefik.http.routers.traefik.entrypoints=websecure"
        - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"
        - "traefik.http.routers.traefik.middlewares=admin"
#middleware chain called admin catches https and validates known ip and authorized user
        - "traefik.http.middlewares.admin.chain.middlewares=me-only,auth-users"
        - "traefik.http.middlewares.me-only.ipwhitelist.sourcerange=192.168.100.0/24"
        - "traefik.http.middlewares.me-only.ipwhitelist.ipstrategy.depth=2"
        - "traefik.http.middlewares.auth-users.basicauth.users=user:pwd"
#global redirect to https
        - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
        - "traefik.http.routers.http-catchall.entrypoints=web"
        - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
volumes:
  traefik-certificates:
networks:
  traefik-public:
    external: true

This is caused due to the additional "overlay" network; you can bypass this by using "host" networking. The only downside is that you can only run one instance of Traefik per node. That being said, you should really be spreading your Traefik instances across nodes anyway, so it isn't really a problem.

services:
  traefik:
    ...
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
2 Likes

thank you that is exactly right.

For what it's worth, I didn't need:

- "--entryPoints.web.forwardedHeaders.trustedIPs=10.0.0.0/24,10.0.2.0/24,192.168.100.0/24"
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=10.0.0.0/24,10.0.2.0/24,192.168.100.0/24"

nor did I need:

- "traefik.http.middlewares.me-only.ipwhitelist.ipstrategy.depth=2"

just your host port mapping and the whitelist middleware is all that was required.