Cela fait un moment que j’utilise Authelia pour faire de l’authentification à mes applications qui n’en proposent pas, cela reste beaucoup plus joli qu’un simple auth-basic.
Cela fait également un moment que cet article est commencé, mais au vu des sujets déjà présents sur le net, je ne voyais pas l’utilité d’en ajouter un nouveau, surtout qu’à ce moment, j’estimais n’avoir aucune plus-value. Au final, ayant beaucoup pratiqué authelia, j’ai appris quelques trucs écrient nulle-part ailleurs, donc je me lance enfin.

Qu’est ce que Authelia

Authelia est un logiciel permettant d’ajouter une authentification au niveau du reverse proxy, plutôt pratique pour sécuriser des applications qui n’en ont pas de base. Il permet de faire de l’authentification simple et double facteur, avec notamment TOTP, Duo ou Yubikey.
Si vous avez en plus un ldap, il est possible de l’utiliser pour populer vos utilisateurs.

Comme dit, authelia vient s’installer entre le reverse et vos applications, il fait donc partie de la classe middleware, cela veut dire qu’il n’y a aucune modification à apporter à vos applications. Voici un léger schéma de l’architecture : authelia

Installation

Nous utiliserons Traefik comme reverse proxy, mais pour changer un peu, nous ferons une installation manuelle de notre stack, pas de docker ou kubernetes. Pour cela, nous installons une VM sous ubuntu en local, en redirigeant le nom de domaine *.traefik.local sur celle-ci.
Pour les applications, nous utiliserons caddy server. En fait, c’est surtout que j’utilise la même machine que mes tests caddy, comme ça, c’est plus simple.

traefik

Installation

Pour installer traefik, nous récupérons le binaire :

$ wget https://github.com/traefik/traefik/releases/download/v2.5.6/traefik_v2.5.6_linux_amd64.tar.gz
$ tar xzvf traefik_v2.5.6_linux_amd64.tar.gz
$ cp traefik /usr/local/bin/traefik

Pour utiliser traefik, nous lui créons un utilisateur, un reverse n’a aucunement besoin d’être exécuté en root :

$ useradd --system traefik

Et voilà, c’est fait, pas de dépendances puisque c’est du Go.

Configuration

Nous allons partir sur une configuration simple, pour ceci nous créons un répertoire /etc/traefik/conf.d :

$ mkdir -p /etc/traefik/conf.d
$ chown root:traefik -R /etc/traefik
$ chmod g+w -R /etc/traefik

Nous créons également le répertoire qui accueillera les logs :

$ mkdir -p /var/log/traefik && chown traefik:traefik /var/log/traefik

Puis nous créons un fichier /etc/traefik/traefik.yml, nous partons sur une configuration simple

accesslog:
  filePath: "/var/log/traefik/traefik-access.log"

api:
  insecure: false
  dashboard: true
  debug: false

log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"

providers:
  file:
    directory: /etc/traefik/conf.d/
    watch: true

entryPoints:
  web:
    address: ":8080"
    http:
      redirections:
        entryPoint:
          scheme: https
          to: websecure
  websecure:
    address: ":8443"

serverstransport:
  insecureskipverify: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: "xataz@traefik.local"
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      storage: "/etc/traefik/acme.json"
      keyType: "EC384"
      tlsChallenge: {}

Création du service

Pour finir, il ne manque qu’un service systemd pour le démarrer correctement, pour ceci, nous créons un fichier /etc/systemd/system/traefik.service :

[Unit]
Description = Traefik Daemon
After = syslog.target network.target

[Service]
User = traefik
Group = traefik
Type = simple
ExecStart = /usr/local/bin/traefik
TimeoutStopSec = 20
KillMode = process
Restart = on-failure
WorkingDirectory = /etc/traefik

[Install]
WantedBy = multi-user.target

On reload la configuration de systemd, et on lance le service :

$ systemctl daemon-reload
$ systemctl enable --now traefik
Created symlink /etc/systemd/system/multi-user.target.wants/traefik.service → /etc/systemd/system/traefik.service.

Création d’un router traefik

Pour commencer, nous allons ajouter un router pour accéder au dashboard de traefik, pour ceci, nous créons le fichier /etc/traefik/conf.d/traefik.yml :

http:
  routers:
    traefik:
      rule: "Host(`traefik.traefik.local`)"
      entryPoints:
        - "websecure"
      service: "api@internal"
      tls:
        certResolver: letsencrypt

puis on peux directement tester via notre navigateur avec https://traefik.traefik.local:8443 (Si vous êtes en local, vous aurez une erreur de certificat)

authelia

Installation

L’installation d’authelia est vraiment simple. Tout comme traefik c’est du Go, donc un seul binaire, cependant authelia à besoin de redis pour la gestion des sessions, et d’un backend SQL. Pour notre installation nous utiliserons sqlite, qui est largement suffisant pour une installation personnelle.

On commence donc par installer redis :

$ apt install redis

Puis on installe authelia :

$ wget https://github.com/authelia/authelia/releases/download/v4.33.1/authelia-v4.33.1-linux-amd64.tar.gz
$ tar xzvf authelia-v4.33.1-linux-amd64.tar.gz 
$ cp authelia-linux-amd64 /usr/local/bin/authelia

On crée l’utilisateur ainsi que ses répertoires :

$ useradd --system authelia
$ mkdir -p /etc/authelia /var/lib/authelia /var/log/authelia
$ chown root:authelia /etc/authelia
$ chown authelia: /var/lib/authelia /var/log/authelia

Et voilà pour l’installation.

Configuration

Nous allons faire une configuration minimal, je vous laisse regarder la documentation officiel pour les options, mais c’est plutôt clair, pour ceci, nous créons le fichier /etc/authelia/configuration.yml :

server:
  host: 0.0.0.0
  port: 9091

theme: dark

server:
  read_buffer_size: 4096
  write_buffer_size: 4096
  path: ""


jwt_secret: XXXXXXXXXXXXXXXXXXXXXXx

default_redirection_url: https://traefik.local:8443

totp:
  issuer: traefik.local
  period: 30
  skew: 1

authentication_backend:
  disable_reset_password: false

  refresh_interval: 5m

  file:
    path: /etc/authelia/users.yml
    password:
      algorithm: argon2id
      iterations: 1
      key_length: 32
      salt_length: 16
      memory: 64
      parallelism: 8

access_control:
  default_policy: deny

  rules:
    - domain: traefik.traefik.local
      subject: "group:admins"
      policy: one_factor
session:
  name: authelia_session
  domain: traefik.local
  same_site: lax
  secret: XXXXXXXXXXXXXXXXXXXXXXXXX
  expiration: 1h
  inactivity: 5m
  remember_me_duration: 1M

  redis:
    host: localhost
    port: 6379
    #password: authelia
    database_index: 0
    maximum_active_connections: 8
    minimum_idle_connections: 0

regulation:
  max_retries: 3
  find_time: 2m
  ban_time: 5m

storage:
  encryption_key: XXXXXXXXXXXXXXXXXXXX
  local:
    path: /var/lib/authelia/db.sqlite3

notifier:
  disable_startup_check: false

  filesystem:
    filename: /var/lib/authelia/notification.txt

Nous allons hashé un mot de passe pour nos utilisateurs, pour ce test, nous utiliserons le super mot de passe password pour tous nos utilisateurs :

$ authelia hash-password password
Password hash: $argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c

Puis nous allons créer notre fichier contenant les utilisateurs, comme indiqué dans la configuration, nous le nommerons /etc/authelia/users.yml, nous y ajouterons un admin et 5 utilisateurs :

users:
  admin:
    displayname: "admin"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: admin@traefik.local
    groups:
      - admins
  user1:
    displayname: "user1"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: user1@traefik.local
    groups:
      - users
  user2:
    displayname: "user2"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: user2@traefik.local
    groups:
      - users
  user3:
    displayname: "user3"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: user3@traefik.local
    groups:
      - users
  user4:
    displayname: "user4"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: user4@traefik.local
    groups:
      - users
  user5:
    displayname: "user5"
    password: "$argon2id$v=19$m=65536,t=1,p=8$TXJLbFRjYjBhbFhFUFdRVA$Dn4U0j0mu2vgb+PCtFFpOm2SAmeoS0mwuLM+vZGka+c"
    email: user5@traefik.local
    groups:
      - users

Création du service

Nous allons également ajouter un service systemd, nous créons donc un fichier vim /etc/systemd/system/authelia.service :

[Unit]
Description = Authelia Daemon
After = syslog.target network.target

[Service]
User = authelia
Group = authelia
Type = simple
ExecStart = /usr/local/bin/authelia --config /etc/authelia/configuration.yml
TimeoutStopSec = 20
KillMode = process
Restart = on-failure
WorkingDirectory = /var/lib/authelia

[Install]
WantedBy = multi-user.target

Puis on lance le service :

$ systemctl daemon-reload
$ systemctl enable --now authelia
Created symlink /etc/systemd/system/multi-user.target.wants/authelia.service → /etc/systemd/system/authelia.service.

Configuration de traefik

Maintenant que nous avons authelia qui tourne et qui est fonctionnel, il nous reste à faire la configuration de traefik, nous allons créer un fichier /etc/traefik/conf.d/authelia.yml :

http:
  middlewares:
    sso:
      forwardAuth:
        address: "http://localhost:9091/api/verify?rd=https://sso.traefik.local:8443/"
        trustForwardHeader: true
        authResponseHeaders: 
          - Remote-User
          - Remote-Groups
          - Remote-Name
          - Remote-Email

  services:
    sso:
      loadBalancer:
        servers:
          - url: "http://localhost:9091"

  routers:
    sso:
      rule: "Host(`sso.traefik.local`)"
      entryPoints:
        - "websecure"
      service: "sso@file"
      tls:
        certResolver: letsencrypt

Normalement, à partir de maintenant, vous avez accès une jolie page d’authetification sur http://sso.traefik.local:8080.

Caddy

Installation

Pour installer caddy, c’est très simple, nous utiliserons le repo de caddy :

$ apt install -y debian-keyring debian-archive-keyring apt-transport-https
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | tee /etc/apt/trusted.gpg.d/caddy-stable.asc
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
$ apt update && apt install caddy

Configuration

Nous allons créer des répertoires dans /opt, pour 5 applications :

$ for i in 1 2 3 4 5; do mkdir -p /opt/caddy/app${i} && echo "Ceci est l'application n°${i}" > /opt/caddy/app${i}/index.html; done
$ chown -R caddy:caddy /opt/caddy

Puis nous allons editer le fichier /etc/caddy/Caddyfile :

:9001 {
        root * /opt/caddy/app1
        file_server
}
:9002 {
        root * /opt/caddy/app2
        file_server
}
:9003 {
        root * /opt/caddy/app3
        file_server
}
:9004 {
        root * /opt/caddy/app4
        file_server
}
:9005 {
        root * /opt/caddy/app5
        file_server
}

Puis on relance caddy :

systemctl restart caddy

À partir de maintenant, vous avez 5 applications qui vous affichent le numéro de celle-ci.

Configuration de traefik

Cependant, le but étant de pouvoir y accéder avec une authentification, nous créons donc /etc/traefik/conf.d/apps.yml :

http:
  services:
    app1:
      loadBalancer:
        servers:
          - url: "http://localhost:9001"
    app2:
      loadBalancer:
        servers:
          - url: "http://localhost:9002"
    app3:
      loadBalancer:
        servers:
          - url: "http://localhost:9003"
    app4:
      loadBalancer:
        servers:
          - url: "http://localhost:9004"
    app5:
      loadBalancer:
        servers:
          - url: "http://localhost:9005"

  routers:
    app1:
      rule: "Host(`app1.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app1@file"
      tls:
        certResolver: letsencrypt

    app2:
      rule: "Host(`app2.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app2@file"
      tls:
        certResolver: letsencrypt

    app3:
      rule: "Host(`app3.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app3@file"
      tls:
        certResolver: letsencrypt

    app4:
      rule: "Host(`app4.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app4@file"
      tls:
        certResolver: letsencrypt

    app5:
      rule: "Host(`app5.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app5@file"
      tls:
        certResolver: letsencrypt

Coté traefik, le simple fait d’ajouter le middlewares au router suffit pour lui dire qu’il va devoir passer par authelia.

Amusons nous

Dans cette partie, nous verrons comment configurer nos applications par l’exemple.

Sécurisation de traefik

Traefik étant un dashboard utile pour les administrateurs, seuls le groupe admins doit y avoir accès. Il suffit d’ajouter le middleware à la configuration du router, c’est à dire dans le fichier /etc/traefik/conf.d/traefik.yml :

http:
  routers:
    traefik:
      rule: "Host(`traefik.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "api@internal"
      tls:
        certResolver: letsencrypt

Coté authelia on ne fait rien, car on l’avait déjà configuré.

Maintenant, si vous accédez à https://traefik.traefik.local:9443, vous serez redirigé vers https://sso.traefik.local:9443 pour vous authentifier, seul le couple admin/password fonctionnera, les autres utilisateurs n’aurons pas accès.

Sécurisons app1

Nous avons déjà correctement configuré traefik pour toutes les applications, donc pas besoin d’y retourner.

Nous allons ici configurer app1 pour qu’il soit accessible par tous les utilisateurs, nous modifions le fichier /etc/authelia/configuration.yml et plus particulièrement la partie access_control.rules :

  rules:
    - domain: app1.traefik.local
      subject: 
        - "group:admins"
        - "group:users"
      policy: one_factor

Et il suffit de redémarrer authelia :

$ systemctl restart authelia

Sécurisons app2 et app3

Ici, nous allons en une seul règle, sécuriser les app2 et 3 pour seulement user2 et user3 :

  rules:
    - domain: ["app2.traefik.local", "app3.traefik.local"]
      subject: 
        - "user:user2"
        - "user:user3"
      policy: one_factor

Et nous redémarrons.

Sécurisons app4

Pour app4, nous allons l’autoriser que pour user4, mais nous allons activer l’authentification à double facteur :

  rules:
    - domain: app4.traefik.local
      subject: 
        - "user:user4"
      policy: two_factor

Et comme à chaque modification, on relance authelia.

Dans ce cas, et dans le cas d’une première authentification, il vous sera envoyé un mail ou une notification dans le fichier de notification, avec un lien dans ce genre-là : https://sso.traefik.local:8443/one-time-password/register?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRoZWxpYSIsImV4cCI6MTY0MjIwNzE0MywiaWF0IjoxNjQyMjA2ODQzLCJqdGkiOiJkNmQ3MWUxNS03NTlhLTExZWMtYjQxNC01MjU0MDBlMzg3ZTkiLCJhY3Rpb24iOiJSZWdpc3RlclRPVFBEZXZpY2UiLCJ1c2VybmFtZSI6InVzZXI0In0.8-N5QVWhXezrETrqYbL-dTsLdmlVh5ibfvghBIeQhJQ
Celui-ci vous permettra d’avoir le Qrcode afin de l’enregistrer dans votre application de TOTP.

Laissons app5 ouvert

Nous pouvons également laisser ouvert l’accès, sans authentification, ce qui permet par exemple d’ajouter le middleware sso par défaut à toutes les applications.

  rules:
    - domain: app5.traefik.local
      policy: bypass

Rediriger la même url vers 2 services différents en fonction de l’utilisateur

Pour ce dernier exemple, nous allons depuis l’url https://app.traefik.local:9443 vers app1 ou app2 en fonction de l’utilisateur.

Nous ajoutons donc une règle à authelia :

  rules:
    - domain: app.traefik.local
      subject: 
        - "user:user1"
        - "user:user2"
      policy: one_factor

Puis nous allons avoir une petite configuration niveau traefik, dans le fichier /etc/traefik/conf.d/apps.yml. Nous commençons par rajouter un service, qui pointera sur un router :

  services:
    app:
      loadBalancer:
        servers:
          - url: "https://app.traefik.local:8443"

Il faut évidemment que le domaine app.traefik.local soit connu du serveur.

Puis nous créons 2 routers :

  routers:
    app:
      rule: "Host(`app.traefik.local`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app@file"
      tls:
        certResolver: letsencrypt

    app-user1:
      rule: "Host(`app.traefik.local`) && Headers(`Remote-User`,`user1`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app1@file"
      tls:
        certResolver: letsencrypt

    app-user2:
      rule: "Host(`app.traefik.local`) && Headers(`Remote-User`,`user2`)"
      entryPoints:
        - "websecure"
      middlewares:
        - "sso@file"
      service: "app2@file"
      tls:
        certResolver: letsencrypt

Normalement à partir de là, user1 devrait accéder à app1 et user2 à app2.

Cependant là, je pense qu’il faut quelques explications Pour faire ceci, il n’y a pas de condition if sous traefik, donc nous devons passer par les headers. Authelia fourni plusieurs headers (Remote-User, Remote-Group etc …). Authelia étant un middleware, c’est l’application derrière le reverse qui reçoit ce header, mais notre application ne sais pas gérer directement ceci, et traefik n’a à ce moment aucun connaissance de l’existance de ce header. L’astuce revient donc à repasser par traefik après authelia, en retapant directement sur lui-même, afin de lui transmettre l’header. Traefik refait un check de l’authentification, et valide donc celle-ci.

Conclusion

Je pense que je vous ai appris tout ce que je pouvais, et que maintenant vous n’avez plus aucune raison de continuer à utiliser auth_basic. Bien évidemment, il est possible d’utiliser authelia avec nginx ou HAProxy.
Si le sujet vous interesse, je ne peux que vous conseillez d’aller voir la documentation officiel.

Ressource