[GH-ISSUE #1133] Stuck on port 80/443 (and port mismatch with resources) #6573

Open
opened 2026-04-25 15:28:40 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @Error-Gap on GitHub (Jul 26, 2025).
Original GitHub issue: https://github.com/fosrl/pangolin/issues/1133

There doesn't seem to be an easy way to get Pangolin to run properly on alternate ports. Simple changing the mapping does not work.
For example, you could have in docker-compose.yml

    ports:
      - 8443:443 # Port for traefik because of the network_mode
      - 8080:80 # Port for traefik because of the network_mode

OR, in docker-compose.yml

    ports:
      - 8443:8443 # Port for traefik because of the network_mode
      - 8080:8080 # Port for traefik because of the network_mode

and traefik_config.yml

entryPoints:
  web:
    address: "8080"
  websecure:
    address: ":8443"

This allows access to the admin interface on the alternate HTTPS port. However, once you create a resource, it will never match up and results in an error such as "Unauthorized" (and "access denied" in the logs). This is because the check will also compare the alternate port, while the resource entry apparently expects the default HTTPS port (443). You also connect specify a port in the WebUI for the resource (colon isn't an allow character).

Error Snippet (IP's and domain masked)

pangolin    | 2025-07-26T03:38:51.241Z [debug]: Verify session: Badger sent {"sessions":{},"originalRequestURL":"https://alt.mysite.com:8443/","scheme":"","host":"alt.mysite.com:8443","path":"/","method":"GET","tls":true,"requestIp":"1.2.3.4:41409","headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"en-CA,en-US;q=0.7,en;q=0.3","Priority":"u=0, i","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Sec-Gpc":"1","Te":"trailers","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0","X-Forwarded-Host":"alt.mysite.com:8443","X-Forwarded-Port":"8443","X-Forwarded-Proto":"https","X-Forwarded-Server":"7d0cda26f0c","X-Real-Ip":"1.2.3.4"}}
pangolin    | 2025-07-26T03:38:51.244Z [debug]: Request IP: {"requestIp":"1.2.3.4:41409"}
pangolin    | 2025-07-26T03:38:51.246Z [debug]: Client IP: {"clientIp":"6.7.8.9"}
pangolin    | 2025-07-26T03:38:51.249Z [debug]: Resource not found {"0":"a","1":"l","2":"t","3":".","4":"m","5":"y","6":"s","7":"i","8":"t","9":"e","10":".","11":"c","12":"o","13":"m","14":"8","15":"4","16":"4","17":"3"}
pangolin    | 2025-07-26T03:38:51.250Z [debug]: {"data":{"valid":false},"success":true,"error":false,"message":"Access denied","status":200}

Suggestion:

  • Strip the port when matching against a resource
    or
  • Allow users to enter a port with the resource

(there are many reasons to run on ports other than 80+443, including firewall issues/configs or if an existing webserver etc is also binding that port)

Originally created by @Error-Gap on GitHub (Jul 26, 2025). Original GitHub issue: https://github.com/fosrl/pangolin/issues/1133 There doesn't seem to be an easy way to get Pangolin to run properly on alternate ports. Simple changing the mapping does not work. For example, you could have in docker-compose.yml ``` ports: - 8443:443 # Port for traefik because of the network_mode - 8080:80 # Port for traefik because of the network_mode ``` OR, in docker-compose.yml ``` ports: - 8443:8443 # Port for traefik because of the network_mode - 8080:8080 # Port for traefik because of the network_mode ``` and traefik_config.yml ``` entryPoints: web: address: "8080" websecure: address: ":8443" ``` This allows access to the admin interface on the alternate HTTPS port. However, once you create a resource, it will never match up and results in an error such as "Unauthorized" (and "access denied" in the logs). This is because the check will also compare the alternate port, while the resource entry apparently expects the default HTTPS port (443). You also connect specify a port in the WebUI for the resource (colon isn't an allow character). Error Snippet (IP's and domain masked) ``` pangolin | 2025-07-26T03:38:51.241Z [debug]: Verify session: Badger sent {"sessions":{},"originalRequestURL":"https://alt.mysite.com:8443/","scheme":"","host":"alt.mysite.com:8443","path":"/","method":"GET","tls":true,"requestIp":"1.2.3.4:41409","headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"en-CA,en-US;q=0.7,en;q=0.3","Priority":"u=0, i","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Sec-Gpc":"1","Te":"trailers","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0","X-Forwarded-Host":"alt.mysite.com:8443","X-Forwarded-Port":"8443","X-Forwarded-Proto":"https","X-Forwarded-Server":"7d0cda26f0c","X-Real-Ip":"1.2.3.4"}} pangolin | 2025-07-26T03:38:51.244Z [debug]: Request IP: {"requestIp":"1.2.3.4:41409"} pangolin | 2025-07-26T03:38:51.246Z [debug]: Client IP: {"clientIp":"6.7.8.9"} pangolin | 2025-07-26T03:38:51.249Z [debug]: Resource not found {"0":"a","1":"l","2":"t","3":".","4":"m","5":"y","6":"s","7":"i","8":"t","9":"e","10":".","11":"c","12":"o","13":"m","14":"8","15":"4","16":"4","17":"3"} pangolin | 2025-07-26T03:38:51.250Z [debug]: {"data":{"valid":false},"success":true,"error":false,"message":"Access denied","status":200} ``` Suggestion: - Strip the port when matching against a resource or - Allow users to enter a port with the resource (there are many reasons to run on ports other than 80+443, including firewall issues/configs or if an existing webserver etc is also binding that port)
GiteaMirror added the enhancement label 2026-04-25 15:28:40 -05:00
Author
Owner

@Error-Gap commented on GitHub (Jul 26, 2025):

Traced this down to the following in Badger

        logger.debug("Client IP:", { clientIp });

        let cleanHost = host;
        // if the host ends with :443 or :80 remove it
        if (cleanHost.endsWith(":443")) {
            cleanHost = cleanHost.slice(0, -4);
        } else if (cleanHost.endsWith(":80")) {
            cleanHost = cleanHost.slice(0, -3);

The above appears to slice off the port from the end of the FQDN, but only if the port is 443 or 80, which means it is going to not respect the entryPoint ports as defined in the traefik config, port bindings defined in the docker-compose file, traffic NAT'ing, etc

Rather than hardcoding 443 or 80, it might make more sense to slice off any numeric values following after the first colon

<!-- gh-comment-id:3121340386 --> @Error-Gap commented on GitHub (Jul 26, 2025): Traced this down to the following in Badger ``` logger.debug("Client IP:", { clientIp }); let cleanHost = host; // if the host ends with :443 or :80 remove it if (cleanHost.endsWith(":443")) { cleanHost = cleanHost.slice(0, -4); } else if (cleanHost.endsWith(":80")) { cleanHost = cleanHost.slice(0, -3); ``` The above appears to slice off the port from the end of the FQDN, but _only_ if the port is 443 or 80, which means it is going to not respect the entryPoint ports as defined in the traefik config, port bindings defined in the docker-compose file, traffic NAT'ing, etc Rather than hardcoding 443 or 80, it might make more sense to slice off any numeric values following after the first colon
Author
Owner

@Error-Gap commented on GitHub (Jul 28, 2025):

Have forked out for some experimentation over the weekend. Altering the updated section to slice off any "port" section of the host does work in terms of having the appropriate resource matched and auth page show up, but will still run into errors elsewhere in the code where the current port being used does not match the resource entry (in particular, the post successful-auth redirect will send the user back to the HTTPS port).

Still running some trials on this but if I can get it working in the fork I will hopefully be able to provide the appropriate updates (so no need for the project devs to spend time looking into this as of current).

<!-- gh-comment-id:3125800483 --> @Error-Gap commented on GitHub (Jul 28, 2025): Have forked out for some experimentation over the weekend. Altering the updated section to slice off any "port" section of the host _does_ work in terms of having the appropriate resource matched and auth page show up, but will still run into errors elsewhere in the code where the current port being used does not match the resource entry (in particular, the post successful-auth redirect will send the user back to the HTTPS port). Still running some trials on this but if I can get it working in the fork I will hopefully be able to provide the appropriate updates (so no need for the project devs to spend time looking into this as of current).
Author
Owner

@oschwartz10612 commented on GitHub (Jul 28, 2025):

Ahh yep that makes sense I would 100 agree thats the issue in Badger. Also take a look at verifySession.ts but I think that is probably good.

Thanks for finding this. If you want to open a PR that would be incredible!

<!-- gh-comment-id:3128358251 --> @oschwartz10612 commented on GitHub (Jul 28, 2025): Ahh yep that makes sense I would 100 agree thats the issue in Badger. Also take a look at `verifySession.ts` but I think that is probably good. Thanks for finding this. If you want to open a PR that would be incredible!
Author
Owner

@Error-Gap commented on GitHub (Jul 29, 2025):

Will do. I've resolved the issue in
server/routers/badger/verifySession.ts

There's another in
pangolin/src/app/auth/resource/[resourceId]/page.tsx

Just sorting that out then I'll request a pull for the forked updates, assuming nothing else comes up.

<!-- gh-comment-id:3130855040 --> @Error-Gap commented on GitHub (Jul 29, 2025): Will do. I've resolved the issue in server/routers/badger/verifySession.ts There's another in pangolin/src/app/auth/resource/[resourceId]/page.tsx Just sorting that out then I'll request a pull for the forked updates, assuming nothing else comes up.
Author
Owner

@Linuturk commented on GitHub (Jul 29, 2025):

Wanted to say thanks for taking a swing at supporting alternative ports. I just tried to setup Pangolin on my server that's already running web services on 80 and 443, and hit this issue. Looking forward to your fixes.

<!-- gh-comment-id:3134235053 --> @Linuturk commented on GitHub (Jul 29, 2025): Wanted to say thanks for taking a swing at supporting alternative ports. I just tried to setup Pangolin on my server that's already running web services on 80 and 443, and hit this issue. Looking forward to your fixes.
Author
Owner

@Error-Gap commented on GitHub (Jul 30, 2025):

Finally looking like it works on the alt-port, but had to make changes in:

  • server/routers/badger/verifySession.ts : verifyResourceSession() allowed stripping port from the cleanHost variable (when it's something other than just 80 or 443). This allows the resource page to show up a login, but the page will still throw you back to port 443 https
  • src/app/auth/resource/[resourceId]/page.tsx : Had to account for the current port when doing the comparison between serverResourceHost and redirectHost so that redirectURL would be read in with the alternate port. No more being thrown back to 443, but successful auth was resulting in infinite redirects
  • exchangeSession.ts : Updated exchangeSession() to do the same port stripping as in verifyResourceSession() so that the session cookie matches up when read later by extractResourceSessionToken()

This seems to be successful thus far in that accessing a resource URL properly forwards to the login page, which then redirects back to the resource URL and allows access to the remote resource.

I will need to do a bit more testing to ensure that this doesn't visible break something elsewhere, as well as revert the relevant files back to versions without the crap-ton of debugging outputs I added to trace flow. :-)

Once I've got a clean+working version and done a few more tests I'll commit it back to my fork and then submit a PR to have the changes pulled back into the proper project (this one)

<!-- gh-comment-id:3135127238 --> @Error-Gap commented on GitHub (Jul 30, 2025): Finally looking like it works on the alt-port, but had to make changes in: - server/routers/badger/verifySession.ts : verifyResourceSession() allowed stripping port from the cleanHost variable (when it's something other than just 80 or 443). This allows the resource page to show up a login, but the page will still throw you back to port 443 https - src/app/auth/resource/[resourceId]/page.tsx : Had to account for the current port when doing the comparison between serverResourceHost and redirectHost so that redirectURL would be read in with the alternate port. No more being thrown back to 443, but successful auth was resulting in infinite redirects - exchangeSession.ts : Updated exchangeSession() to do the same port stripping as in verifyResourceSession() so that the session cookie matches up when read later by extractResourceSessionToken() This seems to be successful thus far in that accessing a resource URL properly forwards to the login page, which then redirects back to the resource URL and allows access to the remote resource. I will need to do a bit more testing to ensure that this doesn't visible break something elsewhere, as well as revert the relevant files back to versions without the crap-ton of debugging outputs I added to trace flow. :-) Once I've got a clean+working version and done a few more tests I'll commit it back to my fork and then submit a PR to have the changes pulled back into the proper project (this one)
Author
Owner

@Error-Gap commented on GitHub (Jul 31, 2025):

Hi @oschwartz10612

Testing seems OK:

While running on an alternate bound port, I was able to:

  • Login to admin control-panel and create web resources

Then, in a separate browser/user:

  • Accessed the associated subdomain on the alternate port
  • Presented with login page, and logged in successfully
  • Was able to access the page from the remote resource(s) the user was granted access to
  • Got the expected "permission denied" error from resource pages the user was not granted access to

Pretty quick smoke-test but it seems good so I've submitted the the PR

<!-- gh-comment-id:3138542249 --> @Error-Gap commented on GitHub (Jul 31, 2025): Hi @oschwartz10612 Testing seems OK: While running on an alternate bound port, I was able to: - Login to admin control-panel and create web resources Then, in a separate browser/user: - Accessed the associated subdomain on the alternate port - Presented with login page, and logged in successfully - Was able to access the page from the remote resource(s) the user was granted access to - Got the expected "permission denied" error from resource pages the user was not granted access to Pretty quick smoke-test but it seems good so I've submitted the the PR
Author
Owner

@bodleytunes commented on GitHub (Jul 31, 2025):

With OpenZITI things like this (the ports various services listen on), can be set via ENV vars for specific ports set in the .env file or the docker compose file.

<!-- gh-comment-id:3138944681 --> @bodleytunes commented on GitHub (Jul 31, 2025): With OpenZITI things like this (the ports various services listen on), can be set via ENV vars for specific ports set in the .env file or the docker compose file.
Author
Owner

@jasperweyne commented on GitHub (Jan 3, 2026):

I'm successfully running Pangolin on a different port, by modifying a couple of files. For future reference, these steps worked for me:

  • First, I installed Pangolin normally with the installer. Note that during the installation, I didn't specify the custom port anywhere yet, so if ports 80 and 443 are unavailable for you, your mileage may vary here.
  • Then, I had to ofcourse modify the open port. I'll be running it on port 9443. Since a custom port is used, the insecure port serves no purpose anymore (also not for ACME, see below).
# file: docker-compose.yml
services:
  gerbil:
    ports:
      - 51820:51820/udp
      - 21820:21820/udp
      - 9443:443 # note the change here
      #- 80:80 # this one is commented out entirely
  • After that, I had to modify the Pangolin config, as it seems that absolute URI references are used in the UI code instead of relative references. The required modifications were:
# file: config/config.yml
app:
    dashboard_url: "https://[pangolin domain]:9443"
server:
    cors:
        origins: ["https://[pangolin domain]:9443"]
  • Lastly, if the server isn't running on ports 80/443, LetsEncrypt is (currently) only able to issue HTTPS certificates with the DNS-01 ACME challenge, instead of the default HTTP-01 challenge, so the config needs to be updated accordingly. Pangolin uses Traefik, which uses the lego library for ACME challenges. Since configuration depends on your DNS provider, see the lego documentation for supported providers and the configuration. To change the challenge type, I modified two files:
# file: config/traefik/traefik_config.yml
certificatesResolvers:
  letsencrypt:
    acme:
      # remove the httpChallenge key entirely here
      dnsChallenge:
        provider: [provider] # the code/cli name of the provider as specified in the lego documentation
# file: docker-compose.yml
services:
  traefik:
    environment:
      # if the lego documentation specifies environment variables, put them here
      # example:
      DNS_PROVIDER_API_KEY: api_key_value
  • And of course, don't forget to restart the server with docker compose down and then docker compose up -d

These steps resulted in a successful installation for me.

<!-- gh-comment-id:3707091009 --> @jasperweyne commented on GitHub (Jan 3, 2026): I'm successfully running Pangolin on a different port, by modifying a couple of files. For future reference, these steps worked for me: - First, I installed Pangolin normally with the installer. Note that during the installation, I didn't specify the custom port anywhere yet, so if ports 80 and 443 are unavailable for you, your mileage may vary here. - Then, I had to ofcourse modify the open port. I'll be running it on port 9443. Since a custom port is used, the insecure port serves no purpose anymore (also not for ACME, see below). ```yml # file: docker-compose.yml services: gerbil: ports: - 51820:51820/udp - 21820:21820/udp - 9443:443 # note the change here #- 80:80 # this one is commented out entirely ``` - After that, I had to modify the Pangolin config, as it seems that absolute URI references are used in the UI code instead of relative references. The required modifications were: ```yml # file: config/config.yml app: dashboard_url: "https://[pangolin domain]:9443" server: cors: origins: ["https://[pangolin domain]:9443"] ``` - Lastly, if the server isn't running on ports 80/443, LetsEncrypt is (currently) only able to issue HTTPS certificates with the DNS-01 ACME challenge, instead of the default HTTP-01 challenge, so the config needs to be updated accordingly. Pangolin uses Traefik, which uses the lego library for ACME challenges. Since configuration depends on your DNS provider, see the [lego documentation](https://go-acme.github.io/lego/dns/index.html) for supported providers and the configuration. To change the challenge type, I modified two files: ```yml # file: config/traefik/traefik_config.yml certificatesResolvers: letsencrypt: acme: # remove the httpChallenge key entirely here dnsChallenge: provider: [provider] # the code/cli name of the provider as specified in the lego documentation ``` ```yml # file: docker-compose.yml services: traefik: environment: # if the lego documentation specifies environment variables, put them here # example: DNS_PROVIDER_API_KEY: api_key_value ``` - And of course, don't forget to restart the server with `docker compose down` and then `docker compose up -d` These steps resulted in a successful installation for me.
Author
Owner

@coavins commented on GitHub (Feb 22, 2026):

The above reply was very helpful to me. I had to move the pangolin dashboard so I could forward port 443 as a raw TCP resource.

<!-- gh-comment-id:3940432687 --> @coavins commented on GitHub (Feb 22, 2026): The above reply was very helpful to me. I had to move the pangolin dashboard so I could forward port 443 as a raw TCP resource.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#6573