3 puntos por GN⁺ 2024-02-12 | 1 comentarios | Compartir por WhatsApp
  • Es un procedimiento de despliegue de SSO en una sola VM Linux, ejecutando Keycloak aislado con Docker rootless, mientras nginx del sistema se encarga de la terminación TLS y del proxy inverso
  • Los requisitos previos son acceso por SSH, un dominio o subdominio para Keycloak, un registro A, un usuario keycloak sin contraseña, el directorio home /srv/keycloak y una instalación de Docker rootless
  • docker-compose.yml usa postgres:16 y quay.io/keycloak/keycloak:25.0.1; Keycloak se enlaza solo a 127.0.0.1:8080 y las contraseñas y KC_HOSTNAME se administran desde .env
  • nginx redirige las solicitudes HTTP de your.tld.com a HTTPS y las reenvía con proxy_pass; después de emitir el certificado con Certbot, se habilita KC_PROXY_HEADERS: xforwarded
  • Si se necesita un tema personalizado o ejecutar con --optimized, se crea una imagen propia con un Dockerfile multietapa, y en producción se pueden ajustar además el tamaño de los buffers de JGroups y la configuración de recuperación de transacciones de Quarkus

Estructura del despliegue y requisitos previos

  • Keycloak es una opción de IAM open source que se puede usar cuando resulta difícil implementar directamente la gestión de usuarios y SSO en una aplicación web
  • Interopera con los principales protocolos de SSO como OpenID Connect (OIDC), OAuth 2.0 y SAML
  • La configuración se escribió tomando como base Keycloak 23.0.6 y se probó hasta 25.0.5
  • La estructura general usa nginx como proxy inverso central, reenviando el tráfico por localhost hacia namespaces de Docker rootless separados por servicio
    • El tráfico web entra por 0.0.0.0:80 y 0.0.0.0:443, y nginx lo reenvía a Keycloak en 127.0.0.1:8080
    • El objetivo es una configuración económica que comparte los recursos de un único host mientras separa los entornos de servicio

Preparación

  • Como entorno base se necesita una VM Linux, acceso remoto por SSH y un dominio o subdominio para el servicio Keycloak
    • El dominio necesita un registro A y, opcionalmente, se puede agregar un registro AAAA
  • La configuración básica de Docker rootless sigue el procedimiento de otra publicación de Mastodon
    • Crear un nuevo usuario non-root keycloak sin contraseña
    • Establecer el directorio home en /srv/keycloak
    • Agregar el rango del usuario keycloak en /etc/subuid y /etc/subgid
    • Instalar Docker rootless con dockerd-rootless-setuptool.sh
    • Configurar el inicio automático del servicio del usuario keycloak

Ejecutar Keycloak con Docker Compose

  • Al entrar con el usuario keycloak recién creado, usar machinectl shell keycloak@
    • Se evita el método sudo -u keycloak -H bash porque no prepara la variable de entorno XDG_RUNTIME_DIR
  • Primero crear los directorios para datos persistentes y archivos Docker
    • La ruta de datos de PostgreSQL es /srv/keycloak/data/postgres16
    • Los archivos Docker se colocan en ~/docker
  • docker-compose.yml usa directamente la imagen oficial de Keycloak y PostgreSQL
    • La imagen de PostgreSQL es postgres:16
    • La imagen de Keycloak es quay.io/keycloak/keycloak:25.0.1
    • En entornos de producción se recomienda usar una etiqueta de imagen específica en lugar de :latest
  • La configuración principal del contenedor de Keycloak es la siguiente
    • 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
    • El puerto se enlaza solo a localhost con '127.0.0.1:8080:8080'
  • En .env se colocan valores sensibles o que varían según el entorno
    • POSTGRES_PASSWORD
    • KEYCLOAK_ADMIN_PASSWORD
    • KC_HOSTNAME
  • La sintaxis ${KC_HOSTNAME:-your.tld.com} usa el valor de KC_HOSTNAME si existe en .env; si no existe, usa your.tld.com como valor predeterminado
  • /srv/keycloak/data/postgres16 contiene los datos persistentes de PostgreSQL, por lo que debe incluirse en respaldos periódicos
    • Para reinicializarlo por completo, borrar esa carpeta de PostgreSQL y volver a crearla; se generará nuevamente en el siguiente inicio
  • Si administras ~/docker como repositorio Git, puedes agregar .env a .gitignore y commitear solo docker-compose.yml

Prueba local

  • Iniciar el stack de Docker Compose y revisar los logs
docker compose up -d && docker compose logs --follow
  • Para acceder al puerto local de Keycloak en la VM, crear un túnel SSH inverso
ssh you@111.11.11.11 -L :8080:127.0.0.1:8080 -p 22 -N -v
  • Abrir 127.0.0.1:8080 en el navegador y verificar la pantalla de bienvenida de Keycloak

Proxy inverso nginx y TLS

  • Después de cerrar sesión del usuario keycloak, configurar nginx del sistema
  • Reemplazar your.tld.com por el dominio real y agregar un registro A en el registrador del dominio para que las consultas DNS apunten a la IP de la VM
  • Crear y habilitar el archivo de configuración del sitio 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 configuración de nginx redirige las solicitudes HTTP a HTTPS y, en el bloque del servidor HTTPS, hace proxy hacia 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
  • Para la configuración SSL se recomienda usar Mozilla SSL configurator for nginx para generar valores predeterminados adecuados a la versión de nginx
  • Probar la configuración y recargar nginx
nginx -t
systemctl reload nginx
  • Al emitir el certificado con Certbot, las líneas necesarias en your.tld.com.conf se actualizan automáticamente
certbot --nginx -d your.tld.com
  • Después, habilitar ssl_trusted_certificate /etc/letsencrypt/live/your.tld.com/chain.pem; y recargar nginx
  • Para que funcione detrás de nginx, habilitar KC_PROXY_HEADERS: xforwarded en docker-compose.yml y reiniciar el stack Docker
docker compose down && docker compose up -d && docker compose logs --follow

Ruta de depuración

  • Después de configurar, conectarse a your.tld.com y verificar el inicio de sesión del usuario admin con la contraseña de .env
  • El primer punto de revisión son los logs de Docker Compose
docker compose logs --follow
  • Revisar los logs de acceso y error de nginx siguiendo estos archivos
tail -f /var/log/nginx/your.tld.com-access.log
tail -f /var/log/nginx/your.tld.com-error.log
  • Si necesitas revisar directamente la base de datos de Keycloak, entra como usuario keycloak y ejecuta psql dentro del contenedor PostgreSQL
machinectl shell keycloak@
cd ~/docker
docker compose exec postgres_db /bin/bash
psql -h localhost -p 5432 -U keycloak keycloak

Imagen personalizada y ejecución optimized

  • La configuración básica usa la imagen precompilada de quay.io
  • Si se requiere personalizar temas o ejecutar en modo --optimized, se construye una imagen propia
  • El Dockerfile usa una compilación multietapa basada en la imagen oficial de Keycloak
    • En la etapa builder, establecer ENV KC_DB=postgres
    • Ejecutar /opt/keycloak/bin/kc.sh build
    • Copiar /opt/keycloak/ a la imagen final
    • Definir ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
  • En docker-compose.yml, cambiarlo así
    • Eliminar o comentar la línea image:
    • Habilitar build: .
    • Agregar command: start --optimized
  • Luego detener el stack Docker, opcionalmente ejecutar un build explícito y volver a iniciarlo
docker compose down
docker compose build
docker compose up -d && docker compose logs --follow

Ajustes adicionales realizados en producción

  • La advertencia de buffer de recepción de MulticastSocket relacionada con JGroups se resuelve agregando valores de buffer en /etc/sysctl.conf del host
net.core.rmem_max = 26214400
net.core.wmem_max = 1048576
  • Para aplicar la configuración, ejecutar sysctl -p
  • La advertencia de recuperación de transacciones XA de Quarkus se resuelve agregando un montaje de volumen y una variable de entorno al servicio Keycloak
    • Volumen: /srv/keycloak/data/keycloak/ObjectStore/:/ObjectStore/
    • Variable de entorno: QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY: true
  • Antes de iniciar, crear el directorio ObjectStore en el host y cambiar el propietario de /ObjectStore dentro del contenedor al ID de usuario 1000

Pasos posteriores a la configuración

  • El estado final es una configuración en la que el servicio Keycloak dentro de Docker rootless se ejecuta detrás del proxy inverso nginx del sistema, y nginx se encarga de la terminación SSL
  • Para las actualizaciones automáticas de contenedores, consultar el procedimiento de actualización automática en otra publicación de Mastodon
  • El siguiente paso de configuración es agregar el correo electrónico desde la consola de administración de Keycloak
  • Luego se puede continuar con la creación de realms y la configuración de temas
    • Para los temas se pueden usar keycloakify y keycloakify-starter
    • El Dockerfile incluye una línea comentada para copiar el JAR del tema de keycloakify a /opt/keycloak/providers/

1 comentarios

 
GN⁺ 2024-02-12
Opiniones de Hacker News
  • Hace poco, al agregar autenticación a mi homelab, elegí Authelia
    Keycloak también funciona, pero es demasiado grande, y para usarlo con traefik forward auth se necesita otro servicio adicional
    Authelia no tiene una UI para editar usuarios y tampoco sincronización bidireccional con un servidor LDAP backend, pero se puede configurar solo con archivos estáticos y variables de entorno, así que encaja bien en muchos casos
    Si lo que buscas es agregar autenticación a algunos servicios y sumar SSO donde sea posible, vale la pena empezar por Authelia

    • Trabajo en FusionAuth
      Si necesitas SSO, configuración sencilla y una UI de administración, FusionAuth también vale la pena. La UI/UX sí se siente de mediados de los 2000, pero se puede descargar y ejecutar directamente[0], es compatible con Docker[1], y ofrece varias formas de configuración[2], como administrar OIDC u otros ajustes con Terraform[3]
      Se puede usar gratis, pero no es 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
    • Llevo más de 2 años operando Authelia, y configuré la integración con LDAP usando LLDAP[0], una implementación ligera de LDAP
      Uso Caddy como reverse proxy y lo integré con Authelia[1]; funciona bien
      Agregué autenticación de dos factores sólida a todos los servicios, y siento que es lo suficientemente seguro acceder a apps autoalojadas sin VPN
      Sin embargo, me preocupa la seguridad porque Authelia lleva más de un año sin nuevos releases
      [0] https://github.com/lldap/lldap
      [1] https://www.authelia.com/integration/proxies/caddy/
    • Hace poco seguí el mismo camino y, personalmente, elegí caddy-security[1], un plugin de Caddy
      [1] https://github.com/greenpau/caddy-security
    • Authelia tiene una ventaja muy particular: una instalación de tamaño pequeño, y a Keycloak o authentik les cuesta encajar bien en ese ámbito
      Es un caso de uso muy bueno para entornos de homelab, y encaja especialmente bien si no necesitas o no quieres las funciones de soluciones más grandes
    • Como tema relacionado, me pregunto si hoy existen alternativas a LDAP
      Por más que busco, no encuentro nada con una forma tipo esquema simple, JSON o HTTP, y me cuesta creer que las personas que reinventaron casi todo no hayan reinventado LDAP
      Hasta donde sé, en la práctica el único otro estándar es Active Directory
      También me pregunto si existe algún estándar o protocolo para delegar el control de acceso a un sistema externo
      Authelia puede controlar el acceso por patrones de URL, pero, por ejemplo, en el caso de un servidor de archivos, esperaría algo como consultar permisos en un servidor LDAP tomando como base el ID del usuario autenticado y una clave en la base de datos
      Esto parece ir en la dirección opuesta a OAuth2, donde el servidor obtiene permisos de acceso a servicios de terceros
  • Hace poco estuve investigando cómo configurar un SSO relativamente simple en mi homelab, y el objetivo principal era iniciar sesión fácilmente con autenticación de Google o GitHub
    En mi trabajo anterior usé tanto JetBrains Hub[1] como Keycloak, y ambos eran bastante molestos de configurar
    JetBrains Hub era realmente fácil de poner en marcha, y esa también fue mi experiencia anterior, pero me resultó incómodo que no hubiera una etiqueta latest en el registro de Docker
    Sé que fijar versiones es una buena práctica, pero para uso personal normalmente quiero actualizar todos los contenedores Docker de una sola vez
    En cambio, Keycloak fue muy engorroso para empezar; en modo de desarrollo era fácil, pero me trabé con la configuración de producción
    Según recuerdo, tenía que ver con un certificado comodín de Let's Encrypt, y después de unas horas me rendí
    Al final elegí Dex[2]. Lo había postergado por falta de documentación, pero la configuración real fue muy sencilla: un YAML básico, una base de datos SQLite y un subdominio alcanzaron
    Combiné Dex con el excelente OAuth2 Proxy[3] y una plantilla personalizada de Nginx Proxy Manager para dejar una configuración de SSO de dos líneas por cada servicio interno, y también hice una plantilla Docker de Dex para unRAID[4]
    Además, fuera de casa agregué Cloudflare Access y WAF para reforzar la seguridad, y solo me gustaría sumar CrowdSec para obtener un poco más de visibilidad

    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
    • Estoy usando obligator como almacenamiento temporal, sin base de datos y con configuración 100% basada en código
      Personalmente, creo que es la opción más simple
      https://github.com/lastlogin-io/obligator
    • Me da curiosidad qué funcionalidad ofrece oauth2-proxy que no tenga Dex
  • Hice una tabla comparativa incompleta de varios servidores OpenID Connect que se pueden autoalojar[0]
    Una de las cosas que me sorprendió fue lo enorme que es realmente el codebase de Keycloak
    [0]: https://github.com/lastlogin-io/obligator?tab=readme-ov-file...

  • Es graciosa la idea de que Keycloak te resuelve los problemas de seguridad
    Si miras la lista de CVE, entiendes a qué me refiero

  • Al menos fue muy útil que ese artículo mencionara https://www.keycloakify.dev/
    Parece una gran alternativa al enfoque estándar de temas

    • Es un proyecto excelente
      Lo uso desde hace 2 años y no tengo más que cosas buenas para decir
      El mantenedor responde muy rápido, y recientemente tanto Keycloak como keycloakify incorporaron cambios que mejoran la compatibilidad futura del enfoque de temas
      Por ahora oficialmente solo admite apps creadas con create-react-app, pero hay una rama vite en desarrollo y, personalmente, ya lo vengo usando con vite desde hace un tiempo
    • Trabajo en un competidor y estamos rehaciendo el sistema de temas, así que este proyecto parece una buena referencia para revisar y tomar como modelo de rediseño
  • Estoy siguiendo de cerca a authentik[1] y authelia[2]
    Authelia se ve muy bien, pero mientras que Keycloak tiene un conector para Angular, con Authelia habría que configurar directamente, por ejemplo, un plugin OIDC para Angular, así que me daba un poco de cautela
    Aun así, si ya hay una configuración para Keycloak, creo que sería más fácil empezar
    [1] https://goauthentik.io/
    [2] https://www.authelia.com/

    • Si alguien está considerando authentik, le advertiría que “aquí hay dragones”
      Protejo constantemente más de 10 servicios en Docker y Kubernetes, y si no te gusta configurar la protección de cada servicio de forma independiente, vas a sufrir con authentik
      authentik tiene un bug grave[0] cuando se protegen varios subdominios (app1.example.com, app2.example.com, etc.) con una sola configuración: después de que expira la sesión y el usuario se vuelve a autenticar, se lo redirige aleatoriamente a otro servicio
      [0]: https://github.com/goauthentik/authentik/issues/6886
    • Authentik arruinó por completo la implementación del grant de credenciales de cliente OAuth
      No se puede arreglar sin cambios que rompan compatibilidad, y no funciona con muchas herramientas que usan el grant cc
      Después de ver eso, lo descarté por completo como candidato
      https://github.com/goauthentik/authentik/issues/6139
    • Soy uno de los mantenedores principales de Authelia
      Si hay algo en lo que pueda ayudar con la configuración de Angular, con gusto lo haré a través de GitHub Discussions
    • Estoy por tomar una decisión similar ahora mismo
      Authentik se veía mejor, pero el bug mencionado en otra respuesta se ve bastante malo
  • El problema de Keycloak es que es un proyecto antiguo y ha cambiado muchísimo
    Empezó como un proyecto de JBOSS, y creo que su utilidad como IdP brilla en la autenticación de clústeres on-premises, pero hasta ahí llega
    Implementé Keycloak a gran escala en AWS ECS para una división de Fortune 500, y lograr que clusterizara correctamente fue como una guerra de mil años
    El descubrimiento por DNS no funcionaba bien, y el descubrimiento del clúster estaba basado en UDP, así que no servía en entornos cloud
    Un login con estado en un servidor no existía en otro, así que el balanceo de carga simple era imposible, y las sesiones sticky eran la única opción
    Levantar Keycloak con docker run y enchufarlo como Auth0 es fácil, pero se siente como comprar una Ford F-150 de 1996 con 250 mil millas
    Anda y funciona, pero el mantenimiento es verdaderamente horrible

    • Lo hice a pequeña escala dentro de Docker Swarm on-premises, y efectivamente fue doloroso
      Por lo que recuerdo, el método de descubrimiento predeterminado usa multicast, que no está habilitado en redes cloud típicas ni en redes overlay de Swarm/Kubernetes
      También vi database ping, que usa un RDBMS como una especie de mecanismo de quórum, pero se veía muy frágil y se sentía como un último recurso
      Al final pude usar el driver de clúster de Kubernetes, que descubre nodos mediante el DNS del clúster de Swarm
      Costó bastante hacerlo funcionar, pero desde entonces, hasta donde sé, es estable
      Parece que hoy también hay un driver de red nativo para EC2, pero no lo he investigado personalmente
    • Escuché que hubo mejoras después de migrar por completo a Quarkus
      Me da curiosidad cuándo fue esa implementación en ECS
    • OAuth y OpenID Connect, y el hecho de poder agregar apps y clientes a un realm, fueron la salvación de Keycloak y la única razón por la que seguimos manteniéndolo
    • Eso de que el descubrimiento del clúster fuera por UDP suena doloroso
      Nosotros solo tenemos permitido UDP para DNS
  • Keycloak es excelente, pero puede ser bastante confuso para principiantes
    Tiene muchas funciones y la documentación no es la mejor
    Hace poco probé Zitadel y fue mucho más fácil de usar
    Eso sí, no puede ejecutarse con una base de datos integrada, así que el self-hosting es un poco más difícil

    • Sí, necesita su propia base de datos
      A futuro van en dirección de usar Postgres como base de datos, así que espero que sea más fácil desplegarlo junto con otras herramientas
  • Mi jefe llamó hace poco a Keycloak “el regalo que sigue dando”, pero en realidad quería decir que siguen apareciendo tickets de Jira nuevos para averiguar cómo hacer algo
    Aun así, tenemos Terraform que crea un clúster EKS y despliega Keycloak, crea clientes SAML/OIDC, agrega proveedores de identidad externos e incluso configura un AWS IAM Identity Provider
    Una vez que averiguas qué se puede hacer, cómo hacerlo en la UI y cómo automatizarlo con el provider de Terraform de mrparkers, usarlo en sí se vuelve muy fácil

    • Me pregunto si ese Terraform está publicado como open source en algún lado
      Un amigo lo necesita :)
  • Después de usar Keycloak durante varios años, la verdad es que me cansé de todo tipo de comportamientos raros y empecé a buscar alternativas
    Varias opciones como Authentik se veían bien, pero Zitadel[1] me llamó la atención, y desde entonces no miré atrás
    [1] https://zitadel.com/blog/zitadel-vs-keycloak

    • Qué bueno que te guste
      Me da curiosidad qué fue lo que te atrajo de forma decisiva