dashy
dashy copied to clipboard
[FEATURE_REQUEST] Add Header Authentication
Is your feature request related to a problem? If so, please describe.
Currently the only authentication options are Keycloak and inbuilt auth. I use Authelia to manage my SSO, which supports passing user/group info through to the reverse proxied app. There are also other apps out there that can pass in user/group info in a similar way.
Describe the solution you'd like
I see this providing the same functionality as the current Keycloak auth, just grabbing the user/group info from headers passed via the proxy instead of interrogating Keycloak.
appConfig.auth (optional)
| Field | Type | Required | Description |
|---|---|---|---|
enableHeaderAuth |
boolean |
Optional | If set to true enable Header Authentication. See appConfig.auth.headerAuth. |
appConfig.auth.headerAuth (optional)
| Field | Type | Required | Description |
|---|---|---|---|
userHeader |
string |
Optional | Header name which contains username (default: X-Forwarded-User) |
groupHeader |
string |
Optional | Header name which contains group info (default: X-Forwarded-Groups) |
proxyWhitelist |
array |
Required | Upstream proxy servers to expect authenticated requests from. |
From here it could use the already existing hideForUsers and showForUsers config, but given that Keycloak has them split out I'm not sure how feasible it is. Could also extend the displayData options to have hideForGroup and showForGroup for extra configurability.
Priority
Medium (Would be very useful)
Is this something you would be keen to implement
No
This issue has gone 6 weeks without an update. To keep the ticket open, please indicate that it is still relevant in a comment below. Otherwise it will be closed in 5 working days.
I believe this is still relevant.
I started to try to hack this in as it is just process.env.REMOTE_USER or similar but the client side nature Vuejs and the Axios is a bit more than I can wrap my head around.
I found your 2.1.2 and it had a good start at the config race condition in main.js that I was running into with implementing headerAuth. I will say I'm not that good at async coding so there might be some big issues. I have it working for my use case which isn't what a maintainer likes to hear.
- It is currently hardcoded for my
REMOTE-USERmostly because I don't know how to accessgetAppConfig().headerAuth.userHeaderin the nodejs services/get-user.js which is one of the few things I need to make this configurable. - There is no documentation on how to use this. I'm using Authelia but this would work for Apache/NGINX basic/ldap auth too. Even Apache and mod_auth_mellon(SAML) would allow adding a username header.
- I'm using the built in users 'database' to get the hash and fake like a normal user instead of the bolt on that is Keycloak with it's own auth level.
I'll try to work on a WIP PR in a bit.
It looks like this missed 2.1.2. Should I try again? Lots of auth/permissions config could use server side help to limit information leak.
It looks like this missed 2.1.2. Should I try again? Lots of auth/permissions config could use server side help to limit information leak.
Sorry @toddejohnson, this slipped my mind. I'll look into this this week, and see if we can get something working :)
I started to try to hack this in as it is just process.env.REMOTE_USER or similar but the client side nature Vuejs and the Axios is a bit more than I can wrap my head around.
Yeah, it's annoying. If you need to read an env var client-side, prefix it with VUE_APP_ and then it can be read in Vue.
Edit: Got Authentik proxy working with Dashy 3.0 behind traefik with config below
users:
- user: user1
hash: removed
type: admin
- user: user2
hash: removed
enableHeaderAuth: true
headerAuth:
userHeader: X-Authentik-Username # the tag containing username according whoami
proxyWhitelist:
- "treafik.docker.network.ip" #
Edit: found root cause of reload loop
proxyWhitelist has to have traefik docker network ip since my dashy is on the same network
Otherwise response.data.user.toLowerCase() undefined in HeaderAuth.js causing .catch(() => window.location.reload());
to infinite loop
Food for thought @Lissy93
-
Maybe replace
.catch(() => window.location.reload());at the end of main.js with CoolConsole, at least won't be a reload loop -
proxyWhitelist seems to be cached until container restart or there might be some other cache, which made it seem like intermittent issue, maybe add some info to documentation.
-
perhaps make proxyWhitelist check against "172.16.0.0/12" notation rather than single IP in array?
-
up on saving to conf.yml via GUI, all previous manual #comment are deleted, it'd be nice if they can be kept
One more thing, Dashy's log out doesn't really work anymore with headerAuth via Authentik proxy. I don't think there is a good solution for this one. As long as signed into Authentik, the username header will always be there
@rxunique - You're absolutely correct, shouldn't be triggering a page reload there (not sure why I put that?!). I've updated.
I'm just looking into the header auth feature at the moment, and if I can get something working this evening, will merge as part of 3.0.1. But I'm struggling to understand what exactly needs implementing in Dashy to get this working. I've not really used Authelia, quickly tested it out with NGINX, and it seemed to work alright.
So any tips on what specifically isn't working or what needs to be added/amended would be appreciated :)
Oh pretty much nothing you need to implement, existing code works, documentation would be nice. It was quite head against the wall trying to figure it out, but with benefit of hindsight, it's so obvious.
The flow is like this
- Authtik proxy auth will add header
X-Authentik-UsernameX-Authentik-EmailX-Authentik-Groups - In dashy specify
auth.usersandauth.headerAuth.userHeader=X-Authentik-Username - In dashy specify the correct IP
That's it, if authentik username (or alternative fields) matches dashy users, you are in with per user authentication.
The difficulty I ran into, which may be improved on a back burner
- the page loop which you already fixed
proxyWhitelistseems to be cached on container start making testing not that easy, or maybe its something else, wasn't too sure- make
proxyWhitelistmatches IP by range (currently match by string). it will make things a lot easier
In my case, since all behind Traefik on the same docker network, it was my Traefik container ip 172.x.x.x.
Because I restarted different containers while trying to figure it out, their IP changed which made it quite a guess work with proxyWhitelist, now I've got key containers on static ip.
A generic OIDC would be superior than authetik proxy auth + dashy headerauth, but it works now.
Hey, just another idea to deal with whitelist.
Currently matching by string is just one line of code, matching by range would need quite a few extra line of code but still need some guess work.
Different people setup docker network and proxy differently, so proxy IP as appeared to dashy will always be different.
What about add another match condition like (proxySecret === X-Dashy-ProxySecret) || proxyWhitelist.includes() , its very easy to manually add a header 'X-Dashy-ProxySecret = MySecretHash' with proxy, certainly with treafik. That way no guess work with
The idea is to list the trusted IP of the reverse proxy that is trusted. If a request from another IP was made saying it was someone else logged in it shouldn't be trusted.