Caddy plugin, Authentik sso : missing authorization header

Started by caplam, January 17, 2025, 03:43:21 PM

Previous topic - Next topic
Hello,

I started with opnsense a few weeks ago and now my setup is becoming more complete.
I now want to implement sso because of the hassle to authenticate each time I use a docker (I have around 40 apps in containers)
I don't have many users that need access to apps but I enjoy learning new things.
I chose Authentik; it's certainly overkill for my use case but when you are an enthusiast...
I have many apps; some of them are published but the majority is not.
At first I used caddy plugin to publish apps; and authenticating is done within the app.
Then I used it also for apps on the lan (using an access list with only private ips). Authenticating is also done within the app.

Reading several tutorials and articles I used these methods:

For published apps :

Caddy setup is straight forward. I use let's encrypt certificates. external hostnames are setup at the registar and nat reflection does the rest.
I access these apps from the lan or from outside with no problem.

For lan only apps:

When you have many containers it's a pain to remember ip:port, login and password.
So for each app I setup an host override in unbound.
app1.mydomain.com -> ip-of-the-container (I don't have to remember ip)
caddy.mydomain.com -> ip-of-opnsense (all the apps have an alias to caddy)

to be more clear with an example:
for Sonarr container :
Sonarr.mydomain.com -> 192.168.1.10
caddy.domain.com -> 192.168.1.1
Sonarr.mydomain.Com is an alias of caddy.mydomain.com

i also setup reverse proxy:

http://tvshows.mydomain.com -> http://sonarr.mydomain.com:8989

That way I don't have to remember ip:port of my containers and can access them from the lan with an hostname.

Does this way of doing appear correct to you ?

Now I want sso; so I started Authentik using provided compose file on their site.
I started with Portainer using oidc and all went well. For now I access Portainer with ip:port (nothing setup in caddy or dns override).

I noticed that in caddy setup there is an Auth Provider tab.
How do you use it ? is it mandatory for using Authentik?
What is the flow of a request to access a proxied app ?
is caddy redirecting all requests to Auth Provider unconditionally ? if so how is there anything particular to configure in the proxied app ?

As you will probably notice it's unclear for me how to handle sso with a reverse proxy.
Without reverse proxy (as in my Portainer example) it's the app that redirects to authentic; but when I use reverse proxy what should be the way of doing ?

Some apps like Portainer or immich are compatible with oauth and handle multi users; some others like Sonarr in my previous example are not and only use Basic Auth for a single user.
Sorry to be a little confuse but the more I read the less I understand.

edit: I didn't see the checkbox "forward auth"  in handler's page in caddy config. it answers one of my questions.
But I don't know how to configure Auth Provider, especially "Forward Auth URI"
I find somewhere: "/outpost.goauthentik.io/auth/caddy" but it doesn't work

Auth provider configuration in the reverse proxy (such as Caddy) is for access control to the proxy itself. Without some form of authentication at the proxy, it will expose your internal apps to "the whole internet" (or whatever your firewall policy allows), and then it's up to the apps to protect themselves (and potential intrusion into your internal network). If you implement an auth provider, users must authenticate before they are allowed to access apps through the proxy. This allows you to "securely" access even apps that don't have any form of authentication built in. If your apps also integrate with the same auth provider, you get single signon between the proxy and the apps - so once you've authenticated at the proxy, you'll already be authenticated to access the apps.

The forward auth URI that you found looks correct (based on https://docs.goauthentik.io/docs/add-secure-apps/providers/proxy/server_caddy). What does "doesn't work" mean? What did you try to do? What did you expect to happen? What actually happened?

BTW, if your apps are all docker-based, another possible approach would be to run traefik as your reverse-proxy, integrated with your auth provider, and port-forward (443) to it.

I don't really understand the way it works.
So far I configured the reverse proxy and dns override parts to access my apps with http://films.domain.tld (it opens http://ip-of-radar:7878)

I also configured Authentik to be used to login in portainer (app compatible with oauth2) without passing through caddy.
I managed to login to portainer using reverse proxy. It works but after authenticating (login + password) in authentik I have an error message in portainer that states "Failure Invalid OAuth state, try again."
I then click again on login with auth and I'm logged in without entering again login and password

But when I enable Forward Auth in the reverse proxy handler config I have 404 error or a page with " Not found powered by Authentik"




Quote from: caplam on January 19, 2025, 06:01:59 PMBut when I enable Forward Auth in the reverse proxy handler config I have 404 error or a page with " Not found powered by Authentik"

That seems to suggest that the forward auth URI is not right. What URL is your browser trying to access when you see that?

I've not actually tried Authentik myself. I did have the Caddy plugin sortof working with Authelia at one point, but ended up abandoning it and going back to Traefik(+Authelia).

January 20, 2025, 04:20:16 PM #4 Last Edit: January 20, 2025, 04:44:41 PM by dseven
I had a play with Authentik (been meaning to check it out anyway), and I was able to get it working with the OPNsense Caddy plugin, after a bit of head-scratching. I did run into the 404 issue. It wasn't due to incorrect forward auth URL, but because I didn't have a "Provider" in Authentik that matched the external URL being proxied by Caddy. I was trying to use "Forward auth (domain level)" at first. When I switched to "... (single application)" and specified the "External host", it started working. In addition to defining the Provider, you need to define an Application that uses the provider, and add that Application to the predefined "authentik Embedded Outpost". I need to go back and see if I can figure out how the "domain level" variation is supposed to work...

Edit: Got "domain level" to work. I'm using port 10443 for Caddy (because 443 is still forwarded to Traefik), and had to include the port in my cookie domain for the Provider ("subdomain.mydomain.com:10443") to get it to match. I haven't found that documented anywhere, but took a guess at it, and it seems to work...

January 20, 2025, 05:37:35 PM #5 Last Edit: January 21, 2025, 10:04:01 AM by caplam
i made small steps.
the uri I used was wrong. it's mentioned "/outpost.goauthentik.io/auth/caddy" but was using "/outpost.goauthentik.io/caddy"
I forgot to check dns challenge in domain definition and to uncheck a dns override in unbound.

Now when  I enter the external url of the app I'm redirect to Authentik which forwards me to the app but without passing the Basic Auth so I have to log in the app with credentials. I payed attention to the creation of the group in Authentik. I don't know where to search now.

edit: within the logs I can see that:
in Authentik logs says I'm logged in and aupplication authorized.
in Radarr : "Basic was not authenticated. Failure message: Authorization header missing."

So I don't think it's in my config. Moreover in caddy it's said that you can't add headers when using authentic for Forward Auth.

Finally made a step forward (Radarr for now I have to test with some others apps). but I need advice to go further.

I added to caddy config/reverse proxy/headers a header_up type Authorization value: "Basic username:password" (username:password has to be encoded in base64)
Then in handlers definition (advanced mode) I added the header created above to http headers.

With that config I can now login to radarr. The downside is of course that username and password are hardcoded in caddy and authorization no longer relies on Authentik group. So I have to find a way to make Authentik generate the authorization header and pass it to caddy.
I'm a complete noob in this field  so any help would be greatly appreciated.


I already saw this post and it's why I tried to add authorization header.
I also explore the possibility to set authorization method to external; it works and you can define which user can access the app. But accessing radarr with local address leaves it open.

January 21, 2025, 03:48:13 PM #9 Last Edit: January 21, 2025, 03:50:15 PM by Monviech (Cedrik)
Somebody writes this:

https://github.com/goauthentik/authentik/issues/4381#issuecomment-2585278018

But the current template in the plugin does not have it in copy_headers

https://github.com/opnsense/plugins/blob/305af347b831d068dc6ee7b8eb1e2489afb5560b/www/caddy/src/opnsense/service/templates/OPNsense/Caddy/includeAuthProvider#L27

So try adding it to the template and see if it helps.

E.G. like this:

copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version Authorization
Hardware:
DEC740

Thanks for your reply.
I'm currently trying to figure out how to add a custom configuration file.
I guess I need to add it in /usr/local/etc/caddy/caddy.d/.  and name it my_file.conf but have no clue on what to put in.
Would I need a confirmation file per handler?

I don't know what you are trying to do, but if you look at the caddyfile you see the spots where stuff is imported. It will be imported in the same way you write it in the file, at exactly that spot.

Imagine writing manually into the Caddyfile and it will be printed in the spot where the file is imported.

If something is missing tell me so we can add it to the GUI, if its in the scope of making Authentik work better.
Hardware:
DEC740

I managed to do it. I don't know if it's the best way but it works.
I first disabled the concerned handler => caddy generated a file without this handler.
I created a file radarr.conf under  /usr/local/etc/caddy/caddy.d/
with the content of the whole previous radarr reverse proxy "block" and added Authorization to the copy_headers directive.
Now I can login to Authentik and be automatically logged in radarr.

Before that I searched a way to add "Authorization" without having to write the whole reverse proxy block but didn't find a way to do it.

edit: I didn't try with other apps using Basic Auth but I expect they should behave the same.
So adding "Authorization" to copy_headers seems to solve the problem.

If you open a small feature request and link your thread I can add it to the plugin.

https://github.com/opnsense/plugins/issues
Hardware:
DEC740