[GH-ISSUE #2071] Pangolin client connector shows wrong requesting IP #2066

Closed
opened 2026-04-16 09:01:26 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @thutex on GitHub (Dec 13, 2025).
Original GitHub issue: https://github.com/fosrl/pangolin/issues/2071

Originally assigned to: @miloschwartz on GitHub.

Describe the Bug

When adding a new user client (in my case, using the linux pangolin cli), you need to login and authorize this connection.
so far, so good.

however, the IP being displayed is not the one from the actual connection, but the one used to connect to the site, in my case, that of cloudflare.
i am assuming that this is related to the fact that badger also doesn't really handle cloudflare IPs correctly (as seen in another bug report)

but, since we are talking about a client here, which is running on a machine, it should be able to fetch the external IP of the connection for the machine it is running on, and provide that as the actual connecting IP, instead of (seemingly) relying on the IP given through the http connection towards the installation (as this might, for example, go through a proxy - such as in my case - and then showing the incorrect connecting IP for the client)

Environment

  • OS Type & Version: ubuntu
  • Pangolin Version: 1.13.0
  • Gerbil Version: n/a
  • Traefik Version: n/a
  • Newt Version: 1.7.0
  • Olm Version: n/a
  • Client Version: Pangolin CLI v0.1.1 (linux)

To Reproduce

simply make sure either your http connections are behind a proxy, or put your pangolin installation behind a proxy such as cloudflare.
then, connect a client.

the IP shown in the authorization window will be that of the proxy, not the external IP of the machine requesting the connection.

Expected Behavior

get the actual external IP through the client itself, instead of relying on the IP provided in the http connection that is established

Originally created by @thutex on GitHub (Dec 13, 2025). Original GitHub issue: https://github.com/fosrl/pangolin/issues/2071 Originally assigned to: @miloschwartz on GitHub. ### Describe the Bug When adding a new user client (in my case, using the linux pangolin cli), you need to login and authorize this connection. so far, so good. however, the IP being displayed is not the one from the actual connection, but the one used to connect to the site, in my case, that of cloudflare. i am assuming that this is related to the fact that badger also doesn't really handle cloudflare IPs correctly (as seen in another bug report) but, since we are talking about a client here, which is running on a machine, it should be able to fetch the external IP of the connection for the machine it is running on, and provide that as the actual connecting IP, instead of (seemingly) relying on the IP given through the http connection towards the installation (as this might, for example, go through a proxy - such as in my case - and then showing the incorrect connecting IP for the client) ### Environment - OS Type & Version: ubuntu - Pangolin Version: 1.13.0 - Gerbil Version: n/a - Traefik Version: n/a - Newt Version: 1.7.0 - Olm Version: n/a - Client Version: Pangolin CLI v0.1.1 (linux) ### To Reproduce simply make sure either your http connections are behind a proxy, or put your pangolin installation behind a proxy such as cloudflare. then, connect a client. the IP shown in the authorization window will be that of the proxy, not the external IP of the machine requesting the connection. ### Expected Behavior get the actual external IP through the client itself, instead of relying on the IP provided in the http connection that is established
GiteaMirror added the non-critical bug label 2026-04-16 09:01:26 -05:00
Author
Owner

@miloschwartz commented on GitHub (Dec 16, 2025):

Hi there, the IP that is displayed when you authorize the device is determined based on the following:

When you initiate the login flow, the device client makes an API request to the server to generate the 8‑digit code you see. When it makes this request, the server stores the IP address of the HTTP request.

This is so you can see which device is requesting access before permission is granted. At this stage, no device or credentials are configured yet, so this initial HTTP request is the only point where we can determine the origin of the request. We can’t fetch the IP from any other connection type because nothing else has been established yet.

You’re right that the behavior you’re seeing is because Cloudflare proxying is enabled in front of Traefik. Cloudflare sets special headers that need to be processed by Traefik before they’re forwarded to the Pangolin API. Also, Badger isn’t really involved here — the request goes directly to the Pangolin server API. Badger only applies to resources served through Pangolin, not Pangolin itself.

Luckily, the fix is pretty easy. You can add this Traefik plugin to parse the Cloudflare headers and apply it to the web / websecure entrypoints:

https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel

experimental:
  plugins:
    badger:
      moduleName: github.com/fosrl/badger
      version: v1.2.0
    cloudflarewarp:
      moduleName: github.com/BetterCorp/cloudflarewarp
      version: v1.3.0

entryPoints:
  web:
    address: ':80'
  websecure:
    address: ':443'
    http:
      middlewares:
        - cloudflarewarp@file
      tls:
        certResolver: letsencrypt
    transport:
      respondingTimeouts:
        readTimeout: 30m

Then set trust_proxy: 2 in the Pangolin config. This tells the API server to trust the second‑level proxy (Traefik is proxy 1, Cloudflare is proxy 2).

https://docs.pangolin.net/self-host/advanced/config-file#param-trust-proxy

This works on my test instance, but let me know if you’re still seeing the wrong IP after setting this up.

<!-- gh-comment-id:3658487665 --> @miloschwartz commented on GitHub (Dec 16, 2025): Hi there, the IP that is displayed when you authorize the device is determined based on the following: When you initiate the login flow, the device client makes an API request to the server to generate the 8‑digit code you see. When it makes this request, the server stores the IP address of the HTTP request. This is so you can see which device is requesting access before permission is granted. At this stage, no device or credentials are configured yet, so this initial HTTP request is the only point where we can determine the origin of the request. We can’t fetch the IP from any other connection type because nothing else has been established yet. You’re right that the behavior you’re seeing is because Cloudflare proxying is enabled in front of Traefik. Cloudflare sets special headers that need to be processed by Traefik before they’re forwarded to the Pangolin API. Also, Badger isn’t really involved here — the request goes directly to the Pangolin server API. Badger only applies to resources served through Pangolin, not Pangolin itself. Luckily, the fix is pretty easy. You can add this Traefik plugin to parse the Cloudflare headers and apply it to the web / websecure entrypoints: https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel ``` experimental: plugins: badger: moduleName: github.com/fosrl/badger version: v1.2.0 cloudflarewarp: moduleName: github.com/BetterCorp/cloudflarewarp version: v1.3.0 entryPoints: web: address: ':80' websecure: address: ':443' http: middlewares: - cloudflarewarp@file tls: certResolver: letsencrypt transport: respondingTimeouts: readTimeout: 30m ``` Then set `trust_proxy: 2` in the Pangolin config. This tells the API server to trust the second‑level proxy (Traefik is proxy 1, Cloudflare is proxy 2). https://docs.pangolin.net/self-host/advanced/config-file#param-trust-proxy This works on my test instance, but let me know if you’re still seeing the wrong IP after setting this up.
Author
Owner

@miloschwartz commented on GitHub (Dec 16, 2025):

Note: I'm going to close this issue, but if I am missing something we can re-open it. I added to the docs here: https://docs.pangolin.net/self-host/advanced/cloudflare-proxy

<!-- gh-comment-id:3658523127 --> @miloschwartz commented on GitHub (Dec 16, 2025): Note: I'm going to close this issue, but if I am missing something we can re-open it. I added to the docs here: https://docs.pangolin.net/self-host/advanced/cloudflare-proxy
Author
Owner

@AstralDestiny commented on GitHub (Dec 16, 2025):

If you don't want to put a plugin you can still do,

x-trusted-ips: &trustedIPs # this
        # Cloudflare V4
        - 173.245.48.0/20
        - 103.21.244.0/22
        - 103.22.200.0/22
        - 103.31.4.0/22
        - 141.101.64.0/18
        - 108.162.192.0/18
        - 190.93.240.0/20
        - 188.114.96.0/20
        - 197.234.240.0/22
        - 198.41.128.0/17
        - 162.158.0.0/15
        - 104.16.0.0/13
        - 104.24.0.0/14
        - 172.64.0.0/13
        - 131.0.72.0/22
        # Cloudflare V6
        - 2400:cb00::/32
        - 2606:4700::/32
        - 2803:f800::/32
        - 2405:b500::/32
        - 2405:8100::/32
        - 2a06:98c0::/29
        - 2c0f:f248::/32

entryPoints:
  http:
    address: ":80"
    http:
      middlewares:
        - middleware-crowdsec-bouncer@file
      redirections:
        entryPoint:
          to: https
          scheme: https
          permanent: true
    forwardedHeaders: # this
      trustedIPs: *trustedIPs 
  https:
    address: ":443"
    asDefault: true
    http3:
      advertisedPort: 443
    # transport:
    #   respondingTimeouts:
    #     readTimeout: "30m"
    http:
      middlewares:
        - middleware-crowdsec-bouncer@file
        - middleware-secure-headers@file
      tls:
         options: default
         certResolver: dns
    forwardedHeaders: # this
      trustedIPs: *trustedIPs 
 

But still follow the,

server:
  external_port: 3000
  internal_port: 3001
  next_port: 3002
  internal_hostname: pangolin
  session_cookie_name: p_session_token
  resource_access_token_param: p_token
  resource_session_request_param: p_session_request
  trust_proxy: 2 # this
<!-- gh-comment-id:3658581191 --> @AstralDestiny commented on GitHub (Dec 16, 2025): If you don't want to put a plugin you can still do, ``` x-trusted-ips: &trustedIPs # this # Cloudflare V4 - 173.245.48.0/20 - 103.21.244.0/22 - 103.22.200.0/22 - 103.31.4.0/22 - 141.101.64.0/18 - 108.162.192.0/18 - 190.93.240.0/20 - 188.114.96.0/20 - 197.234.240.0/22 - 198.41.128.0/17 - 162.158.0.0/15 - 104.16.0.0/13 - 104.24.0.0/14 - 172.64.0.0/13 - 131.0.72.0/22 # Cloudflare V6 - 2400:cb00::/32 - 2606:4700::/32 - 2803:f800::/32 - 2405:b500::/32 - 2405:8100::/32 - 2a06:98c0::/29 - 2c0f:f248::/32 entryPoints: http: address: ":80" http: middlewares: - middleware-crowdsec-bouncer@file redirections: entryPoint: to: https scheme: https permanent: true forwardedHeaders: # this trustedIPs: *trustedIPs https: address: ":443" asDefault: true http3: advertisedPort: 443 # transport: # respondingTimeouts: # readTimeout: "30m" http: middlewares: - middleware-crowdsec-bouncer@file - middleware-secure-headers@file tls: options: default certResolver: dns forwardedHeaders: # this trustedIPs: *trustedIPs ``` But still follow the, ``` server: external_port: 3000 internal_port: 3001 next_port: 3002 internal_hostname: pangolin session_cookie_name: p_session_token resource_access_token_param: p_token resource_session_request_param: p_session_request trust_proxy: 2 # this ```
Author
Owner

@thutex commented on GitHub (Dec 16, 2025):

i already had the trustedip's setting, but missed the trust_proxy setting to 2.
updating that does give me the actual IP through the client as well, so this issue is indeed solved for my use case.

thanks @miloschwartz & @AstralDestiny

<!-- gh-comment-id:3659712698 --> @thutex commented on GitHub (Dec 16, 2025): i already had the trustedip's setting, but missed the trust_proxy setting to 2. updating that does give me the actual IP through the client as well, so this issue is indeed solved for my use case. thanks @miloschwartz & @AstralDestiny
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#2066