Authelia es un servidor de autenticación open source que se coloca delante de tus aplicaciones web y exige inicio de sesión, con segundo factor opcional (TOTP o WebAuthn), antes de dejar pasar cada petición. Trae su propio portal de login, una base de usuarios en archivo o LDAP y reglas de acceso por dominio, ruta, grupo o red. En este tutorial lo desplegarás con Docker Compose en Ubuntu 24.04, lo integrarás con Nginx mediante auth_request y protegerás una aplicación con usuario, contraseña y código TOTP.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios
sudo. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
- Nginx y Certbot instalados:
sudo apt install nginx certbot python3-certbot-nginx, y los puertos 80 y 443 abiertos consudo ufw allow 'Nginx Full'. - Un dominio propio con dos registros DNS A apuntando al servidor:
auth.your_domainpara el portal de Authelia yapp.your_domainpara la aplicación que vas a proteger. - Una aplicación escuchando en
127.0.0.1:3000. Para probar sirvepython3 -m http.server 3000 --bind 127.0.0.1. - Una app de autenticación en el móvil (Aegis, Google Authenticator, 1Password o similar).
Paso 1: Preparar los directorios y los secretos
Authelia guarda su configuración en config/ y la base de datos SQLite (dispositivos 2FA, registros de autenticación) en data/. Crea la estructura en /opt/authelia:
sudo mkdir -p /opt/authelia/config /opt/authelia/data
cd /opt/authelia
Authelia necesita tres secretos aleatorios: uno para firmar los enlaces de restablecimiento de contraseña, otro para cifrar la sesión y otro para cifrar la base de datos. Genera tres valores distintos y guárdalos para el paso 3:
for name in JWT SESSION STORAGE; do echo "$name: $(openssl rand -hex 64)"; done
JWT: 3f9c1e...
SESSION: a81b0d...
STORAGE: 5e27f4...
Importantela clave
STORAGEcifra los secretos TOTP en la base de datos. Si la pierdes o la cambias, todos los usuarios tendrán que volver a registrar su segundo factor.
Paso 2: Crear el archivo de Docker Compose
El despliegue tiene dos contenedores: Authelia y Redis, que guarda las sesiones para que no se pierdan al reiniciar Authelia. Crea el archivo:
sudo nano /opt/authelia/compose.yaml
services:
authelia:
image: authelia/authelia:4.39
container_name: authelia
restart: unless-stopped
depends_on:
- redis
volumes:
- ./config:/config
- ./data:/data
ports:
- "127.0.0.1:9091:9091"
environment:
TZ: Europe/Madrid
redis:
image: redis:7-alpine
container_name: authelia-redis
restart: unless-stopped
volumes:
- redis-data:/data
volumes:
redis-data:
El puerto se publica solo en 127.0.0.1. Docker añade sus propias reglas de iptables y se salta UFW, así que publicarlo en 0.0.0.0 dejaría Authelia accesible desde Internet sin pasar por Nginx. Redis no publica ningún puerto: Authelia lo alcanza por la red interna de Compose con el nombre redis.
Paso 3: Configurar Authelia
Crea el archivo de configuración principal:
sudo nano /opt/authelia/config/configuration.yml
Pega el siguiente contenido. Sustituye your_domain por tu dominio y los tres secretos por los valores que generaste en el paso 1:
server:
address: 'tcp://:9091/'
log:
level: 'info'
theme: 'auto'
identity_validation:
reset_password:
jwt_secret: 'your_jwt_secret'
totp:
issuer: 'your_domain'
authentication_backend:
file:
path: '/config/users_database.yml'
password:
algorithm: 'argon2'
access_control:
default_policy: 'deny'
rules:
- domain: 'app.your_domain'
policy: 'two_factor'
session:
secret: 'your_session_secret'
expiration: '1h'
inactivity: '15m'
cookies:
- domain: 'your_domain'
authelia_url: 'https://auth.your_domain'
default_redirection_url: 'https://app.your_domain'
redis:
host: 'redis'
port: 6379
regulation:
max_retries: 5
find_time: '2m'
ban_time: '10m'
storage:
encryption_key: 'your_storage_secret'
local:
path: '/data/db.sqlite3'
notifier:
filesystem:
filename: '/data/notifications.txt'
Los puntos clave de esta configuración:
access_controldeniega todo por defecto y exige dos factores enapp.your_domain. El dominio del portal (auth.your_domain) no debe aparecer en las reglas.session.cookiesdefine para qué dominio se emite la cookie y dónde está el portal. Authelia usaauthelia_urlpara construir la redirección al login.regulationbloquea temporalmente a un usuario tras 5 intentos fallidos en 2 minutos.notifier.filesystemescribe en un archivo los correos que Authelia enviaría (códigos de verificación, restablecimiento de contraseña). Sirve para empezar; en producción sustitúyelo por un bloquesmtp.
El archivo contiene secretos, así que limita su lectura a root:
sudo chmod 600 /opt/authelia/config/configuration.yml
Paso 4: Crear el primer usuario
Authelia no guarda contraseñas en claro, sino un hash Argon2id. Genéralo con el propio binario de Authelia, sustituyendo your_strong_password por una contraseña real:
sudo docker run --rm authelia/authelia:4.39 authelia crypto hash generate argon2 --password 'your_strong_password'
Digest: $argon2id$v=19$m=65536,t=3,p=4$Zm9vYmFyYmF6$2cN8...
Crea la base de usuarios:
sudo nano /opt/authelia/config/users_database.yml
Pega el bloque siguiente y sustituye el valor de password por el digest completo que obtuviste (empieza por $argon2id$):
users:
your_user:
disabled: false
displayname: 'Your Name'
password: '$argon2id$v=19$m=65536,t=3,p=4$Zm9vYmFyYmF6$2cN8...'
email: 'you@your_domain'
groups:
- 'admins'
Protege el archivo y arranca los contenedores:
sudo chmod 600 /opt/authelia/config/users_database.yml
sudo docker compose up -d
Comprueba que Authelia arrancó sin errores y responde en su endpoint de salud:
sudo docker compose logs authelia --tail 20
curl -s http://127.0.0.1:9091/api/health
{"status":"OK"}
Si el contenedor se reinicia en bucle, los registros indican la clave de configuración que falla. Los errores más habituales son un secreto vacío o una sangría incorrecta en YAML.
Paso 5: Publicar el portal de Authelia en Nginx
Crea un fragmento reutilizable con las cabeceras de proxy que Authelia necesita para saber el esquema, el host y la IP original:
sudo nano /etc/nginx/snippets/authelia-proxy.conf
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_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-URI $request_uri;
proxy_http_version 1.1;
proxy_set_header Connection "";
Crea el sitio del portal:
sudo nano /etc/nginx/sites-available/auth.your_domain
server {
listen 80;
server_name auth.your_domain;
location / {
include /etc/nginx/snippets/authelia-proxy.conf;
proxy_pass http://127.0.0.1:9091;
}
}
Paso 6: Proteger la aplicación con auth_request
Nginx preguntará a Authelia por cada petición a la aplicación a través del endpoint /api/authz/auth-request. Crea un fragmento con la ubicación interna que hace esa consulta:
sudo nano /etc/nginx/snippets/authelia-location.conf
location = /internal/authelia/authz {
internal;
proxy_pass http://127.0.0.1:9091/api/authz/auth-request;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Content-Length "";
proxy_set_header Connection "";
proxy_pass_request_body off;
proxy_http_version 1.1;
}
Y otro fragmento que aplica la comprobación dentro de cada location protegida:
sudo nano /etc/nginx/snippets/authelia-authrequest.conf
auth_request /internal/authelia/authz;
# Identidad del usuario, para pasarla a la aplicación
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email;
# Sin sesión válida, Authelia devuelve la URL del portal en Location
auth_request_set $redirection_url $upstream_http_location;
error_page 401 =302 $redirection_url;
Crea el sitio de la aplicación:
sudo nano /etc/nginx/sites-available/app.your_domain
server {
listen 80;
server_name app.your_domain;
include /etc/nginx/snippets/authelia-location.conf;
location / {
include /etc/nginx/snippets/authelia-authrequest.conf;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Activa los dos sitios y valida la configuración:
sudo ln -s /etc/nginx/sites-available/auth.your_domain /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/app.your_domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Authelia solo emite cookies de sesión sobre HTTPS, así que obtén los certificados de Let's Encrypt para los dos subdominios. Certbot añade a cada bloque server la configuración TLS y la redirección desde HTTP:
sudo certbot --nginx -d auth.your_domain -d app.your_domain
Comprueba que una petición sin sesión se redirige al portal:
curl -sI https://app.your_domain/ | grep -iE '^(HTTP|location)'
HTTP/1.1 302 Moved Temporarily
Location: https://auth.your_domain/?rd=https%3A%2F%2Fapp.your_domain%2F&rm=GET
Paso 7: Iniciar sesión y registrar el segundo factor
Abre https://app.your_domain en el navegador. Authelia te lleva al portal:
- Inicia sesión con
your_usery la contraseña del paso 4. - Como la regla exige
two_factor, Authelia te pide un método de segundo factor. Elige Contraseña de un solo uso (TOTP) y registra un nuevo dispositivo. - Antes de mostrar el QR, Authelia verifica tu identidad enviando un código de un solo uso por correo. Con el notificador de archivo, el mensaje está en el servidor:
sudo cat /opt/authelia/data/notifications.txt
- Introduce ese código en el portal, escanea el QR con tu app de autenticación y confirma con el código de 6 dígitos que genera.
- Authelia te redirige a
https://app.your_domainy ves la aplicación.
En los siguientes inicios de sesión bastará con la contraseña y el código TOTP. Desde el mismo portal puedes registrar también una llave de seguridad o passkey (WebAuthn) como método alternativo.
Paso 8: Afinar las reglas de acceso
Las reglas se evalúan en orden y se aplica la primera que coincide, así que coloca las más específicas arriba. Este ejemplo deja pública una ruta de salud, exige el grupo admins para /admin y permite el resto de la aplicación con un solo factor desde la red de la oficina:
access_control:
default_policy: 'deny'
networks:
- name: 'oficina'
networks:
- '203.0.113.0/24'
rules:
- domain: 'app.your_domain'
resources:
- '^/health$'
policy: 'bypass'
- domain: 'app.your_domain'
resources:
- '^/admin([/?].*)?$'
subject:
- 'group:admins'
policy: 'two_factor'
- domain: 'app.your_domain'
networks:
- 'oficina'
policy: 'one_factor'
- domain: 'app.your_domain'
policy: 'two_factor'
Sustituye el bloque access_control de configuration.yml y reinicia Authelia para aplicar los cambios:
cd /opt/authelia
sudo docker compose restart authelia
sudo docker compose logs authelia --tail 20
Solución de problemas
Los códigos TOTP siempre son incorrectos. Casi siempre es la hora del servidor o del móvil. Comprueba que el servidor está sincronizado:
timedatectl status
La línea System clock synchronized: yes debe aparecer. Si no, activa NTP con sudo timedatectl set-ntp true.
Tras iniciar sesión vuelves al portal en bucle. La cookie no se guarda. Revisa que session.cookies.domain es el dominio padre de app.your_domain y auth.your_domain, y que accedes por HTTPS.
Nginx devuelve 500 en lugar de redirigir. Authelia no está disponible o la ubicación interna no llega a 127.0.0.1:9091. Comprueba curl http://127.0.0.1:9091/api/health y sudo tail /var/log/nginx/error.log.
El usuario queda bloqueado. Es la protección de regulation. Espera el tiempo de ban_time o revisa los intentos en sudo docker compose logs authelia.
Conclusión
Has desplegado Authelia con Docker Compose, lo has publicado detrás de Nginx con TLS y has protegido una aplicación que ahora exige contraseña y código TOTP, con reglas de acceso por ruta, grupo y red. Como siguientes pasos, configura el notificador smtp para que los códigos lleguen por correo, añade más aplicaciones reutilizando los dos fragmentos de Nginx y, si ya tienes un directorio corporativo, cambia el backend de archivo por LDAP.
