[GH-ISSUE #2551] Bypass Auth path rules do not work for unauthenticated users on Protected resources #10947

Closed
opened 2026-05-06 15:36:44 -05:00 by GiteaMirror · 11 comments
Owner

Originally created by @BeBeRex56 on GitHub (Feb 26, 2026).
Original GitHub issue: https://github.com/fosrl/pangolin/issues/2551

Describe the Bug

When a resource is set to Protected, path-based Bypass Auth rules do not allow unauthenticated users to access those paths. Unauthenticated visitors are always redirected to the Pangolin authentication wall regardless of matching Bypass Auth rules. Authenticated users with an existing session are unaffected, which makes the issue easy to miss during testing.

Environment

  • OS Type & Version: Ugreen NAS (Docker)
  • Pangolin Version: latest (hosted via app.pangolin.net)
  • Gerbil Version: N/A
  • Traefik Version: N/A
  • Newt Version: 1.10.0
  • Pangolin-CLI Version: latest
  • Olm Version: N/A

To Reproduce

  1. Create a resource with Authentication set to Protected
  2. Navigate to the Rules tab for the resource
  3. Add a rule: Action = Bypass Auth, Match Type = Path, Value = /share/
  4. Save the rules
  5. Open a path matching the rule (e.g. /share/) in a private/incognito browser window
  6. Observe that Pangolin redirects to the authentication page instead of allowing access

Expected Behavior

A Bypass Auth path rule should allow unauthenticated users to access the matched path without being redirected to the login page, even when the resource is globally Protected. This is needed to support use cases where a protected resource has specific public-facing endpoints, such as application-generated share links.

<html> <html><head></head>

Bypass Auth and Pass to Auth Path Rules Do Not Work Correctly in Either Authentication Mode

Summary

Path-based rules in Pangolin's Resource Rules Configuration are broken in both directions:

  1. When a resource is set to Protected, Bypass Auth rules do not allow unauthenticated users to access matched paths — the global auth wall fires before rules are evaluated.
  2. When a resource Authentication is set to off/none, Pass to Auth rules do not enforce authentication on matched paths — everything is publicly accessible regardless of rules.

This makes it impossible to achieve a mixed-access configuration where some paths on a resource are public (e.g. share links) and others are protected — regardless of which direction you configure it from.

Expected Behavior

  • When Authentication is Protected: Bypass Auth path rules should allow unauthenticated users to access matched paths without hitting the auth wall.
  • When Authentication is off: Pass to Auth path rules should enforce authentication on matched paths, blocking unauthenticated access to those paths.

In both cases, path rules should be evaluated before the global authentication decision is made.

Actual Behavior

Scenario A — Protected resource with Bypass Auth rules: Unauthenticated users visiting any path, including those explicitly covered by Bypass Auth rules, are redirected to the Pangolin authentication page. The bypass rules are never applied.

Scenario B — Unprotected resource with Pass to Auth rules: All paths are publicly accessible to unauthenticated users regardless of Pass to Auth rules. The rules are never applied.

In both scenarios, authenticated users with existing Pangolin sessions are unaffected, which makes the issue easy to miss during initial testing.

Steps to Reproduce

Scenario A:

  1. Create a resource with Authentication set to Protected
  2. Navigate to the Rules tab and add: Action = Bypass Auth, Match Type = Path, Value = /share/
  3. Save the rules
  4. Open a matching path (e.g. /share/<token>) in a private/incognito browser window
  5. Observe redirect to Pangolin auth page instead of bypassing

Scenario B:

  1. Set the same resource Authentication to off/none
  2. Navigate to the Rules tab and add: Action = Pass to Auth, Match Type = Path, Value = /
  3. Save the rules
  4. Open the resource root URL in a private/incognito browser window
  5. Observe that the resource is fully accessible with no authentication required

Environment

  • OS Type & Version: Ugreen NAS (Docker)
  • Pangolin Version: latest (hosted via app.pangolin.net)
  • Gerbil Version: N/A
  • Traefik Version: N/A
  • Newt Version: 1.10.0
  • Pangolin-CLI Version: latest
  • Olm Version: N/A

Use Case / Impact

This prevents a common and important self-hosting use case: protecting a resource behind Pangolin authentication while allowing specific public-facing paths (such as application-generated share links) to remain accessible without login.

The specific case tested was File Browser running behind Pangolin, where File Browser generates unique share link tokens accessible at /share/<token>, /api/public/share/<token>, and serving assets from /static/assets/ and /static/img/. These endpoints are intended to be publicly shareable. With the current behavior there is no way to expose these public endpoints while keeping the rest of the resource protected.

Rules configuration tested (Scenario A):

Priority Action Match Type Value
1 Bypass Auth Path /share/
2 Bypass Auth Path /api/public/
3 Bypass Auth Path /static/assets/
4 Bypass Auth Path /static/img/

Even with rule 5 in place, the resource root is fully publicly accessible with no authentication.

Additional Observations

  • The issue is only reproducible for unauthenticated sessions. Logged-in users with a valid Pangolin session are unaffected and rules appear to function correctly for them.
  • This strongly suggests that the global authentication state (Protected or off) is evaluated at a layer above the rules engine, and rules are only consulted after a valid session is already confirmed.
  • There is no option in the Proxy settings UI to strip forwarded headers, which would have been an alternative workaround.
  • Secondary CORS/CORB errors were also observed when static assets required by the share page (JS, CSS) were being intercepted by the auth wall and redirected cross-origin to app.pangolin.net.

Suggested Fix

Path rules should be evaluated before the global authentication check, so that:

  • Matching Bypass Auth rules pass unauthenticated requests directly to the upstream without triggering the auth redirect
  • Matching Pass to Auth rules enforce authentication even when the resource is globally unprotected

Alternatively, a dedicated "Mixed" authentication mode that explicitly supports public paths with protected fallback would address this use case cleanly.

</html> </html>
Originally created by @BeBeRex56 on GitHub (Feb 26, 2026). Original GitHub issue: https://github.com/fosrl/pangolin/issues/2551 ### Describe the Bug When a resource is set to Protected, path-based Bypass Auth rules do not allow unauthenticated users to access those paths. Unauthenticated visitors are always redirected to the Pangolin authentication wall regardless of matching Bypass Auth rules. Authenticated users with an existing session are unaffected, which makes the issue easy to miss during testing. ### Environment - OS Type & Version: Ugreen NAS (Docker) - Pangolin Version: latest (hosted via app.pangolin.net) - Gerbil Version: N/A - Traefik Version: N/A - Newt Version: 1.10.0 - Pangolin-CLI Version: latest - Olm Version: N/A ### To Reproduce 1. Create a resource with Authentication set to Protected 2. Navigate to the Rules tab for the resource 3. Add a rule: Action = Bypass Auth, Match Type = Path, Value = /share/ 4. Save the rules 5. Open a path matching the rule (e.g. /share/<token>) in a private/incognito browser window 6. Observe that Pangolin redirects to the authentication page instead of allowing access ### Expected Behavior A Bypass Auth path rule should allow unauthenticated users to access the matched path without being redirected to the login page, even when the resource is globally Protected. This is needed to support use cases where a protected resource has specific public-facing endpoints, such as application-generated share links. <html> <body> <!--StartFragment--><html><head></head><body><h1>Bypass Auth and Pass to Auth Path Rules Do Not Work Correctly in Either Authentication Mode</h1> <h2>Summary</h2> <p>Path-based rules in Pangolin's Resource Rules Configuration are broken in both directions:</p> <ol> <li>When a resource is set to <strong>Protected</strong>, <code>Bypass Auth</code> rules do not allow unauthenticated users to access matched paths — the global auth wall fires before rules are evaluated.</li> <li>When a resource Authentication is set to <strong>off/none</strong>, <code>Pass to Auth</code> rules do not enforce authentication on matched paths — everything is publicly accessible regardless of rules.</li> </ol> <p>This makes it impossible to achieve a mixed-access configuration where some paths on a resource are public (e.g. share links) and others are protected — regardless of which direction you configure it from.</p> <h2>Expected Behavior</h2> <ul> <li>When Authentication is <strong>Protected</strong>: <code>Bypass Auth</code> path rules should allow unauthenticated users to access matched paths without hitting the auth wall.</li> <li>When Authentication is <strong>off</strong>: <code>Pass to Auth</code> path rules should enforce authentication on matched paths, blocking unauthenticated access to those paths.</li> </ul> <p>In both cases, path rules should be evaluated <strong>before</strong> the global authentication decision is made.</p> <h2>Actual Behavior</h2> <p><strong>Scenario A — Protected resource with Bypass Auth rules:</strong> Unauthenticated users visiting any path, including those explicitly covered by Bypass Auth rules, are redirected to the Pangolin authentication page. The bypass rules are never applied.</p> <p><strong>Scenario B — Unprotected resource with Pass to Auth rules:</strong> All paths are publicly accessible to unauthenticated users regardless of Pass to Auth rules. The rules are never applied.</p> <p>In both scenarios, authenticated users with existing Pangolin sessions are unaffected, which makes the issue easy to miss during initial testing.</p> <h2>Steps to Reproduce</h2> <p><strong>Scenario A:</strong></p> <ol> <li>Create a resource with Authentication set to Protected</li> <li>Navigate to the Rules tab and add: Action = Bypass Auth, Match Type = Path, Value = /share/</li> <li>Save the rules</li> <li>Open a matching path (e.g. /share/&lt;token&gt;) in a private/incognito browser window</li> <li>Observe redirect to Pangolin auth page instead of bypassing</li> </ol> <p><strong>Scenario B:</strong></p> <ol> <li>Set the same resource Authentication to off/none</li> <li>Navigate to the Rules tab and add: Action = Pass to Auth, Match Type = Path, Value = /</li> <li>Save the rules</li> <li>Open the resource root URL in a private/incognito browser window</li> <li>Observe that the resource is fully accessible with no authentication required</li> </ol> <h2>Environment</h2> <ul> <li>OS Type &amp; Version: Ugreen NAS (Docker)</li> <li>Pangolin Version: latest (hosted via app.pangolin.net)</li> <li>Gerbil Version: N/A</li> <li>Traefik Version: N/A</li> <li>Newt Version: 1.10.0</li> <li>Pangolin-CLI Version: latest</li> <li>Olm Version: N/A</li> </ul> <h2>Use Case / Impact</h2> <p>This prevents a common and important self-hosting use case: protecting a resource behind Pangolin authentication while allowing specific public-facing paths (such as application-generated share links) to remain accessible without login.</p> <p>The specific case tested was File Browser running behind Pangolin, where File Browser generates unique share link tokens accessible at <code>/share/&lt;token&gt;</code>, <code>/api/public/share/&lt;token&gt;</code>, and serving assets from <code>/static/assets/</code> and <code>/static/img/</code>. These endpoints are intended to be publicly shareable. With the current behavior there is no way to expose these public endpoints while keeping the rest of the resource protected.</p> <p><strong>Rules configuration tested (Scenario A):</strong></p> Priority | Action | Match Type | Value -- | -- | -- | -- 1 | Bypass Auth | Path | /share/ 2 | Bypass Auth | Path | /api/public/ 3 | Bypass Auth | Path | /static/assets/ 4 | Bypass Auth | Path | /static/img/ <p>Even with rule 5 in place, the resource root is fully publicly accessible with no authentication.</p> <h2>Additional Observations</h2> <ul> <li>The issue is only reproducible for unauthenticated sessions. Logged-in users with a valid Pangolin session are unaffected and rules appear to function correctly for them.</li> <li>This strongly suggests that the global authentication state (Protected or off) is evaluated at a layer above the rules engine, and rules are only consulted after a valid session is already confirmed.</li> <li>There is no option in the Proxy settings UI to strip forwarded headers, which would have been an alternative workaround.</li> <li>Secondary CORS/CORB errors were also observed when static assets required by the share page (JS, CSS) were being intercepted by the auth wall and redirected cross-origin to app.pangolin.net.</li> </ul> <h2>Suggested Fix</h2> <p>Path rules should be evaluated <strong>before</strong> the global authentication check, so that:</p> <ul> <li>Matching <code>Bypass Auth</code> rules pass unauthenticated requests directly to the upstream without triggering the auth redirect</li> <li>Matching <code>Pass to Auth</code> rules enforce authentication even when the resource is globally unprotected</li> </ul> <p>Alternatively, a dedicated "Mixed" authentication mode that explicitly supports public paths with protected fallback would address this use case cleanly.</p></body></html><!--EndFragment--> </body> </html>
GiteaMirror added the stale label 2026-05-06 15:36:44 -05:00
Author
Owner

@JonathanCrouse commented on GitHub (Feb 27, 2026):

Just updated to 1.16.0 and I'm not seeing similar behavior with my resources. I think this might just be a rules issues, at least for your first scenario.

For Scenario A change the path from '/share/' to '/share/*'. Then after that rule add another as Action = Pass to Auth, Match Type = Path, Value = *

Scenario B could be a caching/cookie issue if you're already authenticated with pangolin. If it's not that then similar to above change '/' to '*' and try it.

Here is a good list of examples in the docs too - https://docs.pangolin.net/manage/access-control/rules

<!-- gh-comment-id:3972772455 --> @JonathanCrouse commented on GitHub (Feb 27, 2026): Just updated to 1.16.0 and I'm not seeing similar behavior with my resources. I think this might just be a rules issues, at least for your first scenario. For Scenario A change the path from '/share/' to '/share/*'. Then after that rule add another as Action = Pass to Auth, Match Type = Path, Value = * Scenario B could be a caching/cookie issue if you're already authenticated with pangolin. If it's not that then similar to above change '/' to '*' and try it. Here is a good list of examples in the docs too - https://docs.pangolin.net/manage/access-control/rules
Author
Owner

@moohbr commented on GitHub (Feb 27, 2026):

@JonathanCrouse I had the same issue as the OP, but I'm using the direct page (/config.html). I'm on version 1.13.1

<!-- gh-comment-id:3974011003 --> @moohbr commented on GitHub (Feb 27, 2026): @JonathanCrouse I had the same issue as the OP, but I'm using the direct page (/config.html). I'm on version 1.13.1
Author
Owner

@JonathanCrouse commented on GitHub (Feb 27, 2026):

@moohbr In your case, assuming /config.html is the only location that you want to allow access unauthenticated, you would have a rule to bypass auth for path /config.html and then another rule (with a higher priority number) that will pass to auth for path *.

This should require authentication through pangolin for any location on that resource except for /config.html.

<!-- gh-comment-id:3974864558 --> @JonathanCrouse commented on GitHub (Feb 27, 2026): @moohbr In your case, assuming /config.html is the only location that you want to allow access unauthenticated, you would have a rule to bypass auth for path /config.html and then another rule (with a higher priority number) that will pass to auth for path *. This should require authentication through pangolin for any location on that resource except for /config.html.
Author
Owner

@moohbr commented on GitHub (Mar 1, 2026):

@JonathanCrouse not successful here. I tried the exactly thing you suggest, but no effects happened. My actual goal is block only /config.html

<!-- gh-comment-id:3981017491 --> @moohbr commented on GitHub (Mar 1, 2026): @JonathanCrouse not successful here. I tried the exactly thing you suggest, but no effects happened. My actual goal is block only `/config.html`
Author
Owner

@moohbr commented on GitHub (Mar 1, 2026):

If I full block access to this page, it work perfectly, however when I try force authentication only on this page, it won't work.

  • 1st case -> block access for everyone on a unprotected resource - works
    Image

  • 2nd case -> force auth in a unique path - failed

Image
<!-- gh-comment-id:3981025737 --> @moohbr commented on GitHub (Mar 1, 2026): If I full block access to this page, it work perfectly, however when I try force authentication only on this page, it won't work. - **1st case** -> block access for everyone on a _unprotected_ resource - **works** <img width="1536" height="62" alt="Image" src="https://github.com/user-attachments/assets/4382f4a6-0d09-45d7-9f90-7da703e92e2d" /> - **2nd case** -> force auth in a unique path - **failed** <img width="1541" height="61" alt="Image" src="https://github.com/user-attachments/assets/4691213a-a728-4f8f-879c-cae65d7b3344" />
Author
Owner

@moohbr commented on GitHub (Mar 1, 2026):

After analyzing the code, the file server/routers/badger/verifySession.ts is responsible for applying the access rules. Here is what actually happens:

  • Bypass authAlways returns allowed
  • Block accessAlways returns not allowed
  • Pass to authTries to verify the enabled authentication methods

On line 264, the condition if (!sso && !pincode && !password && ...) checks whether the resource has any authentication method configured.
If none exists, the rule is ignored and access is automatically granted to the user, even if the option selected was Pass to Auth.

IMO, a safer approach would be:

If the resource has no authentication methods configured, the system should only allow the Block Access option and on API return an explicit message. Maybe this would avoid misconfiguration scenarios.

BTW, to fix the issue, I did:

  1. Enabled at least one authentication method.
  2. Pass to Auth on the routes I actually want to block.
  3. Bypass Auth on * to allow access to all other pages.
Image

In the screenshot there are more blocked pages (and I disabled * xD), but you get the idea.

PS: Until 01/03, I can't bypass specifics URL in the scenario above.

<!-- gh-comment-id:3981308372 --> @moohbr commented on GitHub (Mar 1, 2026): After analyzing the code, the file `server/routers/badger/verifySession.ts` is responsible for applying the access rules. Here is what actually happens: - `Bypass auth` → **Always** returns **allowed** - `Block access` → **Always** returns **not allowed** - `Pass to auth` → **Tries** to verify the enabled authentication methods On _line 264_, the condition `if (!sso && !pincode && !password && ...)` checks whether the resource has **any** authentication method configured. If **none exists**, **the rule is ignored** and access is automatically granted to the user, even if the option selected was `Pass to Auth`. **IMO**, a safer approach would be: If the resource has **no authentication methods** configured, the system should **only** allow the `Block Access` option and on API return an explicit message. Maybe this would avoid misconfiguration scenarios. BTW, to fix the issue, I did: 1. Enabled at least one authentication method. 2. `Pass to Auth` on the routes I actually want to **block**. 3. `Bypass Auth` on `*` to **allow access** to all other pages. <img width="1035" height="1158" alt="Image" src="https://github.com/user-attachments/assets/405a23ba-0647-49cd-b373-9e5228a7519e" /> In the screenshot there are more blocked pages (and I disabled * xD), but you get the idea. PS: Until 01/03, I can't bypass specifics URL in the scenario above.
Author
Owner

@moohbr commented on GitHub (Mar 1, 2026):

@BeBeRex56 Please, try what I said. It may help you. If it works, we can discuss the next steps.

<!-- gh-comment-id:3981314324 --> @moohbr commented on GitHub (Mar 1, 2026): @BeBeRex56 Please, try what I said. It may help you. If it works, we can discuss the next steps.
Author
Owner

@BeBeRex56 commented on GitHub (Mar 1, 2026):

@BeBeRex56 Please, try what I said. It may help you. If it works, we can discuss the next steps.

Ok I'll try this ! Thank you !

<!-- gh-comment-id:3981337829 --> @BeBeRex56 commented on GitHub (Mar 1, 2026): > @BeBeRex56 Please, try what I said. It may help you. If it works, we can discuss the next steps. Ok I'll try this ! Thank you !
Author
Owner

@moohbr commented on GitHub (Mar 2, 2026):

Hey guys, I found something interesting: when the path contains URL parameters, Pangolin doesn’t handle it properly and the rule validation “fails”. I used a basic URL validation (from the Web API) and that solved the issue. Until they review and accept my MR, I suggest adding a * to any URL that may receive parameters.

For example: if you allow stream.html, then stream.html?token=123 will not be allowed. Pangolin validates the exact path, so you need to set Bypass auth for stream.html*.

<!-- gh-comment-id:3981444845 --> @moohbr commented on GitHub (Mar 2, 2026): Hey guys, I found something interesting: when the path contains URL parameters, Pangolin doesn’t handle it properly and the rule validation “fails”. I used a basic URL validation (from the [Web API](https://developer.mozilla.org/en-US/docs/Web/API/URL)) and that solved the issue. Until they review and accept my MR, I suggest adding a `*` to any URL that may receive parameters. For example: if you allow `stream.html`, then `stream.html?token=123` will not be allowed. Pangolin validates the **exact** path, so you need to set `Bypass auth` for `stream.html*`.
Author
Owner

@github-actions[bot] commented on GitHub (Mar 17, 2026):

This issue has been automatically marked as stale due to 14 days of inactivity. It will be closed in 14 days if no further activity occurs.

<!-- gh-comment-id:4071531536 --> @github-actions[bot] commented on GitHub (Mar 17, 2026): This issue has been automatically marked as stale due to 14 days of inactivity. It will be closed in 14 days if no further activity occurs.
Author
Owner

@github-actions[bot] commented on GitHub (Mar 31, 2026):

This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.

<!-- gh-comment-id:4159048534 --> @github-actions[bot] commented on GitHub (Mar 31, 2026): This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#10947