Unable to get real ip of clients for my setup (CF, CGNat, IPv6)

It seems there's tons of posts about this problem, but somehow none of the mentioned solutions (or attempts at them) were helping me, so I'm hoping that with this thread maybe someone can tell me what my problem is (or at least where it lies)


My Setup:

I'm using Unraid for my Homeserver with Traefik as the Reverse Proxy.
Since I'm behind a CGNat I'm using the IPv6 address of my server in my DNS records at Cloudflare.
My router exposes only ports 80 & 443 and only for my server.

So far everything's working well, I can reach my services from the outside and use them.


The Problem:
I have a couple of services that I'd like to be able to tell whether they're being accessed from inside my servers network or from the outside.

Trying to access the whoami service from inside my local network yields a result like this (reduced to the important lines)

Hostname: ac0c471dff5e
IP: 127.0.0.1
IP: 172.18.0.50
RemoteAddr: 172.18.0.39:47574
GET / HTTP/1.1
Host: who.domain.com
# ...
X-Forwarded-For: 192.168.178.133 # Real Client IP
# ...
X-Real-Ip: 192.168.178.133 # Real Client IP

When doing the same from outside, aka a remote network, I get something like this: (for gray-clouded, unproxied DNS entries)

Hostname: ac0c471dff5e
IP: 127.0.0.1
IP: 172.18.0.50
RemoteAddr: 172.18.0.39:47574
GET / HTTP/1.1
Host: who.domain.com
# ...
X-Forwarded-For: 172.18.0.1 # Docker Network IP
# ...
X-Real-Ip: 172.18.0.1 # Docker Network IP

While for orange-clouded, proxied DNS entries I get the following for X-Forwarded-For and X-Real-Ip (using cloudflarewarp plugin)

# ...
X-Forwarded-For: 2a02:xxx:xxx:xxx:xxxx:xxxx:xxxx:xxxx, 172.18.0.1 # Real IPv6 addr, Docker Network IP
# ...
X-Real-Ip: 172.18.0.1 # Docker Network IP

This still doesn't work with my ip-whitelist, since the docker network IP is still in it..


My setup:
the compose.yml file

services:
  traefik:
    container_name: traefik
    image: traefik
    user: ${PUID}:${PGID}
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped
    ports: # I used the standard list of 80:80 & 443:443 before, changing to this didn't help sadly
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
      - 8080:8080 # Management WebUI
    volumes: ... # volumes for config and logs
    environment: ... # variables for CF DNS Challenge
    extra_hosts:
      - host.docker.internal:${TRAEFIK_EXTRA_HOST_IP} # so Traefik can detect Services running on the host network mode (Adguard, Home Assistant, ...)
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.entrypoints=webSecure
      - traefik.http.routers.api.rule=Host(`${TRAEFIK_SUBDOMAIN}.${DOMAIN}`)
      - traefik.http.routers.api.service=api@internal
      - traefik.http.routers.api.middlewares=local-ipAllowList@file

The relevant traefik.yml part:

...
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: webSecure
          scheme: https
    forwardedHeaders:
      insecure: true # To try and get real IP, this didn't help either sadly

  webSecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt
        domains:
          - main: "domain.com"
          - sans:
              - "*.domain.com"
      middlewares:
        - securityHeaders@file
    forwardedHeaders:
      insecure: true # To try and get real IP, this didn't help either sadly

And the fileConfig.yml

http:
    local-ipAllowList: .. # omitted for brevity

    securityHeaders:
      headers:
        customResponseHeaders:
          X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
          server: ""
        sslProxyHeaders:
          X-Forwarded-Proto: https
        referrerPolicy: "same-origin"
        hostsProxyHeaders:
          - "X-Forwarded-Host"
        customRequestHeaders:
          X-Forwarded-Proto: "https"
        contentTypeNosniff: true
        browserXssFilter: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsSeconds: 63072000
        stsPreload: true

    auth:
      forwardauth:
        address: http://authentik-server:9000/outpost.goauthentik.io/auth/traefik
        trustForwardHeader: true
        authResponseHeaders:
          - X-authentik-username
          - X-authentik-groups
          - X-authentik-email
          - X-authentik-name
          - X-authentik-uid
          - X-authentik-jwt
          - X-authentik-meta-jwks
          - X-authentik-meta-outpost
          - X-authentik-meta-provider
          - X-authentik-meta-app
          - X-authentik-meta-version
          - authorization

    gzip:
      compress: {}

    rate-limit:
      rateLimit:
        average: 100
        period: 1
        burst: 100

Let me know if you need any more information and thanks in advance for taking time to help me :slight_smile:

After experimenting a bit more, one way I was able to get the correct IP was by having both Traefik and Plex run in network_mode: host.

Obviously I'd rather avoid doing that, since then I'm not able to rely on Traefik's label system for easy access to containers anymore.

Is there any way to avoid having to run Traefik in network_mode: host?
As mentioned in my initial post, I already tried just exposing the ports in host mode, but that didn't work

I thought there should be no difference between

and

network_mode: host

except that the last enables all ports and is therefore a bit more un-controlled.

In general it works for us using individual ports in host mode, like you described.

could that potentially be a Unraid/Docker specific problem then?

Thanks for sharing. It helps me a lot.

Anyone else got an idea?