15 min read

Protecting Web Services with Authentik, Traefik and Azure AD

Setting up an Authentik Docker container to act as Identity Provider in combination with Traefik as reverse proxy. Optionally configuring Azure AD as Social Login SSO.
Protecting Web Services with Authentik, Traefik and Azure AD
Photo by Erik Mclean / Unsplash

In today's digital age, the importance of protecting exposed web services cannot be overstated. With the proliferation of online applications, it has become increasingly crucial to safeguard sensitive data and ensure secure access. While many applications offer their own authentication mechanisms, they are not immune to hacking attacks. The custom authentication may be bypassed or may be vulnerable to exploits. On the other hand, some apps may not even support authentication at all, leaving them vulnerable to unauthorized access and data breaches.

This is where Authentik, as an Identity Provider (IdP), steps in. Authentik provides an additional layer of security by acting as an authentication wall and access control mechanism for web services. It offers compatibility with various authentication protocols such as OpenID Connect, SAML, LDAP, and even Social Logins with platforms like Github, Facebook, Discord, Microsoft Azure AD and many more.

In this blog post, we will delve into how Authentik can be a powerful tool in fortifying your web services. We will setup an Authentik Docker container and configure it to use an already existing Traefik reverse proxy. Additionally or optionally, we will also configure Authentik to use Microsoft Azure AD as social login for user onboarding and authentication.

Spawning Authentik

To get started with Authentik, we will guide you through the process of setting up an Authentik Docker container via Docker Compose. Docker containers offer a streamlined way to deploy and manage applications, making it easier to integrate Authentik into your existing infrastructure.

Note that we assume an already existing Traefik reverse proxy. If you do not have one yet, may check out the examples at https://github.com/Haxxnet/Compose-Examples/tree/main/examples/traefik. Otherwise, if you already run a different reverse proxy, please consult the official Authentik documentation regarding your setup. You may be able to reuse some information of this blog post though nonetheless.

Docker Compose

To spawn an Authentik Docker service, you can use the below docker-compose.yml file (also availabe here). You can either define secrets directly in the compose file below or use a separate .env file. A separate one is the recommended approach.

The .env file must contain the following variables at least:

PG_USER=authentik
PG_DB=authentik

# generate via pwgen -s 40 1
PG_PASS=7jFjT4pUyf0YOlQ84LrO6JdLVWpzKEGiEMtdVwBE

# generate via pwgen -s 50 1
AUTHENTIK_SECRET_KEY=YZRzXecsKQVEJ3Lr5uoKRGXZkETsYjUDT1qtQ28JjzWzDYvcoG

.env

The compose file looks the following:

version: "3.4"

services:

  postgresql:
    image: docker.io/library/postgres:16-alpine
    container_name: authentik-psql
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/psql:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS:-authentik}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    env_file:
      - .env
    networks:
      - authentik-internal

  redis:
    image: docker.io/library/redis:alpine
    container_name: authentik-redis
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/redis:/data
    networks:
      - authentik-internal

  authentik-proxy:
    image: ghcr.io/goauthentik/server:2023.8
    container_name: authentik
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-authentik}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-authentiksupersecretkey}
    volumes:
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/media:/media
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/custom-templates:/templates
    expose:
      - 9000
      - 9443
    env_file:
      - .env
    depends_on:
      - postgresql
      - redis
    networks:
      - proxy
      - authentik-internal
    labels:
      - traefik.enable=true
      - traefik.http.routers.authentik.rule=Host(`authentik.example.com`) || HostRegexp(`{subdomain:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?}.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)
      - traefik.http.services.authentik.loadbalancer.server.port=9000
      - traefik.docker.network=proxy
      - 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

  worker:
    image: ghcr.io/goauthentik/server:2023.8
    container_name: authentik-worker
    restart: unless-stopped
    command: worker
    user: root
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-authentik}
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-authentiksupersecretkey}
    volumes:
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/certs:/certs
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/media:/media
      - ${DOCKER_VOLUME_STORAGE:-/mnt/docker-volumes}/authentik/custom-templates:/templates
    env_file:
      - .env
    depends_on:
      - postgresql
      - redis      
    networks:
      - authentik-internal

networks:
  proxy:
    external: true
  authentik-internal:
    external: true

docker-compose.yml

🛑
Ensure to use the Authentik version 2023.8.

For the latest version 2023.10 there is currently a bug affecting Federation & Social Logins - especially regarding Azure AD. For the affected versions it is not possible to save the OAuth settings, which will lead to the error message "Authentication failed: Could not determine id".

Docker Networks

As the above compose file uses two external Docker networks, you likely have to create those first before spawning up the compose stack. Also ensure to join your Traefik reverse proxy into the proxy network.

💡
We are utilizing two Docker networks in order to establish network separation. For example, the redis, database and worker container of Authentik do not need access to the proxy network, where Traefik is in.

Traefik as reverse proxy will solely communicate with the Authentik server service. Therefore, the Docker services are in different networks.

Follow these steps to create the networks:

# create revelant docker networks
docker network create proxy
docker network create authentik-internal

# join traefik container to the proxy network
# alternatively, directly define it in traefik's compose file for persistence
docker network connect proxy traefik

creating the relevant docker networks

Afterwards, it should be a matter of:

docker compose up

spawning the Authentik Docker stack

Authentik Middleware

Now that we have Authentik up and running, we can adjust our proxy services to use Authentik as middleware for authentication.

If you are firm with Traefik, you will already know that most configuration settings for proxy services are defined via Traefik labels. For example, directly in your relevant Docker Compose file.

For this blog post, we will use Traefik's whoami container as an example:

  version: '3.8'
  
  whoami:
    image: traefik/whoami
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
      - traefik.http.services.whoami.loadbalancer.server.port=80
      - traefik.docker.network=proxy
      - traefik.http.routers.whoami.middlewares=authentik@docker

networks:
  proxy:
    external: true

docker-compose.yml

As can be seen in the above compose example, the only thing we have to define in our proxy service, in order to use Authentik, is the authentik@docker middleware via Traefik labels. That's it, your application will then be proxied over Authentik, which handles authentication as well as access control.

Traefik: New authentik@docker middleware available after spawning Authentik
💡
Note that the whoami container does not provide authentiation. Therefore, Authentik will act as the single authentication wall to allow or restrict access. If you have other proxy services that already provide their own authentication scheme, Authentik will be used as an additional authentication wall in front of the regular one of your proxy service. You will still have to login to the actual proxy service.

If your service supports it, you may be able to configure Authentik as SSO provider (e.g. OAuth2/OpenID). Portainer for example supports this. May read this blog post.

However, as we have not yet configured Authentik, authentication will fail and not yet work. We have to create a user account, define an authentication provider as well as an application for the whoami container in Authentik first.

Let's go ahead.

Configuring Authentik

After spawning up the Authentik container, your Traefik reverse proxy should automatically detect the new container via the Docker provider. Therefore, the Authentik web application should be available at your defined hostname in the Traefik labels.

Traefik: New routers for Authentik available

Creating a local admin user

Browsing the Authentik HTTP URL directly won't work yet. You have to browse an initialization URL and configure an admin user first.

Go ahead and browse: https://authentik.yourdomain.com/if/flow/initial-setup

Follow the instructions and create your admin user. Afterwards, login with the created account credentials. You will be presented with the Authentik admin dashboard, asking you to create a new application.

💡
You can skip creating a new application. Just browse the root URL to access the regular admin dashboard.

Setting up Providers

In order to use Authentik, we have to setup providers as well as applications. For each of your proxy services, you have to setup an individual provider and of course the individual application, which uses the provider.

For this blog post, we will keep using the above whoami container as an example.

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Applications > Providers
  3. Create a new provider via the button Create
  4. Select Proxy Provider and hit Next
  5. Define a name e.g. Whoami ForwardAuth
  6. Choose an Authentication and Authorization flow. I recommend the explicit ones for the beginning. Explicit ones will show an additional weg page after authentication to users, stating to which service will be redirected. The other option will just redirect the user to the service without an additional info page.
  7. Choose Forward auth (single endpoint)
  8. Define the subdomain of your proxy service. Here, it would be https://whoami.example.com
  9. Finish the provider setup by clicking Finish
Authentik: Creating a new provider

Setting up Applications

After creating our first provider, we now have to create our application. Remeber, this is a 1:1 relationship. For each proxy service, we need one provider and one application.

For this blog post, we will keep using the above whoami container as an example.

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Applications > Applications
  3. Create a new provider via the button Create
  4. Define a name e.g. Whoami
  5. Select the previously created provider Whoami ForwardAuth
  6. Optionally upload an icon and change some settings under UI Settings
  7. Finish the application setup by clicking Finish
Authentik: Creating a new application

Setting up Outposts

After setting up an provider as well as an application, we have to onboard our application in the Authentik outposts.

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Applications > Outpots
  3. Select the default outpost and the action edit
  4. At Applications select all previously created applications. You can multi-select by holding SHIFT on your keyboard.
  5. At Configuration, ensure that the key authentik_host is provisioned to your FQDN URL of your Authentik instance.
  6. Finish the outpots setup by clicking Update
Authentik: Updating the default outpost with the new application(s)
Authentik: Re-checking the authentik_host FQDN URL

Setting up Azure AD

One of the standout features of Authentik is its ability to integrate with external providers like Github, Facebook, Discord, Azure AD and many more as so-called Social Logins. For this blog post, we will focus on Azure AD.

Social login - Wikipedia
💡
If you do not have an Azure AD tenant, you can skip this step and create your local users manually within Authentik at Directoy > Users.

A social login integration like Azure AD enables you to onboard your Azure AD users into Authentik automatically. As soon as a successful authentication occurs (Authentik <> OAuth Azure AD), the user will be created locally in Authentik with the user information obtained from Microsoft Azure. You are freed from creating your employees manually, yeah!

Create Azure App

In order to setup Azure AD as social login, follow these steps:

  1. Log into the Azure portal (now called Entra) and create a new application
  2. Follow the instructions provided by Authentik
  3. Especially keep note of the Client ID and Tenant ID. These will be later used within Authentik to create the social login integration.
  4. Also head to Certificates and Secrets and create a new Client Secret. Keep note of the Secret Value. It will later be used within Authentik to create the social login integration.
  5. As redirect URL, you must define the FQDN of your Authentik service like https://authentik.example.com/source/oauth/callback/azure-ad. The slug azure-ad is important, as it must be reflected within Authentik later on.
  6. At API Permissions, hit Grant admin consent for xxx. Otherwise, you will obtain permission errors during login.
Azure Portal: Client and tenant ID of new application
Azure Portal: Secret Value of new application
Azure Portal: Granting admin consent at API permissions

Then head over to Authentik.

Create Social Login

Adhere to the following steps:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Directory > Federation and Social login
  3. Create a new social login via the button Create
  4. Choose Azure AD Oauth Source
  5. Define Azure AD in the field Name
  6. Ensure the field Slug was propagated to azure-ad correctly. Otherwise, change it manually to azure-ad. This is the important slug mentioned previously when setting things up in the Azure Portal.
  7. Define the IDs and secrets created previously in the Azure portal. Consumer Key is the Client ID. Consumer Secret is the Secret Value.
  8. At the fields Authorization URL and Access token URL, replace the value common in both URLs with your Tenant ID. Just replace the single word common with your ID string.
  9. Finish the setup by clicking Finish

After setting up our Azure AD source as social login, we have to adjust Authentik. As default, the new source won't be available at the Authentik login screen. We first have to configure the new source as an option during authentication.

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Flows and Stages > Flows
  3. Select default-authentication-flow
  4. Select Stage-Bindings
  5. Select default-authentication-identification and Edit
  6. Drop down Sources and select our new source Azure-AD. You can multi-select by holding SHIFT on your keyboard.
  7. Finish the flow setup by clicking Update
Authentik: Enabling Azure-AD Social Login at Flows and Stages
Authentik: Azure AD as Social Login available

Your Azure AD tenant users can now authenticate by selecting the Azure AD Microsoft icon below the login button. An Oauth flow will occur and the user account will be created in Authentik and the user being logged in.

Note that local authentication is still provided by Authentik. For example, if you want to login with your admin user created during the init phase.

🛑
If your users choose to authenticate via Azure AD, they have the option to change their local Authentik user password as well. Therefore, users may set a password and then proceed using the local authentication by supplying their e-mail and the set password instead of using Azure.

Be aware that both authentication methods work natively (local auth and social login). Therefore, think about setting up a local password policy.

Otherwise, you can also change this behaviour. Keep on reading.

Disable Local Auth

If you want to provide authentication via Azure or any other social login only, you have to disable Authentik's default local authentication.

🛑
Important: Please only do this after properly setting up Authentik. Ensure that one of your Azure AD users was successfully onboarded into Authentik and manually upgraded as authentik admin. Otherwise, you won't be able to log into Authentik as admin again, as local auth won't be supported anymore. Conduct thorough testing before doing this change.

Follow these steps to disable local authentication and support social logins only:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Flows and Stages > Flows
  3. Select default-authentication-flow
  4. Select Stage-Bindings
  5. Select default-authentication-identification and Edit
  6. Drop down Sources and select your social login source solely. Remove authentik Built-in from the selection.
  7. Go to Stage-specific settings and unselect the fields Username and Email at the User fields section. Use SHIFT and/or CRTL to unselect all entries.
  8. Finish the flow setup by clicking Update

Atferwards, there won't be an Authetik login screen anymore. You'll be directly redirected to MS Azure or your configured social login source.

Disable Password Change

Finally, if Azure was configured as sole authentication option, you may want to remove the useless password change option. As local auth won't be supported anymore, changing a user's password won't have any effect.

💡
This step is totally optional.

I recommend doing it after disabling Authentik's local authentication method though. This ensures that user's cannot change their password in the UI, as this won't have any technical effect for the Azure SSO login.

The local user's password in Authentik is irrelevant for Azure OAuth.

To disable password change, follow these steps:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Flows and Stages > Flows
  3. Select default-authentication-flow
  4. Select Stage-Bindings
  5. Select default-authentication-password and Edit
  6. Choose empty ------ at Configuration flow to disable password changes
  7. Finish the flow setup by clicking Update

This removes the Change password button from the user settings area of Authentik.

Granting or Denying Access

With Authentik and Azure AD as social login in place, you can start configuring fine-grained control over who can access your web services. You can grant or deny access based on individual users or even groups.

Such fine-grained access controls are configured in Authentik as admin at Applications > Applications. If you remember correctly, we have already created such application for our example whoami container. Let's open this one again and inspect the tab Policy / Group / User Bindings.

Within this tab, we can create a new policy. Simply hit Bind existing policy and choose from either User or Group. Select your individual users or a group. Or both via multiple bindings.

Authentik: Restriction access based on user or group bindings
🛑
As long as no policy is defined for an application, access to your proxy services is granted to all available users within Authentik. No access restrictions are in place per default. This means, any Azure AD tenant user can access your proxy service configured in Authentik (auth provider + app).
💡
Note that your Azure AD users will only be availabe as user object within Authentik after a successful social login took place. Otherwise, the users will not yet be onboarded within Authentik.

If you want to grant access to a specific group only, head over to Directory > Groups and create/modify/adjust to your liking. You can also assign a default group for newly created users or disable automatic user creation completely (read here).

Authenticated users will always have access to the Authentik user interface. This interface will list all applications the current user has permission for to access. It basically acts as a user dashboard with a list of accessible applications.

Authentik: User interface lists available applications

Local Password Policy

In case you are not using Azure AD solely but local authentication too, you may want to introduce a local password policy. Authentik users are per default allowed to change their password after successfully logging into Authentik. This is done via the user's settings area at Change password.

Per default, Authentik does not come with a password policy for local users. So your users may choose a weak password that is easy to remember but also easy to guess as an attacker.

Therefore, if you want to introduce a password policy into Authentik, follow these steps:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Customisation > Policies
  3. Hit the button Create and choose Password Policy
  4. Define a name (e.g. Password Policy) and your preferred policy requirements
  5. Click the button Finish
  6. Afterwards, browse to Flows and Stages > Stages
  7. Select default-password-change-prompt and the action Edit
  8. At Validation Policies select your previously created password policy (e.g. Password Policy)
Authentik: Enabling a custom password policy

Custom Branding

You can also rebrand Authentik with your company logos and custom wallpapers.

In order to change the Authentik logo and favicon:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to System > Tenants
  3. Select the default tenant and the action Edit
  4. Set your preferred logos at Branding settings
  5. Hit Update when finished
💡
Authentik wants local file paths. Therefore, you have to place your media files into the volume bind mounts of the Authentik server container accordingly.

In order to change the background wallpaper for the login or consent screens:

  1. Log into Authentik as admin and visit the admin interface
  2. Browse to Flows and Stages > Flows
  3. Select default-authentication-flow and the action Edit
  4. Upload your background image at Appearance settings
  5. Hit Update when finished

Repeat the same for the flows:

  • default-provider-authorization-explicit-consent
  • default-provider-authorization-implicit-consent
  • default-source-authentication
  • default-invalidation-flow
Authentik: Custom background image works

Summary

If you have followed this blog post, you will have successfully configured Authentik in conjunction with Traefik as reverse proxy and Azure AD as social login. Your Azure AD tenant users can now access proxy services by choosing Azure AD as SSO option at the Authentik login screen.

If Azure AD authentication succeeds, the Azure AD user will be onboarded and created as local user in Authentik. If access controls permit access to the requested proxy service, the user will be able to instantly gain access.

By choosing this setup, we successfully established a single authentication entry point for all our proxy services without the hassle of creating Azure AD users manually within Authentik. The social login integration will handle the authentication flow between Authentik and Microsoft Azure AD and onboard AD users accordingly.

You can now start grouping users and granting fine-grained access controls to all of your services. To add more proxy services into Authentik, here is a short summary of the required steps:

  1. Adjust the proxy service's Traefik labels to use the authentik@docker middleware.
  2. Log into Authentik as admin and create a new authentication provider at Applications > Providers. Choose a fitting name depending on your proxied service. May read this blog's relevant section again.
  3. Log into Authentik as admin and create a new application at Applications > Applications. Choose a fitting name depending on your proxied service and select the previously created authentication provider. Also visit the Policy / Group / User Bindings area and configure access controls, if necessary.
  4. Test accessing your proxy service.

Enjoy!