3 points par GN⁺ 2024-02-12 | 1 commentaires | Partager sur WhatsApp
  • Procédure de déploiement SSO qui exécute Keycloak de façon isolée avec Docker rootless sur une seule VM Linux, tandis que le nginx système prend en charge la terminaison TLS et le reverse proxy
  • Les prérequis sont un accès SSH, un domaine ou sous-domaine pour Keycloak, un enregistrement A, un utilisateur keycloak sans mot de passe, un répertoire personnel /srv/keycloak et une installation Docker rootless
  • docker-compose.yml utilise postgres:16 et quay.io/keycloak/keycloak:25.0.1 ; Keycloak est lié uniquement à 127.0.0.1:8080 et les mots de passe ainsi que KC_HOSTNAME sont gérés dans .env
  • nginx redirige les requêtes HTTP de your.tld.com vers HTTPS et les transmet avec proxy_pass ; après l’émission du certificat Certbot, KC_PROXY_HEADERS: xforwarded est activé
  • Si un thème personnalisé ou une exécution avec --optimized est nécessaire, il faut construire sa propre image avec un Dockerfile multistage ; en production, on peut aussi ajuster la taille des buffers JGroups et la configuration de récupération des transactions Quarkus

Architecture de déploiement et prérequis

  • Keycloak est une option IAM open source utilisable lorsqu’il est difficile d’implémenter directement la gestion des utilisateurs et le SSO dans une application web
  • Il est interopérable avec les principaux protocoles SSO comme OpenID Connect (OIDC), OAuth 2.0 et SAML
  • La configuration a été rédigée pour Keycloak 23.0.6 et testée jusqu’à 25.0.5
  • L’architecture globale consiste à placer nginx comme reverse proxy central, puis à transmettre le trafic vers localhost dans des espaces de noms Docker rootless propres à chaque service
    • Le trafic web arrive sur 0.0.0.0:80 et 0.0.0.0:443, et nginx le transmet à Keycloak sur 127.0.0.1:8080
    • L’objectif est une configuration économique qui partage les ressources d’un seul hôte tout en isolant les environnements de service

Préparation

  • L’environnement de base nécessite une VM Linux, un accès distant SSH et un domaine ou sous-domaine pour le service Keycloak
    • Le domaine nécessite un enregistrement A et peut éventuellement recevoir un enregistrement AAAA
  • La configuration de base de Docker rootless suit la procédure d’un autre billet Mastodon
    • Création d’un nouvel utilisateur non-root keycloak sans mot de passe
    • Définition du répertoire personnel sur /srv/keycloak
    • Ajout de la plage de l’utilisateur keycloak dans /etc/subuid et /etc/subgid
    • Installation de Docker rootless avec dockerd-rootless-setuptool.sh
    • Configuration du démarrage automatique du service pour l’utilisateur keycloak

Exécuter Keycloak avec Docker Compose

  • Pour se connecter avec l’utilisateur keycloak nouvellement créé, utiliser machinectl shell keycloak@
    • La méthode sudo -u keycloak -H bash est à éviter, car la variable d’environnement XDG_RUNTIME_DIR n’est pas préparée
  • Créer d’abord les répertoires pour les données persistantes et les fichiers Docker
    • Le chemin des données PostgreSQL est /srv/keycloak/data/postgres16
    • Les fichiers Docker sont placés dans ~/docker
  • docker-compose.yml utilise directement l’image officielle Keycloak et PostgreSQL
    • L’image PostgreSQL est postgres:16
    • L’image Keycloak est quay.io/keycloak/keycloak:25.0.1
    • En production, il est recommandé d’utiliser un tag d’image précis plutôt que :latest
  • Les principaux paramètres du conteneur Keycloak sont les suivants
    • KC_DB: postgres
    • KC_DB_URL: jdbc:postgresql://postgres_db/keycloak
    • KC_HOSTNAME: ${KC_HOSTNAME:-your.tld.com}
    • KC_HTTP_ENABLED: true
    • HTTP_ADDRESS_FORWARDING: true
    • KEYCLOAK_ADMIN: admin
    • Le port est lié uniquement à localhost avec '127.0.0.1:8080:8080'
  • .env contient les valeurs sensibles ou variables selon l’environnement
    • POSTGRES_PASSWORD
    • KEYCLOAK_ADMIN_PASSWORD
    • KC_HOSTNAME
  • La syntaxe ${KC_HOSTNAME:-your.tld.com} utilise la valeur de KC_HOSTNAME si elle existe dans .env, sinon your.tld.com comme valeur par défaut
  • /srv/keycloak/data/postgres16 contient les données persistantes de PostgreSQL et doit donc faire l’objet de sauvegardes régulières
    • Pour une réinitialisation complète, supprimer puis recréer ce dossier PostgreSQL ; il sera recréé au démarrage suivant
  • Si ~/docker est géré comme dépôt Git, ajouter .env à .gitignore et ne commiter que docker-compose.yml

Test local

  • Démarrer la stack Docker Compose et consulter les logs
docker compose up -d && docker compose logs --follow
  • Pour accéder au port local Keycloak de la VM, créer un tunnel SSH inverse
ssh you@111.11.11.11 -L :8080:127.0.0.1:8080 -p 22 -N -v
  • Ouvrir 127.0.0.1:8080 dans le navigateur pour vérifier l’écran d’accueil de Keycloak

Reverse proxy nginx et TLS

  • Après s’être déconnecté de l’utilisateur keycloak, configurer le nginx système
  • Remplacer your.tld.com par le domaine réel et ajouter un enregistrement A chez le registrar pour que les requêtes DNS pointent vers l’IP de la VM
  • Créer et activer le fichier de configuration du site nginx
nano /etc/nginx/sites-available/your.tld.com.conf
ln -s /etc/nginx/sites-available/your.tld.com.conf /etc/nginx/sites-enabled/
  • La configuration nginx redirige les requêtes HTTP vers HTTPS et, dans le bloc serveur HTTPS, fait proxy vers Keycloak
    • proxy_set_header Host $host
    • proxy_set_header X-Real-IP $remote_addr
    • proxy_set_header X-Forwarded-For $remote_addr
    • proxy_set_header X-Forwarded-Proto $scheme
    • proxy_pass http://127.0.0.1:8080
  • Pour la configuration SSL, il est recommandé d’utiliser le Mozilla SSL configurator for nginx afin de générer des valeurs par défaut adaptées à la version de nginx
  • Tester la configuration puis recharger nginx
nginx -t
systemctl reload nginx
  • Lors de l’émission du certificat avec Certbot, les lignes nécessaires dans your.tld.com.conf sont automatiquement mises à jour
certbot --nginx -d your.tld.com
  • Activer ensuite ssl_trusted_certificate /etc/letsencrypt/live/your.tld.com/chain.pem; et recharger nginx
  • Activer KC_PROXY_HEADERS: xforwarded dans docker-compose.yml pour fonctionner derrière nginx, puis redémarrer la stack Docker
docker compose down && docker compose up -d && docker compose logs --follow

Pistes de débogage

  • Après la configuration, se connecter à your.tld.com et vérifier la connexion de l’utilisateur admin avec le mot de passe de .env
  • Le premier point à vérifier est les logs Docker Compose
docker compose logs --follow
  • Suivre les logs d’accès et d’erreur nginx dans les fichiers suivants
tail -f /var/log/nginx/your.tld.com-access.log
tail -f /var/log/nginx/your.tld.com-error.log
  • S’il faut inspecter directement la base de données Keycloak, se connecter avec l’utilisateur keycloak et exécuter psql dans le conteneur PostgreSQL
machinectl shell keycloak@
cd ~/docker
docker compose exec postgres_db /bin/bash
psql -h localhost -p 5432 -U keycloak keycloak

Image personnalisée et exécution optimisée

  • La configuration de base utilise l’image préconstruite de quay.io
  • Si une personnalisation de thème ou une exécution en mode --optimized est nécessaire, construire sa propre image
  • Le Dockerfile utilise un build multistage basé sur l’image officielle Keycloak
    • Définir ENV KC_DB=postgres à l’étape builder
    • Exécuter /opt/keycloak/bin/kc.sh build
    • Copier /opt/keycloak/ dans l’image finale
    • Spécifier ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
  • Dans docker-compose.yml, modifier comme suit
    • Supprimer ou commenter la ligne image:
    • Activer build: .
    • Ajouter command: start --optimized
  • Ensuite, arrêter la stack Docker, lancer éventuellement un build explicite, puis redémarrer
docker compose down
docker compose build
docker compose up -d && docker compose logs --follow

Ajustements supplémentaires en production

  • Les avertissements de buffer de réception MulticastSocket liés à JGroups se corrigent en ajoutant les valeurs de buffer suivantes dans /etc/sysctl.conf sur l’hôte
net.core.rmem_max = 26214400
net.core.wmem_max = 1048576
  • Exécuter sysctl -p pour appliquer la configuration
  • L’avertissement de récupération des transactions XA de Quarkus se corrige en ajoutant un montage de volume et une variable d’environnement au service Keycloak
    • Volume : /srv/keycloak/data/keycloak/ObjectStore/:/ObjectStore/
    • Variable d’environnement : QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY: true
  • Avant le démarrage, créer le répertoire ObjectStore sur l’hôte et, dans le conteneur, changer le propriétaire de /ObjectStore pour l’ID utilisateur 1000

Étapes après configuration

  • L’état final est une configuration où le service Keycloak dans Docker rootless s’exécute derrière le reverse proxy nginx système, nginx prenant en charge la terminaison SSL
  • Pour la mise à jour automatique des conteneurs, se référer à la procédure de mise à jour automatique d’un autre billet Mastodon
  • L’étape de configuration suivante consiste à ajouter l’e-mail dans la console d’administration Keycloak
  • Ensuite, il est possible d’ajouter des realms et de configurer les thèmes
    • Pour les thèmes, on peut utiliser keycloakify et keycloakify-starter
    • Le Dockerfile contient une ligne commentée qui copie le JAR du thème keycloakify vers /opt/keycloak/providers/

1 commentaires

 
GN⁺ 2024-02-12
Avis sur Hacker News
  • J’ai récemment ajouté une couche d’authentification à mon homelab et j’ai choisi Authelia
    Keycloak fonctionne aussi, mais c’est trop massif, et il faut encore des services supplémentaires pour l’utiliser avec traefik forward auth
    Authelia n’a pas d’interface UI pour modifier les utilisateurs et ne propose pas non plus de synchronisation bidirectionnelle avec un serveur LDAP backend, mais il se configure uniquement avec des fichiers statiques et des variables d’environnement, ce qui le rend bien adapté dans de nombreux cas
    Si l’objectif est simplement d’ajouter de l’authentification à quelques services et du SSO là où c’est possible, cela vaut le coup de commencer par Authelia

    • Je travaille chez FusionAuth
      Si vous avez besoin de SSO, d’une configuration simple et d’une UI d’administration, FusionAuth vaut aussi le détour. L’UI/UX a un petit côté milieu des années 2000, mais on peut le télécharger et l’exécuter soi-même[0], c’est Docker-friendly[1], et il propose plusieurs méthodes de configuration[2], comme la gestion d’OIDC ou d’autres paramètres avec Terraform[3]
      C’est utilisable gratuitement, mais ce n’est pas open source[4]
      0: https://fusionauth.io/download
      1: https://fusionauth.io/docs/get-started/download-and-install/...
      2: https://fusionauth.io/docs/operate/deploy/configuration-mana...
      3: https://fusionauth.io/docs/operate/deploy/terraform
      4: https://fusionauth.io/license-faq#28
    • J’utilise Authelia depuis plus de deux ans et j’ai mis en place l’intégration LDAP avec LLDAP[0], une implémentation LDAP légère
      J’utilise Caddy comme reverse proxy et il fonctionne bien avec l’intégration d’Authelia[1]
      J’ai ajouté une authentification à deux facteurs solide à tous les services, et je trouve l’ensemble suffisamment sûr pour accéder à mes applications auto-hébergées même sans VPN
      Cela dit, Authelia n’a pas eu de nouvelle release depuis plus d’un an, ce qui m’inquiète du point de vue de la sécurité
      [0] https://github.com/lldap/lldap
      [1] https://www.authelia.com/integration/proxies/caddy/
    • J’ai récemment suivi la même voie et, personnellement, j’ai choisi caddy-security[1], un plugin Caddy
      [1] https://github.com/greenpau/caddy-security
    • Authelia a un avantage très particulier : il convient aux petits déploiements, un domaine où Keycloak ou authentik s’adaptent mal
      C’est un excellent cas d’usage pour un homelab, surtout si l’on n’a pas besoin ou pas envie des fonctionnalités de solutions plus lourdes
    • Petite digression : je me demande s’il existe aujourd’hui des alternatives à LDAP
      Même en cherchant, je ne trouve rien de type schéma simple, JSON ou HTTP, et j’ai du mal à croire que tous ceux qui ont tout reconstruit n’aient pas aussi refait LDAP
      À ma connaissance, le seul autre standard de fait est Active Directory
      Je me demande aussi s’il existe un standard ou un protocole pour déléguer le contrôle d’accès à un service externe
      Authelia peut contrôler l’accès à partir de motifs d’URL, mais pour quelque chose comme un serveur de fichiers, je m’attendrais plutôt à vérifier les autorisations sur un serveur LDAP en fonction de l’ID de l’utilisateur authentifié et des clés de la base de données
      Cela semble aller dans le sens inverse d’OAuth2, où c’est le serveur qui obtient l’autorisation d’accéder à un service tiers
  • Récemment, j’ai étudié la mise en place d’un SSO relativement simple dans mon homelab, avec comme objectif principal de pouvoir se connecter facilement via l’authentification Google ou GitHub
    Dans mon précédent emploi, j’avais utilisé à la fois JetBrains Hub[1] et Keycloak, et tous deux étaient assez pénibles à configurer
    JetBrains Hub permettait vraiment de démarrer très facilement, et cela correspondait aussi à mon expérience passée, mais l’absence du tag latest sur le registre Docker était gênante
    Je sais qu’il est préférable d’épingler les versions, mais pour un usage personnel, je veux généralement mettre à jour tous mes conteneurs Docker d’un coup
    À l’inverse, Keycloak était très pénible à prendre en main : c’était simple en mode développement, mais je me suis retrouvé bloqué sur la configuration de production
    Si je me souviens bien, c’était lié à un certificat wildcard Let’s Encrypt, et j’ai abandonné après quelques heures
    Au final, j’ai choisi Dex[2]. J’avais repoussé son adoption à cause d’une documentation insuffisante, mais la configuration réelle a été très simple : un YAML de base, une base de données SQLite et un sous-domaine suffisaient
    J’ai combiné Dex avec l’excellent OAuth2 Proxy[3] et un modèle personnalisé de Nginx Proxy Manager, ce qui m’a permis de ramener la configuration SSO de chaque service interne à deux lignes, et j’ai aussi créé un modèle Docker Dex pour unRAID[4]
    En complément, j’ai ajouté Cloudflare Access et un WAF pour renforcer la sécurité depuis l’extérieur du domicile, et je voudrais simplement ajouter CrowdSec pour obtenir un peu plus de visibilité

    1. https://www.jetbrains.com/hub/
    2. https://dexidp.io/
    3. https://github.com/oauth2-proxy/oauth2-proxy
    4. https://github.com/alex3305/unraid-docker-templates
    • J’utilise obligator avec un stockage temporaire, sans base de données, et une configuration 100 % pilotée par le code
      Personnellement, je pense que c’est l’option la plus simple
      https://github.com/lastlogin-io/obligator
    • Je me demande quelles fonctionnalités oauth2-proxy apporte que Dex n’a pas
  • J’ai créé un tableau comparatif inachevé de plusieurs serveurs OpenID Connect auto-hébergeables[0]
    L’un des points surprenants, c’est à quel point la base de code de Keycloak est énorme
    [0]: https://github.com/lastlogin-io/obligator?tab=readme-ov-file...

  • L’idée que Keycloak résout les problèmes de sécurité est assez comique
    Il suffit de regarder la liste des CVE pour comprendre ce que cela veut dire

  • Au moins, le fait que l’article mentionne https://www.keycloakify.dev/ est très utile
    Cela semble être une excellente alternative à l’approche standard des thèmes

    • C’est un excellent projet
      Je l’utilise depuis 2 ans et je n’ai que du bien à en dire
      Le mainteneur répond très vite, et récemment des changements ont aussi été apportés, côté Keycloak comme côté keycloakify, pour améliorer la compatibilité future de l’approche par thèmes
      Pour l’instant, seules les applications créées avec create-react-app sont officiellement prises en charge, mais la branche vite est en cours de développement, et personnellement je l’utilise déjà avec vite depuis quelque temps
    • Je travaille chez un concurrent, et comme nous sommes en train de retravailler le système de thèmes, ce projet semble intéressant à examiner et à prendre comme modèle de refonte
  • Je garde un œil sur authentik[1] et authelia[2]
    Authelia a l’air très bien, mais alors que Keycloak dispose d’un connecteur pour Angular, avec Authelia il faut par exemple configurer soi-même un plugin OIDC pour Angular, ce qui me rendait un peu prudent
    Cela dit, s’il existe une configuration pour Keycloak, le démarrage devrait être plus facile
    [1] https://goauthentik.io/
    [2] https://www.authelia.com/

    • Pour quiconque envisage authentik, je voudrais prévenir que c’est « ici vivent des dragons »
      Je protège en permanence plus de 10 services avec Docker et Kubernetes, et si vous n’aimez pas gérer la protection de chaque service de façon indépendante, authentik va vous faire souffrir
      authentik a un bug critique[0] lorsqu’on protège plusieurs sous-domaines (app1.example.com, app2.example.com, etc.) avec une seule configuration : après l’expiration de la session, une nouvelle authentification redirige aléatoirement l’utilisateur vers un autre service
      [0]: https://github.com/goauthentik/authentik/issues/6886
    • Authentik a complètement raté l’implémentation du grant OAuth client credentials
      Il est impossible de corriger ça sans changement cassant, et cela ne fonctionne pas avec de nombreux outils qui utilisent le grant cc
      Après avoir vu ça, je l’ai complètement retiré de ma liste de candidats
      https://github.com/goauthentik/authentik/issues/6139
    • Je suis l’un des mainteneurs principaux d’Authelia
      S’il y a quoi que ce soit sur la configuration Angular pour lequel je peux aider, je le ferai volontiers via GitHub Discussions
    • Je suis en train de prendre cette même décision en ce moment
      Authentik semblait meilleur, mais le bug mentionné dans une autre réponse a l’air assez inquiétant
  • Le problème de Keycloak, c’est que c’est un vieux projet qui a connu énormément de changements
    Il a commencé comme un projet JBOSS, et son utilité en tant qu’IdP brille pour l’authentification de clusters on-premise, mais pas vraiment au-delà
    J’ai déployé Keycloak à grande échelle sur AWS ECS dans un service d’une entreprise du Fortune 500, et parvenir à un clustering correct a ressemblé à une guerre de Cent Ans
    La découverte DNS ne fonctionnait pas correctement, et la découverte de cluster reposait sur UDP, donc cela ne marchait pas dans le cloud
    Les connexions avec état présentes sur un serveur n’existaient pas sur les autres, donc un simple équilibrage de charge était impossible, et les sessions persistantes étaient la seule option
    Lancer Keycloak avec docker run pour l’utiliser comme Auth0, c’est facile, mais ça donne l’impression d’acheter un Ford F-150 de 1996 avec 250 000 miles au compteur
    Ça roule et ça fonctionne, mais la maintenance est vraiment horrible

    • Je l’ai fait à petite échelle dans un Docker Swarm on-premise, et c’était effectivement pénible
      Si je me souviens bien, le mode de découverte par défaut utilise le multicast, qui n’est pas activé dans les réseaux cloud classiques ni dans les réseaux overlay de Swarm/Kubernetes
      J’ai aussi vu le ping base de données, qui utilise le SGBDR comme une sorte de mécanisme de quorum, mais cela semblait très fragile, presque comme une solution de dernier recours
      Au final, on a pu utiliser le driver de cluster Kubernetes pour découvrir les nœuds via le DNS du cluster Swarm
      C’était assez difficile à faire fonctionner, mais une fois en place, à ma connaissance, c’est resté stable
      Il semble qu’il existe aujourd’hui un driver réseau EC2 natif, mais je ne l’ai pas exploré moi-même
    • J’ai entendu dire qu’il y avait eu des améliorations après la migration complète vers Quarkus
      Je me demande quand ce déploiement sur ECS a eu lieu
    • OAuth, OpenID Connect, et la possibilité d’ajouter des apps et des clients à un realm ont été le salut de Keycloak, et la seule raison pour laquelle je l’ai gardé
    • Le fait que la découverte de cluster reposait sur UDP, ça fait mal à entendre
      Chez nous, l’UDP n’est autorisé que pour le DNS
  • Keycloak est excellent, mais il peut être assez déroutant pour les débutants
    Il a beaucoup de fonctionnalités et la documentation n’est pas exceptionnelle
    J’ai récemment essayé Zitadel, et c’était bien plus simple à utiliser
    En revanche, on ne peut pas l’exécuter avec une base de données intégrée, donc l’auto-hébergement est un peu plus compliqué

    • Oui, il lui faut sa propre base de données
      À l’avenir, ils vont utiliser Postgres comme base de données, donc j’espère que cela facilitera le déploiement avec d’autres outils
  • Mon manager a récemment qualifié Keycloak de « cadeau qui continue de donner », ce qui voulait en fait dire que de nouveaux tickets Jira apparaissaient sans cesse juste pour comprendre comment faire telle ou telle chose
    Malgré ça, nous avons du Terraform pour créer un cluster EKS, déployer Keycloak, créer des clients SAML/OIDC, ajouter des fournisseurs d’identité externes, et même configurer AWS IAM Identity Provider
    Une fois qu’on a compris ce qu’il est possible de faire, comment le faire dans l’UI, et comment l’automatiser avec le provider Terraform de mrparkers, l’utilisation elle-même devient très simple

    • Je me demande si ce Terraform est open source quelque part
      Un ami en aurait besoin :)
  • Après avoir utilisé Keycloak pendant plusieurs années, j’ai honnêtement commencé à en avoir assez de tous ses comportements bizarres et j’ai commencé à chercher une alternative
    Plusieurs candidats, dont Authentik, semblaient corrects, mais Zitadel[1] a retenu mon attention, et je ne me suis pas retourné depuis
    [1] https://zitadel.com/blog/zitadel-vs-keycloak

    • Ravi que ça te plaise
      Je suis curieux de savoir ce qui t’a convaincu de façon décisive