In this post, we will be looking at Authelia which is an authentication and authorization service using Traefik on Docker containers. We will explore how to secure our web services and use single sign-on with multi-factor authentication.
Authelia supports LDAP as a backend provider, but for this demonstration, we will use the file provider as our backend for our users.
About
From Authelia's website:
Authelia is an open-source authentication and authorization server and portal fulfilling the identity and access management (IAM) role of information security in providing multi-factor authentication and single sign-on (SSO) for your applications via a web portal. It acts as a companion for common reverse proxies.
Prerequisites
I will be using Traefik Proxy, If you are following along, you can find a tutorial to get Traefik installed, below:
- https://containers.fan/posts/setup-traefik-v2-docker-compose/
Setup
Create the authelia project directory:
mkdir -p authelia-demo/{data,config}
cd authelia-demo
Create the docker-compose.yml
with the following content:
version: '3.8'
services:
authelia-service:
image: authelia/authelia:4
container_name: authelia-service
restart: unless-stopped
environment:
- TZ=Africa/Johannesburg
volumes:
- ./config:/config
- ./data:/data
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.authelia.rule=Host(`auth.demo.containers.fan`)'
- 'traefik.http.routers.authelia.entrypoints=https'
- 'traefik.http.routers.authelia.tls=true'
- 'traefik.http.routers.authelia.tls.certresolver=letsencrypt'
- 'traefik.http.middlewares.authelia.forwardauth.address=http://authelia-service:9091/api/verify?rd=https://auth.demo.containers.fan'
- 'traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true'
- 'traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email'
depends_on:
- authelia-redis
networks:
- docknet
logging:
driver: "json-file"
options:
max-size: "1m"
authelia-redis:
image: redis:7
container_name: authelia-redis
restart: unless-stopped
environment:
- TZ=Africa/Johannesburg
networks:
- docknet
logging:
driver: "json-file"
options:
max-size: "1m"
one-factor-service:
image: ruanbekker/web-center-name-v2
container_name: one-factor-service
restart: unless-stopped
environment:
- APP_TITLE=One Factor Service
- APP_URL=
- APP_TEXT=
depends_on:
- authelia-service
networks:
- docknet
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.one-factor.rule=Host(`one-factor.demo.containers.fan`)'
- 'traefik.http.routers.one-factor.entrypoints=https'
- 'traefik.http.routers.one-factor.tls=true'
- 'traefik.http.routers.one-factor.tls.certresolver=letsencrypt'
- 'traefik.http.routers.one-factor.middlewares=authelia@docker'
- 'traefik.http.routers.one-factor.service=one-factor-service'
- 'traefik.http.services.one-factor-service.loadbalancer.server.port=5000'
logging:
driver: "json-file"
options:
max-size: "1m"
two-factor-service:
image: ruanbekker/web-center-name-v2
container_name: two-factor-service
restart: unless-stopped
environment:
- APP_TITLE=Two Factor Service
- APP_URL=
- APP_TEXT=
depends_on:
- authelia-service
networks:
- docknet
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.two-factor.rule=Host(`two-factor.demo.containers.fan`)'
- 'traefik.http.routers.two-factor.entrypoints=https'
- 'traefik.http.routers.two-factor.tls=true'
- 'traefik.http.routers.two-factor.tls.certresolver=letsencrypt'
- 'traefik.http.routers.two-factor.middlewares=authelia@docker'
- 'traefik.http.routers.two-factor.service=two-factor-service'
- 'traefik.http.services.two-factor-service.loadbalancer.server.port=5000'
logging:
driver: "json-file"
options:
max-size: "1m"
public-service:
image: ruanbekker/web-center-name-v2
container_name: public-service
restart: unless-stopped
environment:
- APP_TITLE=Public Service
- APP_URL=
- APP_TEXT=
depends_on:
- authelia-service
networks:
- docknet
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.public.rule=Host(`public.demo.containers.fan`)'
- 'traefik.http.routers.public.entrypoints=https'
- 'traefik.http.routers.public.tls=true'
- 'traefik.http.routers.public.tls.certresolver=letsencrypt'
- 'traefik.http.routers.public.middlewares=authelia@docker'
- 'traefik.http.routers.public.service=public-service'
- 'traefik.http.services.public-service.loadbalancer.server.port=5000'
logging:
driver: "json-file"
options:
max-size: "1m"
networks:
docknet:
name: docknet
The config in config/configuration.yml
:
---
server.host: 0.0.0.0
server.port: 9091
log:
level: debug
jwt_secret: c50498e29383564cd50bdeda9b74a3bf
default_redirection_url: https://public.demo.containers.fan
totp:
issuer: demo.containers.fan
authentication_backend:
file:
path: /config/users_database.yml
access_control:
default_policy: deny
rules:
- domain: public.demo.containers.fan
policy: bypass
- domain: one-factor.demo.containers.fan
policy: one_factor
- domain: two-factor.demo.containers.fan
policy: two_factor
session:
name: authelia_session
secret: unsecure_session_secret
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
domain: demo.containers.fan # Should match whatever your root protected domain is
redis:
host: authelia-redis
port: 6379
regulation:
max_retries: 3
find_time: 120
ban_time: 300
storage:
encryption_key: f30ebde68b2c85c1b3fe2d16d9884190
local:
path: /data/db.sqlite3
notifier:
smtp:
username: apikey
password: secret
host: smtp.sendgrid.net
port: 587
sender: no-reply@mydomain.com
Generate a password hash:
docker run --rm -it authelia/authelia:4 authelia hash-password password123
# $argon2id$v=19$m=65536,t=1,p=8$ek9jRWNRbkhPMVNNWWpreg$myvpTCREAwhITbcD80d2Ae5+pdIK3Y3SSNuLSU8dezw
The user database defined in config/users_database.yml
:
---
users:
authelia:
displayname: "authelia"
password: "$argon2id$v=19$m=65536,t=1,p=8$ek9jRWNRbkhPMVNNWWpreg$myvpTCREAwhITbcD80d2Ae5+pdIK3Y3SSNuLSU8dezw"
email: x@x.com
groups:
- admins
- dev
ruan:
displayname: "Ruan"
email: "x@gmail.com"
password: "$argon2id$v=19$m=65536,t=1,p=8$Q3YwxUdRTnhDbVkxS1JBVx$TNnK9Fku8QfnWovquhdkixDNBn0juhN1upSY9fRcVzA"
groups:
- admins
- dev
Boot the stack:
docker-compose up -d
Access our Services
First, we will access the public service:
Next, we will access the one-factor service on https://one-factor.demo.containers.fan then we will be redirected to auth.demo.containers.fan:
Then we provide the username and password combination that we provided in config/user_database.yml
, which was ruan
as the username and the password password123
, then if the credentials were passed correctly, we will be redirected to our service:
For our two-factor service, we first need to login to Authelia and create an MFA device on https://auth.demo.containers.fan:
Then select "Register device", then you should receive an email with the instructions to associate an MFA device to your account:
Once you click on the link you will get the barcode to scan the QR code for MFA:
If you want an alternative way to setup your advice, you can copy the private key by clicking the key icon, once you have set up your MFA device, select "Done", then provide a one-time password to verify:
To test the whole flow, logout from authelia, by selecting "Logout":
Head over to https://two-factor.demo.containers.fan then provide your username and password:
Then provide the one-time pin from your MFA device:
If the challenge was successful, you should be redirected to the service:
Once you are authenticated to Authelia, you will be able to access the service without authenticating again.
Thank You
Thanks for reading, feel free to check out my website, feel free to subscribe to my newsletter or follow me at @ruanbekker on Twitter.
Linktree: https://go.ruan.dev/links
Patreon: https://go.ruan.dev/patreon
Ko-Fi: https://ko-fi.com/ruanbekker