ipWhiteList with excludedIPs setting will result in empty IP address when there is 1 IP address in X-Forwarded-For Header

Description

Hey guys, I noticed that when there is 1 IP address in the X-Forwarded-For header and I am using the ipStrategy.excludedIPs or ipStrategy.depth=1 setting, it will always return an empty IP address. This results in me getting a forbidden because the empty IP address is not in the IP whitelist source range.

Current Behavior

Setup

My setup is fairly simple, I am using traefik to forward my requests to the containious/whoami docker container. This docker container will show the request headers that it received from traefik.

In my ipWhitelist middleware I put "0.0.0.0/0 to white list all IPv4 addresses and 0000:0000::/0 to whitelist all IPv6 addresses.

[Issue] Accessing Traefik directly - 1 IP address in X-Forwarded-For header

When I access traefik directly (meaning no tunnels in between and I am inside the same network) I only have 1 IP address in the X-Forwarded-For header.

When I have 1 IP in the X-Forwarded-For header and I set the excludedIPs or the depth=1 setting, I get a 403 forbidden response. I am expecting a 200 response instead.

Headers

X-Forwarded-For: 192.168.1.123

Middleware Setting

    known-ips-internal:
      ipWhiteList:
        sourceRange:
          - "0.0.0.0/0"  # Whitelisting ipv4 for testing
          - "0000:0000::/0"  # Whitelisting all ipv6 for testing
        ipStrategy:
          excludedIPs:
            - 172.16.0.0/12
          # depth: 1  # this setting has the same behavior if enabled

Logs

time="2023-02-09T22:32:04-08:00" level=debug msg="Rejecting IP : empty IP address" middlewareName=known-ips-internal@file middlewareType=IPWhiteLister

[Behaving as expected] Accessing Traefik via cloudflare tunnel - 2 IP addresses in X-Forwarded-For header

When I accessing via the cloudflare tunnel I have 2 IPs in the X-Forwarded-For header. It will look like this: X-Forwarded-For: <public IP>, <cloudflared container IP>. When I use the excludedIPs setting it will select the correct IP address which is the public IP address.

Headers

X-Forwarded-For: 111.222.333.444, 172.22.0.123

Middleware Setting

    known-ips-internal:
      ipWhiteList:
        sourceRange:
          - "0.0.0.0/0"  # Whitelisting ipv4 for testing
          - "0000:0000::/0"  # Whitelisting all ipv6 for testing
        ipStrategy:
          excludedIPs:
            - 172.16.0.0/12

Logs

time="2023-02-09T22:27:15-08:00" level=debug msg="Accepting IP 111.222.333.444" middlewareName=known-ips-internal@file middlewareType=IPWhiteLister

Expected Behavior

For the scenario I listed above when I have 1 IP address in the X-Forwarded-For header and I am using a different excludedIPs, I expect it to use that IP address to evaluate for the IP whitelist.

i'm having the same issue,

i have an nginx RP that on the host and traefik as a docker container whith middleware configured to allow 192.168.0.0/16 and exludedIp of 172.20.0.1

Nginx forward the requests to traefik (and add X-Forwarded-For and X-Real-IP headers)
but the middleware reject the request

even tough there is already the header in the request

tcpdump on docker interface :

 172.20.0.1.55706 > 172.20.0.6.http: Flags [P.], cksum 0x6421 (incorrect -> 0x09bc), seq 1:3020, ack 1, win 502, options [nop,nop,TS val 1888465225 ecr 593107050], length 3019: HTTP, length: 3019
        GET /jenkins HTTP/1.0
        Host: *******
        X-Real-IP: 192.168.0.2
        X-Forwarded-For: 192.168.0.2
        X-Forwarded-Proto: http
        X-Forwarded-Login: *********
        X-Forwarded-Name: ********
        X-Forwarded-User: **********
        Connection: close

if i disable the ipStrategy i get the docker ip as remoteAddr (172.20.0.1) which is not whitelisted


i saw also this on the middleware doc

As a middleware, whitelisting happens before the actual proxying to the backend takes place. In addition, the previous network hop only gets appended to X-Forwarded-For during the last stages of proxying, i.e. after it has already passed through whitelisting. Therefore, during whitelisting, as the previous network hop is not yet present in X-Forwarded-For, it cannot be matched against sourceRange.

not sure if i understand the bold part

Hej,

for people (like me) not understanding the issue. See the Github Issue, where it is explained in more detail:

As described in the documentation, when no IP strategy is configured the remote address is used. This is why everything is working when the client is sending the request directly to Traefik.
When the excludedIPs option is configured, the middleware uses the X-Forwarded-For to get the client IP. As there is no hop between the client and Traefik, which will likely add the X-Forwarded-For header, Traefik will receive an empty one and return a 403.
When proxying the request to the backend, Traefik will add the X-Forwarded-For to the request, which will contain the remote address, the client IP in your example. This is why you have the X-Forwarded-For header with only one IP in the whoami response.
To be able to do what you want, you will have to define two routers, which can use different IP allow list middleware configuration in function of the X-Forwarded-For header presence.