Aller au contenu

🔒 Authentification centralisĂ©e avec Authelia et LLDAP

alt text

📔 Introduction

Les futures applications que nous installerons nécessiteront une authentification. Afin de faciliter la création ou la suppression de comptes utilisateurs, nous allons mettre une authentification centralisée.

Pour cela nous nous appuierons sur :

  • L'annuaire lĂ©ger LLDAP qui contiendra la base des comptes utilisateur
  • Le portail IAM Authelia permettant de fournir une solution d'authentification multi-facteur et unique par le biais du SSO.

Préparation

Création de l'arborescence
olivier@ds01:/mnt/nfsdatas$ mkdir stack-sso && chown olivier: stack-sso
olivier@ds01:/mnt/nfsdatas$ cd stack-sso
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p config/authelia
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p config/lldap/ssl
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir env
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p log/authelia
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p secrets/authelia
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p secrets/lldap
olivier@ds01:/mnt/nfsdatas/stack-sso$ mkdir -p data/{redis,data}

olivier@ds01:/mnt/nfsdatas/stack-sso$ tree -ld
.
├── config
│   ├── authelia
│   └── lldap
│       └── ssl
├── data
    ├── authelia
│   └── redis
├── env
├── log
│   └── authelia
└── secrets
    ├── authelia
    └── lldap

13 directories
Configuration de la stack
networks:
  web:
    external: true
  sso:
    external: true

services:
  lldap:
    image: nitnelave/lldap:stable
    networks:
      - web
      - sso
    volumes:
      - ./config/lldap:/data
      - ./secrets/lldap:/secrets
    env_file:
      - ./env/container-vars-lldap.env
    deploy:
      placement:
        constraints:
          - node.role == worker
      restart_policy:
        condition: on-failure
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=web"
        - "traefik.http.routers.lldap.rule=Host(`l.dev.quercylibre.fr`)"
        - "traefik.http.routers.lldap.tls=true"
        - "traefik.http.routers.lldap.tls.certresolver=letsencrypt"
        # Le dashboard de LLDAP ne sera accessible qu'en local
        - "traefik.http.routers.lldap.middlewares=crowdsec@file,lldap-ipallowlist"
        - "traefik.http.middlewares.lldap-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.0/24"
        - "traefik.http.services.lldap.loadbalancer.server.port=17170"
        - "traefik.http.routers.lldap.service=lldap"

  authelia:
    image: authelia/authelia
    depends_on:
      - lldap
      - redis
    networks:
      - web
      - sso
    env_file:
      - ./env/container-vars-authelia.env
    volumes:
      - "./config/authelia:/config"
      - "./secrets/authelia:/secrets"
      - "./log/authelia:/var/log/authelia"
      - "./data/authelia:/data"
    environment:
      - TZ=Europe/Paris
    deploy:
      placement:
        constraints:
          - node.role == worker
      restart_policy:
        condition: on-failure
        delay: 45s
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=web"
        - "traefik.http.routers.authelia.rule=Host(`a.dev.quercylibre.fr`)"
        - "traefik.http.routers.authelia.tls=true"
        - "traefik.http.routers.authelia.tls.certresolver=letsencrypt"
        - "traefik.http.routers.authelia.middlewares=crowdsec@file"
        - "traefik.http.services.authelia.loadbalancer.server.port=9091"
        - "traefik.http.routers.authelia.service=authelia"

  redis:
    image: redis:latest
    networks:
      - sso
    volumes:
      - ./data/redis:/data
    deploy:
      placement:
        constraints:
          - node.role == worker
      restart_policy:
        condition: on-failure
      replicas: 1

J'ai mis un dĂ©lai de 45 secondes pour le reboot du conteneur Authelia car lors du lancement de la stack, le CT Authelia n'attend pas que LLDAP et Redis soient prĂȘts. Par consĂ©quent lors du premier boot, Authelia est en Ă©chec et attend 45 secondes avant de se relancer.

Création du réseau overley sso
olivier@ds01:/mnt/nfsdatas/stack-sso$ docker network create --driver=overlay sso

Configuration de LLDAP

Configuration de la BDD config/lldap/lldap_config.toml
database_url = "sqlite:///data/users.db?mode=rwc"
# Commande pour générer la clé ci-dessous : tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1
key_seed = "O21vDuTDbXo69CimPb3G2FuyEYUuCc0E2A3UcXyUyuS2LxdtPCxKW0DiWfdkTKOs"
Génération du certificat et de la clé privée pour LDAPS
olivier@ds01:/mnt/nfsdatas/stack-sso$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout ./data/ssl/keyfile.key -out ./data/ssl/certfile.crt
Génération du jeton JWT_SECRET et du mot de passe admin LDAP
olivier@ds01:/mnt/nfsdatas/stack-sso$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > ./secrets/lldap/JWT_SECRET
olivier@ds01:/mnt/nfsdatas/stack-sso$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "20" | head -n 1 > ./secrets/lldap/LDAP_USER_PASS
Variables d'environnement env/container-vars-lldap.env
LLDAP_LDAP_BASE_DN=dc=quercylibre,dc=fr
TZ=Europe/Paris
UID=1001
GID=1001

# If using LDAPS, set enabled true and configure cert and key path
LLDAP_LDAPS_OPTIONS__ENABLED=true
LLDAP_LDAPS_OPTIONS__CERT_FILE=/data/ssl/certfile.crt
LLDAP_LDAPS_OPTIONS__KEY_FILE=/data/ssl/keyfile.key

# Secrets: lldap reads them from the specified files.
# This way, the secrets are not part of any process environment.
LLDAP_JWT_SECRET_FILE=/secrets/JWT_SECRET
LLDAP_LDAP_USER_PASS_FILE=/secrets/LDAP_USER_PASS

Configuration d'Authelia

Configuration d'Authelia config/authelia/configuration.yml
server:
  address: 'tcp://0.0.0.0:9091'

log:
  level: info
  format: text
  file_path: /var/log/authelia/authelia.log

theme: light

totp:
  algorithm: sha1
  digits: 6
  period: 30
  skew: 1
  secret_size: 32

password_policy: # Les mots de passe pourront ĂȘtre modifiĂ©s depuis Authelia mais selon les rĂšgles ci-dessous
  standard:
    enabled: false
    min_length: 14
    max_length: 0
    require_uppercase: true
    require_lowercase: true
    require_number: true
    require_special: true
  zxcvbn:
    enabled: false
    min_score: 3

authentication_backend:  
  password_reset:
    disable: false # Autorisation de modification des mots de passe par les utilisateurs
  refresh_interval: 5m
  # Configuration LDAP spécifique à LLDAP
  ldap:
    implementation: custom
    timeout: 5s
    start_tls: false
    additional_users_dn: 'ou=people'
    users_filter: "(&({username_attribute}={input})(objectClass=person))"
    additional_groups_dn: 'ou=groups'
    groups_filter: "(member={dn})"
    attributes:
      username: 'uid'
      display_name: 'displayName'
      mail: 'mail'
      group_name: 'cn'

access_control:
  default_policy: deny
  networks:
  - name: internal
    networks:
      - '192.168.1.0/24'
  rules:
    - domain: 
      - "cobalt.dev.quercylibre.fr" # Application d'extraction de vidéo et de son Youtubre, Instangram,...
      policy: 'one_factor' # Un seul facteur d'auth si connexion depuis le réseau interne 192.168.1.0/24 déclaré plus haut
      networks:
      - 'internal'
    - domain: 
      - "cobalt.dev.quercylibre.fr"
      policy: 'two_factor' # Deux facteurs d'auth si connexion depuis Internet ou un autre réseau.
    - domain:
      - "d.dev.quercylibre.fr" # Dashboard Traefik
      policy: 'two_factor'
      subject:
      - ['user:admin'] # Seul le compte admin aura accĂšs au dashboard Traefik en remplacement du basic auth

session:
  name: authelia_session
  expiration: 12h  # 12 hours
  inactivity: 45m  # 45 minutes
  remember_me: 1M  # 1 month
  cookies:
    - domain: 'dev.quercylibre.fr'
      authelia_url: 'https://a.dev.quercylibre.fr'
      default_redirection_url: https://accueil.dev.quercylibre.fr
      name: 'authelia_session'
      same_site: 'lax'
      inactivity: '5m'
      expiration: '1h'
      remember_me: '1d'
  redis:
    host: redis 
    port: 6379 # port for REDIS docker contianer
    database_index: 0 # change this if you already use REDIS for something

regulation: # Blocage accĂšs de 15 min au bout de 3 Ă©checs d'auth en moins de 5 min.
  max_retries: 3
  find_time: 5m
  ban_time: 15m

notifier: 
  smtp:
    sender: "Authelia <authelia@mail.tld>" # Variable passée ici car bug si déclarée dans les variables d'env.

storage:
  # Il est possible d'utiliser d'autres back-end comme MariaDB ou PGSQL.
  local:
    path: /data/db.sqlite3

ntp:
  address: 'udp://time.cloudflare.com:123'
  version: 3
  max_desync: '3s'
  disable_startup_check: false
  disable_failure: false
Variables d'environnement env/container-vars-authelia.env
PUID=1001
PGID=1001
AUTHELIA_TOTP_ISSUER=a.dev.quercylibre.fr
AUTHELIA_WEBAUTHN_DISPLAY_NAME=authelia
AUTHELIA_NOTIFIER_SMTP_ADDRESS=submission://<SERVER_MAIL>:587
AUTHELIA_NOTIFIER_SMTP_USERNAME=authelia@mail.tld
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDRESS=ldap://lldap:3890
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN=dc=quercylibre,dc=fr
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER=uid=admin,ou=people,dc=quercylibre,dc=fr

# Secrets: Authelia reads them from the specified files.
# This way, the secrets are not part of any process environment.
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE=/secrets/JWT_SECRET
AUTHELIA_SESSION_SECRET_FILE=/secrets/SESSION_SECRET
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE=/secrets/STORAGE_ENCRYPTION_KEY
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE=/secrets/NOTIFIER_SMTP_PASSWORD
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE=/secrets/AUTHENTICATION_BACKEND_LDAP_PASSWORD
Génération des secrets pour Authelia
olivier@ds01:/mnt/nfsdatas/stack-sso$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > ./secrets/authelia/JWT_SECRET
olivier@ds01:/mnt/nfsdatas/stack-sso$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > ./secrets/authelia/SESSION_SECRET
olivier@ds01:/mnt/nfsdatas/stack-sso$ tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1 > ./secrets/authelia/STORAGE_ENCRYPTION_KEY

# Renseignez les mots de passe pour l'utilisateur SMT
olivier@ds01:/mnt/nfsdatas/stack-sso$ vim secrets/authelia/NOTIFIER_SMTP_PASSWORD

# Récupérez le mot de passe admin LLDAP
olivier@ds01:/mnt/nfsdatas/stack-sso$ cp secrets/LLDAP/LDAP_USER_PASS secrets/authelia/AUTHENTICATION_BACKEND_LDAP_PASSWORD
SĂ©curisation de l'accĂšs aux fichiers contenant les secrets
olivier@ds01:/mnt/nfsdatas/stack-sso$ chmod -R u+rwX,g-rwX,o-rwX secrets/

DĂ©ploiement de la stack

DĂ©ploiement de la stack
olivier@ds01:/mnt/nfsdatas/stack-sso$ docker stack deploy -c docker-stack.yml -d sso
Creating service sso_lldap
Creating service sso_authelia
Creating service sso_redis

olivier@ds01:/mnt/nfsdatas/stack-sso$ docker stack ps sso
ID             NAME                 IMAGE                      NODE      DESIRED STATE   CURRENT STATE            ERROR                       PORTS
hly93qjl1mcj   sso_authelia.1       authelia/authelia:latest   ds06      Running         Running 33 minutes ago
# Le premier fail ci-dessous est liĂ© au fait qu'Authelia dĂ©marre sans attendre que LLDAP et Redis soient prĂȘts                              
a2g5wssbh6w2    \_ sso_authelia.1   authelia/authelia:latest   ds06      Shutdown        Failed 34 minutes ago    "task: non-zero exit (1)"   
qkuih02x16ws   sso_lldap.1          nitnelave/lldap:stable     ds04      Running         Running 34 minutes ago                               
v23rog8bh9ah   sso_redis.1          redis:latest               ds04      Running         Running 34 minutes ago 

Configuration du middleware Authelia savec Traefik

Ajout du middleware Authelia dans /mnt/nfsdatas/traefik/traefik/dyn_traefik/traefik.config.yml
http:
  middlewares:
  (...)
    authelia:
      forwardAuth:
        address: 'http://authelia:9091/api/authz/forward-auth'
        trustForwardHeader: true
        authResponseHeaders:
          - 'Remote-User'
          - 'Remote-Groups'
          - 'Remote-Email'
          - 'Remote-Name'

DĂ©claration du middleware pour le dashboard Traefik

L'objectif est de remplacer le basic auth de Traefik par Authelia et en restreigant l'accÚs au compte admin créé sur LLDAP grùce à la rÚgle d'accÚs définie plus haut dans le fichier de configuration d'Authelia.

DĂ©claration du middleware Authelia dans /mnt/nfsdatas/traefik/docker-stack.yml
services:
  traefik:
    image: "traefik:v3.0.2"
    (...)
    deploy:
      placement:
        constraints:
          - node.role == manager
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.traefik-dashboard.rule=Host(`d.dev.quercylibre.fr`)"
        - "traefik.http.routers.traefik-dashboard.service=api@internal"
        - "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
        - "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
        # DĂ©claration du middle-ware authelia@file
        - "traefik.http.routers.traefik-dashboard.middlewares=crowdsec@file,traefik-dashboard-ipallowlist,authelia@file"
        - "traefik.http.middlewares.traefik-dashboard-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.0/24"
        - "traefik.http.services.traefik-dashboard-service.loadbalancer.server.port=8080"
        (...)

On relance le service :

Relance de Traefik
olivier@ds01:/mnt/nfsdatas/traefik$ docker service update traefik_traefik

Il est quand mĂȘme sacrĂ©ment simple d'utiliser le middleware Authelia avec Traefik comparĂ© Ă  Nginx.

Configuration des méthodes 2FA

Outil pour le 2FA

Avant de configurer le 2FA, il vous faudra avoir un outil capable de gérer le 2FA. Personnellement, j'utilise Vaultwarden dont l'extension Firefox Bitwarden est capable de gérer le TOTP et les clés d'identification. Si vous avez déjà un conffre-fort pour vos mots de passe mais qui ne gÚre pas le TOTP, vous pouvez utiliser l'extension Firefox Authenticator.

Connectez vous Ă  Authelia depuis un navigateur web puis authentifiez-vous avec un compte existant sur LLDAP.

authelia01

Une fois authentifié, vous devez définir une méthode 2FA. Cliquez sur "Enregistrer l'appareil" :

authelia02

Ajoutez une des deux méthodes. Ici je sélectionne la méthode basée sur un mot de passe à usage unique.

authelia03

Un email vous est alors envoyé avec code temporaire.

authelia04

Quand le code reçu par mail est saisi, Authelia vous invite alors à suivre la procédure ci-dessous afin d'activer le 2FA basé sur le TOTP.

authelia05

Une fois la procédure terminée, la méthode est activée et visible dans les réglages Authelia accessible par l'utilisateur. Il est possible de le supprimer. Il vous sera alors envoyé de nouveau un code temporaire par email afin de confirmer la suppression.

authelia06

Monitoring des services avec Uptime Kuma

Nous allons monitorer :

  • Le service Authelia Ă  travers son URL et son port 9091
  • Le service LLDAP Ă  travers son URL et ses ports 17170 et 3890
  • Le service Redis Ă  travers son port 6379

On déclare le réseau "sso" afin de permettre à Uptime Kuma de monitorer les services comme Redis.

Configuration du fichier /mnt/nfsdatas/uptime-kuma/docker-stack.yml d'Uptime-Kuma
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    volumes:
      - ./uptime-kuma-data:/app/data
    networks:
      - web
      - sso # Ici
    (...)
networks:
  web:
    external: true
  sso: # Et ici
    external: true

On relance le service.

olivier@ds01:/mnt/nfsdatas/uptime-kuma$ docker stack rm uptime-kuma 
Removing service uptime-kuma_uptime-kuma
olivier@ds01:/mnt/nfsdatas/uptime-kuma$ docker stack deploy -c docker-stack.yml -d uptime-kuma
Creating service uptime-kuma_uptime-kuma