El problema no es evidente hasta que tienes diez servicios corriendo. Tienes Gitea con sus propios usuarios, Portainer con sus propios credenciales, Grafana con otro sistema, Dokploy con otro más. Cada vez que alguien nuevo entra al lab necesita que le crees cuentas en cinco sitios distintos. Y cuando alguien sale, tienes que acordarte de eliminarla en todos ellos.
Esto escala fatal. Y la solución existe desde hace décadas en el mundo corporativo: SSO, Single Sign-On. Un único punto de autenticación al que se conectan todos los servicios. Entras una vez, tienes acceso a todo lo que se te ha dado permiso.
En el mundo self-hosted, la opción más completa que he probado es Authentik.
Qué es Authentik y por qué no Authelia#
Cuando buscas SSO para homelab aparecen dos nombres principales: Authentik y Authelia. Son proyectos distintos con filosofías distintas.
Authelia es un proxy de autenticación. Intercepta peticiones antes de que lleguen al servicio y decide si el usuario puede pasar. Es ligero, rápido, y funciona muy bien para servicios que no tienen soporte nativo de OAuth2. La pega es que no es un Identity Provider completo: no puedes usarlo para hacer login en Gitea con tu cuenta Authelia, por ejemplo.
Authentik es un Identity Provider completo. Implementa OAuth2, OpenID Connect, SAML, LDAP y Radius. Esto significa que los servicios que soporten cualquiera de estos protocolos pueden delegar la autenticación directamente a Authentik. El resultado es un login nativo en el servicio, no un portal externo que intercepta la petición.
La diferencia en la práctica: con Authelia ves un portal de login antes de llegar al servicio. Con Authentik, el botón de “Login with Authentik” aparece dentro del propio servicio y te redirige al flujo de autenticación. Mucho más limpio.
El precio que pagas es recursos. Authentik no es ligero. Necesita PostgreSQL, Redis, y los propios contenedores del servidor y worker. En Phatt, con 512GB de RAM, esto no es ningún problema. En una Raspberry Pi o un ZimaBoard 2 con 16GB habría que pensárselo más.
Instalación con Docker Compose en Phatt#
El stack de Authentik tiene cuatro componentes: la base de datos PostgreSQL, Redis para caché y colas, el servidor (que sirve la UI y los endpoints de autenticación) y el worker (que procesa tareas en segundo plano).
El docker-compose oficial de Authentik está bien documentado, pero hay cosas que ajusté para mi setup. Este es el que uso en Phatt:
| |
El .env que acompaña al compose:
| |
La AUTHENTIK_SECRET_KEY es crítica. Si la pierdes, pierdes el acceso a todos los datos cifrados. La guardo en Vaultwarden, que tengo corriendo en el mismo Phatt.
Para generarla:
| |
Primera configuración: el asistente inicial#
Con el stack corriendo, accedes a http://phatt:9000/if/flow/initial-setup/ la primera vez. Authentik te pide que crees el usuario admin y estableces contraseña. Después de esto ya tienes acceso al panel de administración.
Lo primero que hago siempre es ir a Admin > System > Certificates y revisar que Authentik ha generado su certificado autofirmado. Esto es necesario para los flujos SAML y para firmar tokens JWT.
Después, Admin > System > Email para configurar el servidor de correo. Lo conecto a mi instancia de Maddy (relay SMTP interno) para que las invitaciones y recuperaciones de contraseña funcionen.
Integrar Traefik como Forward Auth#
El caso de uso más sencillo, y el que configuro primero, es usar Authentik como proxy de autenticación delante de servicios que no tienen soporte OAuth2 nativo. Funciona igual que Authelia en este caso: Traefik intercepta la petición y consulta a Authentik si el usuario está autenticado.
En Authentik creas un Outpost de tipo “Proxy Provider”. Un Outpost es un proceso que Authentik despliega (o que despliegas tú) que habla con el servidor central y actúa como middleware de autenticación.
El proceso en el panel de Authentik:
- Admin > Applications > Providers > Create, elige “Proxy Provider”
- Nombre: algo descriptivo como “Traefik Forward Auth”
- Authorization flow: el flujo por defecto que viene preconfigurado funciona
- Mode: “Forward auth (single application)” si quieres proteger un dominio específico, o “Forward auth (domain level)” para un wildcard
- External host: el dominio que quieres proteger
Después creas la Application asociada al Provider, y luego el Outpost vinculado a esa Application.
En el docker-compose de Traefik añades las labels necesarias en el servicio que quieres proteger:
| |
Con esto, cualquier acceso al servicio pasa primero por Authentik. Si no estás autenticado, te redirige al portal de login. Una vez autenticado, Authentik inyecta cabeceras HTTP con tu información de usuario que el servicio puede usar.
Tengo esto configurado para servicios que no tienen OAuth2 propio, como algunos dashboards internos y herramientas de administración.
OAuth2/OIDC: el login nativo#
El caso de uso más potente es OAuth2 y OpenID Connect. Los servicios que lo soportan pueden mostrar un botón “Login with Authentik” directamente en su interfaz, sin necesidad de ningún proxy intermedio.
Gitea#
Gitea tiene soporte OIDC nativo. La configuración es directa.
Primero, en Authentik:
- Admin > Applications > Providers > Create, elige “OAuth2/OpenID Provider”
- Nombre: “Gitea”
- Client type: Confidential
- Redirect URIs:
https://gitea.tudominio.com/user/oauth2/authentik/callback - Guarda el Client ID y Client Secret que te genera
En Gitea, como admin:
- Site Administration > Integrations > OAuth Applications > Add OAuth Application… pero en realidad es mejor usar la autenticación de terceros desde Site Administration > Identity & Access > Authentication Sources > Add Authentication Source
- Type: OAuth2
- OAuth2 Provider: OpenID Connect
- Client ID y Client Secret: los de Authentik
- OpenID Connect Auto Discovery URL:
https://authentik.tudominio.com/application/o/gitea/.well-known/openid-configuration
Después de guardar, aparece un botón “Sign in with Authentik” en la página de login de Gitea. Los usuarios que ya tenían cuenta en Gitea pueden vincularla con su cuenta de Authentik desde su perfil.
Grafana#
Grafana tiene soporte OAuth2 desde hace años. En el grafana.ini o en las variables de entorno del contenedor:
| |
Lo último es la parte interesante: role_attribute_path permite asignar roles de Grafana basándose en los grupos de Authentik. Si el usuario es miembro del grupo grafana-admins en Authentik, entra como Admin en Grafana. Si no, como Viewer.
Esto significa que la gestión de permisos de Grafana la hago desde Authentik, no desde dentro de Grafana. Un único punto de control.
Gestión de usuarios y grupos#
Una vez tienes los servicios conectados, la gestión es desde el panel de Authentik. Creas usuarios, los asignas a grupos, y esos grupos determinan qué pueden hacer en cada servicio.
En mi caso tengo tres grupos principales:
- homelab-admins: acceso total a todo. Solo yo.
- homelab-users: acceso a servicios de consumo (Gitea en modo lectura, dashboards, Immich).
- homelab-media: acceso solo a servicios de media (Plex, Navidrome cuando lo tenga).
Cuando alguien nuevo necesita acceso al lab, creo un usuario en Authentik, lo añado al grupo correspondiente, y ya tiene acceso a todos los servicios configurados con ese grupo. Sin crear cuentas individuales en cada servicio.
MFA: el extra que no cuesta nada#
Authentik tiene soporte de MFA integrado. Puedes forzar que todos los usuarios usen un segundo factor, o hacerlo opcional. Los métodos disponibles son TOTP (apps como Authy o Google Authenticator), WebAuthn (YubiKey, passkeys), y SMS si configuras un proveedor.
Para activarlo, editas el flujo de autenticación por defecto y añades el stage de MFA. Authentik te permite crear flujos personalizados con su editor visual, lo cual es potente pero tiene curva de aprendizaje.
La forma rápida: Admin > Flows & Stages > Stages > Create > Authenticator Validation Stage. Lo vinculas al flujo de autenticación existente y ya pide MFA a todos los usuarios que lo tengan configurado.
Los usuarios que aún no tienen MFA configurado se redirigen automáticamente al flujo de enrollment la primera vez que hacen login. Authentik lo gestiona solo.
Cuánto consume en Phatt#
En Phatt, el stack completo de Authentik consume de manera estable:
- CPU: prácticamente 0% en idle, picos de 2-3% durante logins
- RAM: PostgreSQL unos 80MB, Redis 15MB, el servidor 350MB, el worker 280MB. Total aproximado 730MB
- Disco: la base de datos crece lentamente, unos 50MB tras meses de uso
No es el servicio más ligero del lab, pero tampoco es un problema para Phatt. Si tuvieras que meterlo en un ZimaBoard 2 con 16GB de RAM junto a otros servicios, habría que priorizar.
Lo que cambió después de montarlo#
El cambio más notable no fue técnico sino operativo. Antes, si quería que alguien accediera temporalmente a algún servicio del lab, tenía que crear cuentas en varios sitios y luego acordarme de eliminarlas. Con Authentik, creo un usuario, lo asigno a un grupo, y cuando ya no necesita acceso lo desactivo o elimino desde un único panel. Todo el acceso se corta en segundos.
Lo segundo más valioso: el log de accesos. En Admin > Events > Logs tienes un registro de todos los logins, intentos fallidos, y acciones administrativas. Cuando hay algo raro, puedes ver exactamente qué pasó y desde qué IP.
El tercer punto: MFA para todos los servicios a la vez. Antes, si quería MFA en Gitea, configuraba MFA en Gitea. En Grafana, en Grafana. Ahora activo MFA en Authentik y automáticamente todos los servicios conectados lo requieren.
Conclusión#
Authentik tiene una curva de aprendizaje real. La UI es potente pero no siempre intuitiva, y conceptos como Outposts, Providers y Flows requieren un rato de lectura para entender cómo encajan. Pero una vez que lo tienes montado y los primeros servicios conectados, la lógica tiene sentido.
Para un homelab con más de cinco o seis servicios self-hosted que quieras exponer a más de una persona, Authentik resuelve un problema real de gestión. Especialmente si ya tienes Traefik como reverse proxy y servicios como Gitea o Grafana que tienen soporte OAuth2 nativo.
El tiempo de montarlo inicial son unas dos o tres horas si no has tocado OAuth2 antes. El tiempo que te ahorra después es indefinido.