Open Mijago opened 1 year ago
Relevant documentation links:
Maybe Authentic can do this already right from the interface, then I just missed it - nonetheless, the discussion in this issue can probably improve the setup documentation for Authentic.
Sound slike you're proposing some tighter coupling between the roles. In matrix-docker-ansible-deploy we did stuff like this - you enable some component and via group vars it becomes automatically wired into other components.
It's very convenient, but.. it's also very magical and makes it hard to work on the playbook later on.
With multiple competing implementations of the same thing, the problem gets worse.
For this MASH playbook, we're intentionally trying to keep roles more independent and to not auto-wire them automagically via group vars. As the number of roles gets larger, it would lead to implementation complexity and unexpected bugs to have things wired automatically.
Our goal with MASH is to provide good high quality and customizable roles, which users can plug together as they see fit. No assumptions. No magic.
That said, roles may be made to expose some way to customize the middleware labels and/or to inject a forwardauth
middleware, thus making it easy to plug Authentik (and Authelia or other such implementations) in the future.
I think it would be great to have an example and generic setup Forward Auth of that in the documentation!
Implement a proof-of-concept for one service, then migrate it to all other services with http endpoint/s.
I think picking a role like prometheus which traditionally uses basic auth could be a good start. Are you going to do that?
This probably must be added to every service, but after it has been set up once it can probaby be copy-pasted to every other container.
I thnik I'd be in favor of this. We should turn it off by default and not do some magic there but it would be nice to have it set up as easy as e.g. coupling redis to nextcloud (so one/a few entries in vars.yml
set the middleware up as @spantaleev described)
We already reuse large parts of the traefik middleware, so adding this middleware to every role would be similar to what we already do.
Hi, sadly I do not have as much time to play around as I anticipated. I hope I can get back to this next week and try a few things.
A small documentation of the things I learned so far:
Authentik can be used to add authentication to services that do not have their own authentication, using the "forward-auth" from our traefik.
We can either have
In all cases, a service itself will just be modified with one simple traefik label to add the authentik@docker
rule:
whoami:
image: containous/whoami
labels:
traefik.enable: true
traefik.http.routers.whoami.rule: Host(`auth.MYDOMAIN.net`)
traefik.http.routers.whoami.middlewares: authentik@docker
restart: unless-stopped
No matter which scenario we have, we need (at least) one ghcr.io/goauthentik/proxy
container. This container forwards Host(
auth.MYDOMAIN.net) && PathPrefix(
/outpost.goauthentik.io/)
to the authentik middleware.
In my case I have Authentik at auth.MYDOMAIN.net
and only have one proxy for all services:
Example from the documentation:
authentik-proxy:
image: ghcr.io/goauthentik/proxy
ports:
- 9000:9000
- 9443:9443
environment:
AUTHENTIK_HOST: https://auth.MYDOMAIN.net
AUTHENTIK_INSECURE: "false"
AUTHENTIK_TOKEN: token-generated-by-authentik
# Starting with 2021.9, you can optionally set this too
# when authentik_host for internal communication doesn't match the public URL
# AUTHENTIK_HOST_BROWSER: https://external-domain.tld
labels:
traefik.enable: true
traefik.port: 9000
traefik.http.routers.authentik.rule: Host(`auth.MYDOMAIN.net`) && PathPrefix(`/outpost.goauthentik.io/`)
# `authentik-proxy` refers to the service name in the compose file.
traefik.http.middlewares.authentik.forwardauth.address: http://authentik-proxy:9000/outpost.goauthentik.io/auth/traefik
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: true
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: 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
restart: unless-stopped
General steps are like this:
Provider
. Select Proxy Provider
, fill Name
and External host
and pick a flow.Application
. Enter a name and select your previously created provider.Base Domain
, Type Proxy
. Select the previously created Application.That's it.
I'll show an example to fill X-authentik-email
with the email of the authenticated user. More info here.
Property Mapping
. Like this:
return {
"ak_proxy": {
"user_attributes": {
"additionalHeaders": {
"X-authentik-email": request.user.email
}
}
}
}
We do not need to add a lot:
ghcr.io/goauthentik/proxy
containerauthentik@docker
. The default installation contains a embedded proxy, so the additional container is not needed.
What I did to implement the forward auth
and proxy
is the following. I'm using it for external services so not with mash services and traefik labels yet.
Authentik acts as forwardAuth middleware. Traefik should proxy all services with PathPrefix(`/outpost.goauthentik.io/`)
. The service itself needs the middleware.
devture_traefik_provider_configuration_extension_yaml: |
http:
middlewares:
authentik:
forwardAuth:
address: http://{{ authentik_server_identifier }}:{{ authentik_container_http_port }}/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- 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
routers:
test-one:
rule: "Host(`test2.example.com`)"
middlewares:
- authentik
service: test-one
services:
test-one:
loadBalancer:
servers:
- url: http://test2.example.internal
# Custom variable for keeping list and mapping to host rule
authentik_forward_auth_hosts:
- "test2.example.com"
authentik_forward_auth_host_rule: "Host({{ authentik_forward_auth_hosts | map('regex_replace','^(.+)$','`\\1`') | list | join(', ') }}) && PathPrefix(`/outpost.goauthentik.io/`)"
Authentik acts as reverse proxy. Traefik should send all traffic for services to authentik.
# Custom variable for keeping list and mapping to host rule
authentik_proxy_hosts:
- "test1.example.com"
authentik_proxy_host_rule: "Host({{ authentik_proxy_hosts | map('regex_replace','^(.+)$','`\\1`') | list | join(', ') }})"
The main Authentik Traefik rule needs to be altered with the rules above.
authentik_container_labels_traefik_rule: "Host(`{{ authentik_container_labels_traefik_hostname }}`) || ({{ authentik_proxy_host_rule }}) || ({{ authentik_forward_auth_host_rule }})"
I think we should introduce in the authentik role some variables to define the proxy and forward hosts and fill them in the authentik_container_labels_traefik_rule
like above. Besides that we should make it possible to define additional middlewares for the services.
Hello,
Now that we have Authentic (and keycloak), I want to propose that we add flags that allow us to secure any service with Authentic. We may want to add more services in the future, also services that do not have their own login methods. We could easily secure them behind Authentic now.
If I understood the documentation correctly, then the proxy provider sounds like it is made for exactly this scenario: This supports traefik's forwardAuth labels, so in theory, a few simple flags could already solve this.
Scope of this proposal:
Forward Auth (single application)
orForward Auth (domain level)
. From my understanding, both have their benefits. The latter seems "simpler" and "easier to setup", but I did not yet read a lot in the documentation.Implement a proof-of-concept for one service, then migrate it to all other services with http endpoint/s.
Benefits of this proposal:
With this feature, we can add services that have no own authentication and still make them available to the public (behind Authentic), thus mitigating risk of exposure.
Drawbacks of this proposal: