Skip to content

Se connecter avec GitHub et OIDC

Dozzle peut laisser les utilisateurs se connecter avec un compte externe au lieu de saisir un mot de passe. Cela fait partie du fournisseur simple et ce n'est pas un fournisseur à part entière : users.yml est toujours lu à chaque requête et c'est toujours lui qui décide qui entre.

Cela a une conséquence qui mérite d'être dite d'emblée : users.yml est la liste d'autorisation. Un compte externe auquel aucune entrée n'est liée ne peut pas se connecter, et aucun compte n'est jamais créé automatiquement.

La connexion par mot de passe continue de fonctionner à côté, ce qui compte quand une OAuth App casse et qu'il faut pouvoir entrer pour la réparer.

Se connecter avec GitHub

Dozzle peut laisser les utilisateurs se connecter avec leur compte GitHub au lieu de saisir un mot de passe. Cela fait partie du fournisseur simple et ce n'est pas un fournisseur d'authentification distinct : users.yml est toujours lu à chaque requête et c'est toujours lui qui décide qui entre. Continuez d'utiliser --auth-provider simple. github est accepté comme alias si vous préférez expliciter ce que l'instance utilise.

Créez d'abord une OAuth App dans les Developer settings de GitHub et réglez l'Authorization callback URL sur :

https://your-dozzle-host/api/auth/callback

Si Dozzle est servi sous un chemin de base, incluez-le, par exemple https://example.com/dozzle/api/auth/callback. Dozzle n'envoie pas de redirect_uri au démarrage du flux, GitHub redirige donc toujours vers l'URL de callback enregistrée sur l'OAuth App. Une différence à cet endroit est la cause la plus fréquente d'échec de connexion.

Copiez ensuite le client ID, générez un client secret, et passez les deux à Dozzle :

sh
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider simple --auth-github-client-id Ov23liABCDEFGHIJKLMN --auth-github-client-secret 0123456789abcdef0123456789abcdef01234567
yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /path/to/dozzle/data:/data
    ports:
      - 8080:8080
    environment:
      DOZZLE_AUTH_PROVIDER: simple
      DOZZLE_AUTH_GITHUB_CLIENT_ID: Ov23liABCDEFGHIJKLMN
      DOZZLE_AUTH_GITHUB_CLIENT_SECRET: 0123456789abcdef0123456789abcdef01234567

Liez un utilisateur à son compte GitHub avec une clé github dans users.yml :

yaml
users:
  admin:
    email: me@email.net
    name: Admin
    password: $2a$11$9ho4vY2LdJ/WBopFcsAS0uORC0x2vuFHQgT/yBqZyzclhHsoaIkzK
    github: octocat

  guest:
    email: guest@email.net
    name: Guest
    github: hubot
    filter: "label=com.example.app"
    roles: none

password devient facultatif dès que github est défini, comme le montre guest ci-dessus. admin a les deux, il peut donc se connecter des deux façons. La connexion par mot de passe reste disponible en solution de repli pour tous ceux qui en ont encore un, et la page de connexion propose les deux options.

WARNING

Gardez un mot de passe sur au moins un compte. Quand aucun utilisateur de users.yml n'a de password, le formulaire de connexion disparaît complètement et le fournisseur externe devient le seul moyen d'entrer : une URL de callback erronée, une OAuth App révoquée ou un client secret expiré verrouille alors tout le monde hors de l'interface web. Pour s'en sortir, il faut modifier users.yml sur l'hôte pour y remettre un mot de passe, ce qui demande un accès shell là où se trouve le /data de Dozzle.

La valeur est le login GitHub (l'identifiant dans github.com/octocat), pas l'adresse email. Un login est toujours présent et visible, alors que l'email d'un compte peut être privé ou changer à tout moment.

WARNING

Un login GitHub n'est pas permanent. Si quelqu'un renomme son compte GitHub, l'ancien identifiant est libéré et n'importe qui peut l'enregistrer : celui qui le fait hérite alors de cette entrée de votre users.yml dès sa prochaine connexion. Traitez un renommage comme un changement d'accès : mettez users.yml à jour en même temps, et supprimez les entrées des personnes parties au lieu de laisser un identifiant obsolète dans la liste.

users.yml est la liste d'autorisation. Un compte GitHub qui n'y figure pas ne peut pas se connecter, quelle que soit l'organisation à laquelle il appartient. Il n'y a pas de création automatique de compte : ajouter quelqu'un veut dire l'ajouter au fichier. Les filtres et les rôles sont résolus depuis users.yml à chaque requête, exactement comme pour les utilisateurs avec mot de passe, donc un utilisateur GitHub avec roles: none est restreint de la même manière.

NOTE

Dozzle ne permet volontairement pas d'autoriser toute une organisation GitHub ou tout un domaine email. Chaque utilisateur est listé individuellement. Si vous avez besoin d'un accès par groupe ou par domaine, utilisez forward-proxy avec Authelia ou Authentik, qui sont faits pour ça.

WARNING

Modifier users.yml fait tourner la clé de signature JWT et déconnecte tous les utilisateurs. C'est déjà le cas aujourd'hui quand vous ajoutez ou supprimez un utilisateur, et cela vaut aussi quand vous ajoutez une clé github.

Se connecter avec OIDC

N'importe quel fournisseur qui publie un document de découverte OpenID Connect fonctionne avec le même callback : Google, Keycloak, Pocket ID, Zitadel, Authentik et d'autres. Pointez Dozzle sur l'URL de l'émetteur et donnez-lui un client id et un client secret.

Enregistrez Dozzle comme client confidentiel auprès de votre fournisseur et réglez l'URI de redirection sur :

https://your-dozzle-host/api/auth/callback

Incluez le chemin de base si Dozzle tourne sous un chemin de base, par exemple https://example.com/dozzle/api/auth/callback. Contrairement à GitHub, OIDC oblige Dozzle à envoyer redirect_uri, cette valeur doit donc correspondre exactement à ce que vous avez enregistré.

sh
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider simple --auth-oidc-issuer https://id.example.com --auth-oidc-client-id dozzle --auth-oidc-client-secret secret --auth-oidc-name "Pocket ID"
yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /path/to/dozzle/data:/data
    ports:
      - 8080:8080
    environment:
      DOZZLE_AUTH_PROVIDER: simple
      DOZZLE_AUTH_OIDC_ISSUER: https://id.example.com
      DOZZLE_AUTH_OIDC_CLIENT_ID: dozzle
      DOZZLE_AUTH_OIDC_CLIENT_SECRET: secret
      DOZZLE_AUTH_OIDC_NAME: Pocket ID

DOZZLE_AUTH_OIDC_NAME n'est que le libellé du bouton de connexion. Sa valeur par défaut est SSO.

L'URL de l'émetteur est celle qui sert /.well-known/openid-configuration. Dozzle récupère ce document pour trouver les endpoints d'autorisation, de token et de userinfo, et refuse de démarrer le flux si le document déclare un émetteur différent de celui que vous avez configuré.

Lier les utilisateurs

OIDC fait la correspondance sur l'email vérifié, pas sur le login. Définissez email sur l'utilisateur dans users.yml :

yaml
users:
  admin:
    email: me@email.net
    name: Admin
    # le mot de passe est facultatif une fois le compte lié

L'email doit être marqué comme vérifié par votre fournisseur. Dozzle refuse la connexion quand email_verified est faux, parce que faire correspondre une adresse non vérifiée permettrait à quiconque peut s'inscrire chez un fournisseur permissif de s'approprier un compte en saisissant l'email de quelqu'un d'autre.

NOTE

GitHub fait la correspondance sur le login et OIDC sur l'email, et cette différence est voulue. Un login GitHub est stable et toujours présent, alors qu'un email GitHub peut être privé ou changer. OIDC n'a pas d'équivalent stable et lisible par un humain, l'email vérifié est donc l'attribut que les administrateurs connaissent réellement.

Google

Google est un fournisseur OIDC comme un autre. Créez un client OAuth dans la console Google Cloud et utilisez :

DOZZLE_AUTH_OIDC_ISSUER: https://accounts.google.com
DOZZLE_AUTH_OIDC_NAME: Google

--auth-provider google est accepté comme alias de simple, les deux écritures fonctionnent donc.

Derrière un reverse proxy

Dozzle détermine si la requête d'origine utilisait HTTPS à partir de l'en-tête X-Forwarded-Proto, et prend le nom d'hôte dans X-Forwarded-Host quand il est présent. La plupart des reverse proxies envoient les deux par défaut, mais si le vôtre ne le fait pas, la connexion casse de deux façons.

Pour OIDC, elle casse purement et simplement. Le redirect_uri qu'envoie Dozzle est construit à partir de ces en-têtes : un proxy qui ne pose pas X-Forwarded-Proto: https fait envoyer à Dozzle http://your-host/api/auth/callback. Cela ne correspond pas à l'URI en https:// enregistrée chez votre fournisseur, et le fournisseur rejette la requête au lieu de rediriger vers quoi que ce soit d'utile.

Pour GitHub l'URL n'est pas affectée, puisque Dozzle omet redirect_uri et que GitHub retombe sur l'URL de callback enregistrée sur l'OAuth App. L'en-tête décide quand même si le cookie de session est marqué Secure, il vaut donc la peine d'être correct dans les deux cas.

Vous pouvez vérifier ce qu'envoie votre proxy en regardant le cookie que Dozzle pose au début d'une connexion :

sh
$ curl -sI 'https://your-dozzle-host/api/auth/login?provider=github' | grep -i set-cookie
set-cookie: dozzle_oauth_state=...; Path=/; Max-Age=600; HttpOnly; Secure; SameSite=Lax

La présence de Secure dans cette réponse veut dire que l'en-tête arrive bien. S'il manque, réparez le proxy avant d'aller plus loin. Voir Reverse proxy et chemin de base pour des exemples Nginx, Traefik et Caddy.

Utiliser les secrets Docker pour le client secret

Mettre un client secret directement dans environment: veut dire qu'il apparaît dans docker inspect, dans votre fichier compose, et dans l'historique du shell de quiconque a lancé le conteneur à la main. Les deux client secrets acceptent donc un équivalent _FILE qui nomme un fichier depuis lequel lire la valeur, la convention qu'utilisent les images officielles de Docker :

Au lieu deUtilisez
DOZZLE_AUTH_GITHUB_CLIENT_SECRETDOZZLE_AUTH_GITHUB_CLIENT_SECRET_FILE
DOZZLE_AUTH_OIDC_CLIENT_SECRETDOZZLE_AUTH_OIDC_CLIENT_SECRET_FILE

Dozzle lit le fichier au démarrage et supprime les espaces autour, un retour à la ligne final laissé par echo secret > file ne pose donc pas de problème. Définir à la fois une variable et son équivalent _FILE est une erreur plutôt qu'une préférence silencieuse pour l'une des deux, et pointer _FILE sur un fichier manquant ou vide arrête Dozzle au démarrage au lieu de désactiver discrètement le bouton de connexion.

Docker Compose

Hors Swarm, docker secret create n'existe pas : un secret Compose est donc soit un fichier sur le disque, soit une variable d'environnement. La forme environnement est en général celle qu'il vous faut, elle va de pair avec un .env ignoré par git et aucun fichier en clair ne traîne à côté de votre fichier compose en attendant d'être commité.

yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    ports:
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/data
    environment:
      DOZZLE_AUTH_PROVIDER: simple
      DOZZLE_AUTH_GITHUB_CLIENT_ID: Ov23liABCDEFGHIJKLMN
      DOZZLE_AUTH_GITHUB_CLIENT_SECRET_FILE: /run/secrets/dozzle_github_secret
    secrets:
      - dozzle_github_secret

secrets:
  dozzle_github_secret:
    environment: GITHUB_CLIENT_SECRET
ini
GITHUB_CLIENT_SECRET=your-github-client-secret

Compose lit lui-même la variable et monte la valeur sur /run/secrets/dozzle_github_secret. Elle ne fait jamais partie de l'environnement du conteneur, elle reste donc hors de docker inspect, exactement comme un secret adossé à un fichier.

Utilisez plutôt la forme fichier quand le secret existe déjà sous forme de fichier, par exemple écrit par un gestionnaire de secrets :

yaml
secrets:
  dozzle_github_secret:
    file: /run/secrets/github_client_secret

Dans les deux cas, Dozzle supprime les espaces autour de la valeur, un retour à la ligne final dans le fichier n'a donc aucune importance.

Docker Swarm

En Swarm le secret est géré par le cluster plutôt que par un fichier sur le disque, créez-le donc avec docker secret create et déclarez-le comme external :

sh
printf '%s' 'your-oidc-client-secret' | docker secret create dozzle_oidc_secret_v1 -
yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    environment:
      DOZZLE_MODE: swarm
      DOZZLE_AUTH_PROVIDER: simple
      DOZZLE_AUTH_OIDC_ISSUER: https://id.example.com
      DOZZLE_AUTH_OIDC_CLIENT_ID: dozzle
      DOZZLE_AUTH_OIDC_CLIENT_SECRET_FILE: /run/secrets/dozzle_oidc_secret
      DOZZLE_AUTH_OIDC_NAME: Pocket ID
    secrets:
      - dozzle_oidc_secret
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      mode: global

secrets:
  dozzle_oidc_secret:
    external: true
    name: dozzle_oidc_secret_v1

Remarquez la séparation entre les deux noms. dozzle_oidc_secret est l'alias utilisé par ce fichier compose, et c'est lui qui décide du chemin de montage : le secret arrive dans /run/secrets/dozzle_oidc_secret, ce qui correspond à _FILE. name: est l'objet réel sur le swarm, et c'est le seul endroit où la version apparaît.

Cette séparation existe parce que les secrets Swarm sont immuables. Il n'y a aucun moyen de changer la valeur d'un secret existant, faire tourner un client secret fuité ou expiré veut donc dire créer la version suivante et pointer la stack dessus. Garder la version hors de l'alias transforme cela en une modification d'une seule ligne, au lieu de trois modifications à maintenir synchronisées entre _FILE, la liste secrets: du service et la déclaration de premier niveau :

sh
printf '%s' 'your-new-client-secret' | docker secret create dozzle_oidc_secret_v2 -
yaml
secrets:
  dozzle_oidc_secret:
    external: true
    name: dozzle_oidc_secret_v2 # was _v1

Redéployez la stack, puis supprimez l'ancien avec docker secret rm dozzle_oidc_secret_v1. La variable d'environnement et le chemin de montage n'ont pas bougé.

NOTE

Sans name:, un secret est monté sur /run/secrets/<alias> et l'alias doit correspondre à l'objet réel sur le swarm. Avec name: les deux sont découplés, et c'est ce qui rend la rotation ci-dessus possible en une seule modification. Dans les deux cas, _FILE pointe sur l'alias, jamais sur name:.

Vérifier que ça a marché

Dozzle journalise au démarrage les fournisseurs qu'il a activés. Lancez-le avec --level debug et cherchez la ligne qui nomme le fournisseur :

sh
$ docker compose logs dozzle | grep -i 'sign in'
DBG Enabling Sign in with GitHub

Si le fichier du secret est manquant ou vide, Dozzle s'arrête au démarrage avec un message qui nomme la variable, un montage cassé échoue donc bruyamment au lieu de faire disparaître silencieusement le bouton de connexion.

Publié sous licence MIT. Open source et sponsorisé par Docker OSS.