zoraxy icon indicating copy to clipboard operation
zoraxy copied to clipboard

[HELP] [forward-auth] Authelia Zoraxy Forward-Auth - configuration issue

Open teomemora opened this issue 6 months ago • 13 comments

Hello everyone,

unfortunately, I'm having some difficulties configuring forward-auth.

Zoraxy runs natively on a Linux server. I host a test website (test1.mydomin.com) in a nginx docker container. Authelia (auth.mydomain.com) is running in another container.

I would like to configure Zoraxy so that the test website (test1.mydomin.com) is protected with forward-auth and I get the Authelia password entry dialog before I can access the test website.

My problem is that the test website is displayed without requiring a password, even though I have activated forwad_auth.

Describe the networking setup you are using

  • Are you using the docker build of Zoraxy? no
  • Your Zoraxy version? v3.2.5r2
  • Are you using Cloudflare? no
  • Are your system hosted under a NAT router? no

This is my configuration:

Configuration settings for the Forward Auth provider:

Image

Authentication Provider for test1.mydomin.com:

Image

Zoraxy log:

Image

Authelia log:

Image

@Settings for the Forward Auth provider: Can anyone tell me which headers should be specified here?

Thank you very much.

How can I add the “forward-auth” tag? I can not select the appropriate lable.

teomemora avatar Aug 28 '25 22:08 teomemora

Hi @teomemora

How can I add the “forward-auth” tag?

That tag can only be added by me, it is kinda like a issue class category that I will assign onto an incoming issue and see which section of code might be the problem.

Btw, you might also want to tag the forward-auth module owner in the README if you still cant figure out how the set it up correctly in a week or two. From what I see from these type of issues, it is mostly related to networking / routing in your particular network environment. If you eventually figure out the reason, please feel free to share it here or on the wiki, have a nice day!

tobychui avatar Aug 30 '25 00:08 tobychui

Thank you for your feedback.

I will report back as soon as a solution has been found.

You are probably right, and it is just a routing or network problem. It is also possible that some necessary headers are missing.

To narrow down the problem further:

  • A gitea docker instance is also running on the same server. I have configured oauth2 here so that I can log in to gitea with my authelie user. That works.

  • Even when I call up the domain: ‘auth.mydomain.com’, I can log in to authelia.

Only the forward-auth access protection no longer works since the change from zoraxy v3.1.9 to 3.2.5r2.

  • Even a fresh installation of zraxy 3.2.5r2 without old config data did not help me.

  • Forward-auth works in version 3.1.9.

I also looked at the code and found that zoraxy v3.1.9 sends a POST request and zoraxy v3.2.5r2 always sends a GET request to the authelia server (auth.mydomain.com).

zoraxy 3.1.9 zoraxy-3.1.9\src\mod\auth\sso\authelia\authelia.go -> HandleAutheliaAuth:103 req, err := http.NewRequest("POST", autheliaBaseURL+"/api/verify", nil) zorazy 3.2.5r2 zoraxy-3.2.5r2\src\mod\auth\sso\forward\forward.go -> HandleAuthProviderRouting:164 req, err := http.NewRequest(http.MethodGet, ar.options.Address, nil)

But that can't really be the cause of the problem, can it?

@james-d-elliott I would be grateful if one of the authelia experts could help.

Best regards.

teomemora avatar Aug 30 '25 08:08 teomemora

The X-Forwarded-* headers are being overwritten by a proxy. If you're reverse proxying Authelia via Zoraxy you should be using the same scheme part and host part of the URL in the forward auth address as the proxy to settings, not the proxy from settings.

The method used is not relevant here, the POST method was actually an incorrect implementation.

I'm adding a switch to use X-Original-Method and X-Original-URL again, but Authelia is not providing future support for the /api/verify endpoint and neither should Zoraxy try to. This endpoint is unnecessarily complicated and will not receive any feature additions or bug fixes outside of security fixes, and we'll never cater any documentation to its use.

james-d-elliott avatar Aug 31 '25 07:08 james-d-elliott

Thank you for your feedback james-d-elliott

It works! :-)

That was exactly the problem. It was mainly because the address was incomplete.

I am now using the following settings:

Image

In my own words, I would say that Zoraxy must be able to reach the internal address of the Authelia server. So for someone who has not set up an extra SSL certificate for internal communication between Zoraxy and the Authelia Docker container, the address should then be as follows: http://your-own-domain.com:exposed-port/api/authz/forward-auth

The Authelia configuration.yml should also be configured accordingly:

...
session:
  secret: '__SECRET__'

  cookies:
    - name: 'authelia_session'
      domain: 'your-own-domain.com'
      authelia_url: 'https://auth.your-own-domain.com'
      expiration: '1 hour'
      inactivity: '5 minutes'
...

@tobychui & @james-d-elliott Thank you again for your help and your excellent and diligent development work! :muscle:

teomemora avatar Aug 31 '25 14:08 teomemora

@teomemora

I am also trying to setup authelia with Zoraxy. Since you managed to get it running, would you mind to provide more details how you managed to do it?

I am already strugling with configuring the relevant proxy paramters to get to Auth logging page. Help would really be appeciated!

FirebladeBMW avatar Sep 10 '25 18:09 FirebladeBMW

The effective solution is to make sure that the configuration of the forward auth section uses a URI that doesn't get proxied. So for example if you have Zoraxy on a docker network with Authelia, and Authelia's container name is set to authelia, you'd use http://authelia:9091/api/authz/forward-auth, similar to how you'd configure the reverse proxy element.

There's a new version I think which includes an option to toggle the X-Original-* headers, which could be used with most proxies. The cause of the issue is a proxy changing the header before it's transported to Authelia, so if the proxy doesn't set this header you're good to go. The URI to use for X-Original-* is https://<domain>/api/authz/auth-request. This option has not been as tested as the other so far so YMMV.

james-d-elliott avatar Sep 11 '25 00:09 james-d-elliott

Hi @james-d-elliott,

currently I have Zoraxy running on an LXC and Authelia in a Docker LXC, both on the same Proxmox host.

(I also have a working installation with NPM (Proxmox LXC) as my proxy, setup with the NGINX snippets from the Authelia Integration guide but wanted to test Zoraxy since it seems to be more modern and better maintained.)

More info about my setup can also be found here where I reported a bug: https://github.com/tobychui/zoraxy/issues/816#issue-3403201431

Concerning Zoraxy...

These are my auth.mydomain.com proxy settings:

Image Image

I tried to set this up similar to what is setup in NPM considering the includes from the NGINX snippets. What I am not sure about are the variables. Can I use the same as for NPM?

Forward auth is setup like this:

Image

Any issues you see with these settings? Do I need to change anything in the docker compose.yml to make it work with Zoraxy or should it work with the same settings since it is working with NPM? Do I need to make any adjustments to the docker compose.yml of Zoraxy? (it's very basic only)

services:
  zoraxy:
    image: zoraxydocker/zoraxy:latest
    container_name: zoraxy
    restart: unless-stopped
    ports:
      - 80:80/tcp
      - 443:443/tcp
      - 8001:8000/tcp
      - 8001:8000/udp
    volumes:
      - ~/docker/zoraxy/config/:/opt/zoraxy/config/
      - ~/docker/zoraxy/plugin/:/opt/zoraxy/plugin/
      - /var/run/docker.sock:/var/run/docker.sock
      - /etc/localtime:/etc/localtime:ro
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      FASTGEOIP: "false"

Here for the complete auth.mydomain.com.conf:

{
 "ProxyType": 1,
 "RootOrMatchingDomain": "auth.mydomain.com",
 "MatchingDomainAlias": [],
 "ActiveOrigins": [
  {
   "OriginIpOrDomain": "192.168.188.56:9091",
   "RequireTLS": true,
   "SkipCertValidations": true,
   "SkipWebSocketOriginCheck": true,
   "Weight": 1,
   "MaxConn": 0,
   "RespTimeout": 0
  }
 ],
 "InactiveOrigins": [],
 "UseStickySession": false,
 "UseActiveLoadBalance": false,
 "Disabled": false,
 "BypassGlobalTLS": false,
 "TlsOptions": {
  "DisableSNI": false,
  "DisableLegacyCertificateMatching": false,
  "EnableAutoHTTPS": false,
  "PreferredCertificate": {}
 },
 "VirtualDirectories": [],
 "HeaderRewriteRules": {
  "UserDefinedHeaders": [
   {
    "Direction": 0,
    "Key": "Host",
    "Value": "$host",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Original-URL",
    "Value": "$scheme://$http_host$request_uri",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Forwarded-Proto",
    "Value": "$scheme",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Forwarded-Host",
    "Value": "$http_host",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Forwarded-URI",
    "Value": "$request_uri",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Forwarded-Ssl",
    "Value": "on",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Forwarded-For",
    "Value": "$remote_addr",
    "IsRemove": false
   },
   {
    "Direction": 0,
    "Key": "X-Real-IP",
    "Value": "$remote_addr",
    "IsRemove": false
   }
  ],
  "RequestHostOverwrite": "",
  "HSTSMaxAge": 0,
  "EnablePermissionPolicyHeader": false,
  "PermissionPolicy": {
   "accelerometer": [
    "*"
   ],
   "ambient_light_sensor": [
    "*"
   ],
   "autoplay": [
    "*"
   ],
   "battery": [
    "*"
   ],
   "camera": [
    "*"
   ],
   "cross_origin_isolated": [
    "*"
   ],
   "display_capture": [
    "*"
   ],
   "document_domain": [
    "*"
   ],
   "encrypted_media": [
    "*"
   ],
   "execution_while_not_rendered": [
    "*"
   ],
   "execution_while_out_of_viewport": [
    "*"
   ],
   "fullscreen": [
    "*"
   ],
   "geolocation": [
    "*"
   ],
   "gyroscope": [
    "*"
   ],
   "keyboard_map": [
    "*"
   ],
   "magnetometer": [
    "*"
   ],
   "microphone": [
    "*"
   ],
   "midi": [
    "*"
   ],
   "navigation_override": [
    "*"
   ],
   "payment": [
    "*"
   ],
   "picture_in_picture": [
    "*"
   ],
   "publickey_credentials_get": [
    "*"
   ],
   "screen_wake_lock": [
    "*"
   ],
   "sync_xhr": [
    "*"
   ],
   "usb": [
    "*"
   ],
   "web_share": [
    "*"
   ],
   "xr_spatial_tracking": [
    "*"
   ],
   "clipboard_read": [
    "*"
   ],
   "clipboard_write": [
    "*"
   ],
   "gamepad": [
    "*"
   ],
   "speaker_selection": [
    "*"
   ],
   "conversion_measurement": [
    "*"
   ],
   "focus_without_user_activation": [
    "*"
   ],
   "hid": [
    "*"
   ],
   "idle_detection": [
    "*"
   ],
   "interest_cohort": [
    "*"
   ],
   "serial": [
    "*"
   ],
   "sync_script": [
    "*"
   ],
   "trust_token_redemption": [
    "*"
   ],
   "unload": [
    "*"
   ],
   "window_placement": [
    "*"
   ],
   "vertical_scroll": [
    "*"
   ]
  },
  "DisableHopByHopHeaderRemoval": false
 },
 "EnableWebsocketCustomHeaders": false,
 "AuthenticationProvider": {
  "AuthMethod": 0,
  "BasicAuthCredentials": [],
  "BasicAuthExceptionRules": [],
  "BasicAuthGroupIDs": null,
  "ForwardAuthURL": "",
  "ForwardAuthResponseHeaders": null,
  "ForwardAuthResponseClientHeaders": null,
  "ForwardAuthRequestHeaders": null,
  "ForwardAuthRequestExcludedCookies": null
 },
 "RequireRateLimit": false,
 "RateLimit": 0,
 "DisableUptimeMonitor": false,
 "DisableChunkedTransferEncoding": true,
 "AccessFilterUUID": "default",
 "DefaultSiteOption": 0,
 "DefaultSiteValue": "",
 "Tags": [
  "Network"
 ]
}

For completeness sake here also my authelia configuration.yml:

theme: dark

server.address: tcp://0.0.0.0:9091

log:
  level: warn
  file_path: /etc/authelia/authelia.log
  
totp:
  issuer: mydomain.com
  period: 30
  skew: 1

authentication_backend:
  file:
    path: /etc/authelia/users.yml
    watch: true

access_control:
  default_policy: one_factor
  rules:
    - domain: 
        - "auth.mydomain.com"
      policy: bypass
    - domain: # Proxies only requiring username and password
        - "glance.mydomain.com"
        - "proxmox.mydomain.com"
      policy: one_factor

session:
  name: authelia_session
  secret: <snipped>
  same_site: lax
  inactivity: 5m
  expiration: 1h
  remember_me: 1M
  
  cookies:
    - domain: mydomain.com
      authelia_url: https://auth.mydomain.com

regulation:
  max_retries: 5
  find_time: 2m
  ban_time: 10m

storage:
  encryption_key: <snipped>
  local:
    path: /etc/authelia/db.sqlite

identity_validation:
  reset_password:
    jwt_secret: <snipped>
    jwt_lifespan: 5 minutes
    jwt_algorithm: HS256

notifier:
  filesystem:
    filename: /etc/authelia/emails.txt

FirebladeBMW avatar Sep 11 '25 12:09 FirebladeBMW

Set it to http://192.168.188.56:9091/api/authz/forward-auth.

james-d-elliott avatar Sep 11 '25 16:09 james-d-elliott

hi @FirebladeBMW

Hello,

I can't say for sure if this is the problem, but I noticed the following in your configuration:

You have enabled TSL in Zorax for internal communication with your authelia server (auth.mydomain.com / 192.168.88.188.56:9091).

However, there is no TLS entry in your authelia configuration.yml. This means that the authelia server only speaks http.

That might help.

Best regards

teomemora avatar Sep 11 '25 17:09 teomemora

Thank you both for your feedback! Really appreciated!

At least Zoraxy now is forwarding the request to Authelia. Login page of Authelia is loading but spinner logo keeps "spinning" and not loading the login part of the site with a red error message (banner)

It seems he is missing the X-Original-URL header.

Image

Here is the error log from Authelia for this login session:

time="2025-09-11T19:59:37+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/ (method GET)."
time="2025-09-11T19:59:37+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/static/css/index.CRV7Mtik.css (method GET)."
time="2025-09-11T19:59:37+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/static/js/index.BTAcfhmB.js (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/en/consent.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/de/consent.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/en/portal.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/en/settings.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/de/portal.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/locales/de/settings.json (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/favicon.ico (method GET)."
time="2025-09-11T19:59:38+02:00" level=debug msg="Check authorization of subject username= groups= ip=192.168.188.101 and object https://auth.mydomain.com/api/state (method GET)."
time="2025-09-11T19:59:38+02:00" level=error msg="Error occurred retrieving user session" error="unable to retrieve session cookie domain: failed to parse X-Original-URL header: parse \"://$http_host/api/state\": missing protocol scheme" method=GET path=/api/state remote_ip=192.168.188.165 stack="github.com/authelia/authelia/v4/internal/handlers/handler_state.go:15  StateGET\ngithub.com/authelia/authelia/v4/internal/middlewares/bridge.go:66      handlerMain.(*BridgeBuilder).Build.func7.1\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:65     SecurityHeadersCSPNone.func1\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:105    SecurityHeadersNoStore.func1\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:30     SecurityHeadersBase.func1\ngithub.com/fasthttp/[email protected]/router.go:441                        (*Router).Handler\ngithub.com/authelia/authelia/v4/internal/middlewares/log_request.go:14 handlerMain.LogRequest.func30\ngithub.com/authelia/authelia/v4/internal/middlewares/errors.go:38      RecoverPanic.func1\ngithub.com/valyala/[email protected]/server.go:2455                     (*Server).serveConn\ngithub.com/valyala/[email protected]/workerpool.go:225                  (*workerPool).workerFunc\ngithub.com/valyala/[email protected]/workerpool.go:197                  (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                               goexit"

Wireshark:

Image

FirebladeBMW avatar Sep 11 '25 18:09 FirebladeBMW

Alright, I think I found the issue...

The custom headers do not accept multiple values. It will only parse the last variable. (no matter if separated by "," or not)

Here, I entered for X-Original-URL " $scheme://$host$request_uri " (also tried $scheme**,://,$host,**$request_uri)

Image

This will result in X-Original-URL ommitting the first 2 variables and only parsing the entered chars + last variable:

Image

When I try to trick this by just entering " $host " I can get to the login window and log in. However, it will not move on to the Authelia confirmation window, where it confirms that you are logged in. It will just reload the Login window and you can log in again...

When opening another page than than the Auth itself it will let you progress to the actual page. But well, by doing this you will lose the URI part of the URL.

I am using v3.5.2r2 currently. It's a bug I assume?

FirebladeBMW avatar Sep 11 '25 21:09 FirebladeBMW

I'm pretty sure you can remove the custom headers and it'll work, doesn't seem like a forward auth issue. All of my testing has been with the default header configuration in Zoraxy.

james-d-elliott avatar Sep 11 '25 22:09 james-d-elliott

Yes, just got it working now. Also tested then with removing as many headers as possible to see what is the minimum requirement. And yes, you are right. I can confirm, none is needed... 🤣🤦‍♂️

Really was going crazy with this... Flushed all caches, rebooted the whole network etc... Headers not accepting multiple values still is a bug though?

Anyways, thank you very much, both of you! Truely appreciated!!!

Now I have to find out how to get my OPNsense working with Zoraxy! That's the only site I cannot access via reverse proxy. but with IP only.

FirebladeBMW avatar Sep 11 '25 22:09 FirebladeBMW