Skip to main content

Authenticating Traefik Apps with Authentik

 Introduction

In our previous post, we secured the Homer app with trusted Let's Encrypt certificates using Traefik as a reverse proxy. But what if only authorized users should access Homer? In this blog, we'll address this by adding multi-factor authentication to Homer using Authentik as an Identity Provider (IdP).

Objective

The core objectives of this tutorial are to:
  • Set Up Secure Access with Authentik: Install Authentik using Docker Compose and create your first user to manage access control.
  • Secure Homer with Authentik: Configure Authentik to act as a gatekeeper, ensuring only authorized users can access your Homer application.
  • Simplify Logins with Traefik: Integrate Traefik with Authentik to enable Single Sign-On (SSO) for a seamless login experience across your applications.
  • Connect Homer to Authentik: Configure Homer to leverage Authentik's authentication system for secure logins.

Topology


For the topology details please see the previous post

Access Flow

Homer can be accessed through https://homer.mydomain.com. Here's what happens behind the scenes when you try to visit it:
  1. Login Check: Traefik first checks with Authentik, to see if you're already logged in.
  2. Login Required: If you're not logged in, Traefik automatically redirects you to a login page on Authentik, located at https://authentik.mydomain.com.
  3. Successful Login: Once you log in successfully on Authentik, you'll be granted access to Homer.

Remember: Replace mydomain.com with your actual domain name.

Assumptions

You have followed the steps outlined in the previous post.

Setup Secure Access with Authentik

Let's start by creating an authentik directory
 mkdir authentik
 cd authentik
 vim docker-compose.yml 
Add the provided content to your compose file. This file was downloaded from the Authentik site and minor modifications were made
 ---
version: "3.4"

services:
  postgresql:
    image: docker.io/library/postgres:12-alpine
    container_name: authentik-db
    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:
      - database:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS:?database password required}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    env_file:
      - .env
    networks:
      - traefik_web
  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:
      - redis:/data
    networks:
      - traefik_web
  server:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.2.2}
    container_name: authentik-server
    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}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    env_file:
      - .env
    depends_on:
      - postgresql
      - redis
    labels: # auto-detects port 80 in next line
      traefik.enable: true
      traefik.http.routers.authentik.tls: true
      traefik.http.routers.authentik.rule: Host(`authentik.mydomain.com`)
      traefik.http.routers.authentik.tls.certresolver: letsencrypt
      traefik.http.routers.authentik.entrypoints: websecure
      traefik.http.routers.authentik.service: authentik-svc 
      traefik.http.services.authentik-svc.loadBalancer.server.port: 9000
    networks:
      - traefik_web
  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.2.2}
    container_name: authentik-worker
    restart: unless-stopped
    command: worker
    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}
    # `user: root` and the docker socket volume are optional.
    # See more for the docker socket integration here:
    # https://goauthentik.io/docs/outposts/integrations/docker
    # Removing `user: root` also prevents the worker from fixing the permissions
    # on the mounted folders, so when removing this make sure the folders have the correct UID/GID
    # (1000:1000 by default)
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./media:/media
      - ./certs:/certs
      - ./custom-templates:/templates
    env_file:
      - .env
    depends_on:
      - postgresql
      - redis
    networks:
      - traefik_web
networks:
  traefik_web:
    external: true

volumes:
  database:
    driver: local
  redis:
    driver: local
The Postgres password and Authentik secret key are stored in a .env file. Let's create it in the authentik folder
 echo "PG_PASS=$(openssl rand -base64 36)" >> .env
 echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 36)" >> .env
 echo "AUTHENTIK_ERROR_REPORTING__ENABLED=true" >> .env
Once the installation is complete, navigate to http://authentik.mydomain.com/if/flow/initial-setup/ and login with username akadmin. Follow the steps to create a password and login to Authentik Web UI 

Create your first user in Authentik

  1. Navigate to Admin Interface
  2. Select Directory --> Users
  3. Click on Create
    1. Username - Pick your username
    2. Name - Your Name
    3. User Type - Internal
    4. Email - Provide your email
    5. Is Active - Should be selected by default
    6. Hit Create
  4. Select Groups
  5. Click on authentik Admins
  6. Click on Users --> Add existing users
  7. Add your username and Click Add
  8. Navigate to back to Users and Select your username
  9. Click on Set password to create your password
  10. Logout and log back in with the new user
  11. Navigate to Admin Interface
  12. Select Directory --> Users
  13. Select akadmin user and Deactivate it under Actions
  14. Go back to the Dashboard
  15. Navigate to the Settings on the top corner
  16. Click on MFA Devices
  17. Click on Enroll and choose TOTP Device
  18. Follow the instructions to enable MFA by scanning the QR code using the Google Authenticator App for example
  19. Log out and log in to verify MFA is enabled
That's it! An Authentik user is now configured with MFA

Secure Homer with Authentik

Now lets configure Authentik to secure Homer
  1. Navigate to the Admin Interface
  2. Select Application --> Providers --> Create
  3. Select Proxy Provider and hit Next
    1. Name - homer
    2. Authentication flow - Leave it blank
    3. Authorization flow - default-provider-athorization-implicit-consent
    4. Select Forward auth (single application)
    5. External host - https://homer.mydomain.com
    6. Hit Finish
  4. Navigate to Applications --> Application
    1. Name - homer
    2. Slug - homer
    3. Provider - Proxy Provide you created in the previous step - homer
    4. Hit Create
  5. Navigate to Applications --> Outposts
    1. Edit authentik Embedded Outpost
    2. Add homer to the Selected Applications space
That's it! Authentik is all setup to secure homer with SSO and MFA. Now let's configure traefik to forward requests for Authentik.

Simplify Logins with Traefik

Picking up from the previous post, we need to update the traefik docker-compose with 3 additional lines

Under the command section, add the following:
 command: # Static file is not required if using commands
      - --providers.file=true
      - --providers.file.directory=/etc/traefik/conf/
Under the volumes section, add the following:
 volumes:
      - ./conf:/etc/traefik/conf/
Here is the full traefik docker-compose.yml file
 version: "3.3"
services:
  traefik:
    container_name: traefik
    image: traefik:latest # Download traefik
    restart: always # Restart the container if stops
    ports:
      - 443:443 # Expose port 443 on the host machine
      - 80:80 # Expose port 80 on the host machine
    command: # Static file is not required if using commands
      - --providers.docker=true
      - --providers.file=true
      - --providers.file.directory=/etc/traefik/conf/
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entryPoint.to=websecure
      - --entrypoints.web.http.redirections.entryPoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.forwardedheaders.insecure=true
      - --serverstransport.insecureskipverify=true 
      - --log.level=DEBUG
      - --api=true
      - --api.insecure=true
      - --api.dashboard=true
      - --api.debug=true
      - --entrypoints.websecure.http.tls.certResolver=letsencrypt
      - --entrypoints.websecure.http.tls.domains[0].main=mydomain.com
      - --entrypoints.websecure.http.tls.domains[0].sans=*.mydomain.com
      # It is recommended you uncomment the next line and try out if you get the trusted certs. Once verifed, you can comment it and restart the container
      #- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.letsencrypt.acme.email=your_cloudflare_email
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
      - --certificatesResolvers.cfresolver.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53
    secrets:
      - cf_email
      - cf_api
    environment:
      # you may choose to use secrets instead of environment variables like this
      CF_API_EMAIL_FILE: /run/secrets/cf_email
      CF_API_KEY_FILE: /run/secrets/cf_api 
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock # Allow Traefik access to docker API's to dynamically learn applications
      - ./certs:/letsencrypt
      - ./conf:/etc/traefik/conf/
    labels:
      # Traefik uses lables to learn about containers and dynamically configures itself. Expose the traefik dashboard on port 443
      traefik.enable: true
      traefik.http.routers.traefik.tls: true
      traefik.http.routers.traefik.rule: Host(`traefik.mydomain.com`)
      traefik.http.routers.traefik.entrypoints: websecure
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.tls.certresolver: letsencrypt
        # Use the custom network web that is defined below
    networks:
      - web
networks:
  web:
secrets:
  cf_email:
    file: ./secrets/cf_email.secret
  cf_api:
    file: ./secrets/cf_api.secret
Navigate to the traefik directory and create a conf directory with headers.yml
 mkdir conf
 cd conf
 vim headers.yml 
Add the provided content to your file. Note authentik-server is the name of our container
 http:
    middlewares:
        authentik:
            forwardAuth:
                address: http://authentik-server:9000/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
Navigate to the traefik folder and restart treaefik
 docker-compose up -d --force-recreate
Traefik should automatically apply the middleware. You can verify if the middleware was applied by navigating to https//traefik.mydomain.com. Now let's update homer to use the middleware authentik 

Connect homer to Authentik 

Navigate to the homer folder and open docker-compose.yml Add the following label. 
Note, authentik@file is the name of our middleware. If you used a different name, please use that.
 traefik.http.routers.homer1.middlewares: authentik@file
Restart homer
 docker-compose up -d --force-recreate
That's it! When you navigate to https://homer.mydomain.com, you will be greeted with an Authentik login.

References

Comments

Popular posts from this blog

Run your Meteor App on a Production Ubuntu 16.04 Server with Nginx

Introduction Meteor enables developers to create apps and quickly test them on a development webserver. Once you have created your app the big question is how to run it on a production server. In this tutorial we will demostrate how to run your Meteor app on Ubuntu 16.04 using Nginx Credits and Acknowledgements This entire post is based off this Digital Ocean article. The article was modified for issues we encountered and adapted for Ubuntu 16.04. The entire credit goes to Daniel Speichert Objective In this tutorial we will: Install and Configure Nginx with HTTPS enabled Install MongoDB Install NodeJS Bundle your Meteor App Create a startup script to automatically start your app on reboot Assumptions We assumue the following: You already have a fresh install of Ubuntu 16.04 Server SSH enabled on your fresh install You have root privelages on your server You have a different server where you can insall meteor and budle your app...

Traefik Install and configuration - Part 1

Introduction In this tutorial we are going to install and configure Traefik Proxy . As quoted on their website "Traefik is a leading modern reverse proxy and load balancer that makes deploying microservices easy. Traefik integrates with your existing infrastructure components and configures itself automatically and dynamically." All our applications will be front-ended by traefik. Objective The core objective for this tutorial is to: Configure an A record in our DNS server (pfsesne firewall) to point to our traefik proxy Configure and install Traefik in docker Configure and install two http based applications ( homer and heimdall ) in docker that will be accessed via traefik Topology The above image represents our topology. Our pfsense firewall is connected directly to the Internet and is NAT'ing all traffic from the internal network to the outside world. We have a single LAN segment (192.168.11.0/24) defined on pfsense and we have the following two PC's connected d...