Cómo tener nuestro propio SSO+2FA con Authelia y Nginx Proxy Manager

⚠️
ACTUALIZACIÓN:
Este artículo está basado en la versión 2.9.19 de NginxProxyManager. Las versiones 2.10 y posteriores incluyen algunos Breaking Changes que afectan a cómo funciona la integración con Authelia, que aún no he podido revisar.

Si disponemos de un servidor privado (o VPS) donde tenemos varios servicios expuestos a Internet, a lo mejor queremos añadirle una capa extra de seguridad. O directamente añadir una, ya que algunos de estos servicios, al estar pensados para uso local, pueden carecer de log-in.

Authelia es un servicio que nos permite implementar eso de una forma realmente sencilla y rápida.

¿Qué es SSO?

No entraremos muy en detalle, puesto que es algo más complejo que 2 simples párrafos, pero las siglas (en inglés) significan "Single Sign On", lo que traducido podría ser algo así como "Registro Único". En el mundo empresarial es una terminología muy conocida, pero más allá de eso, y para entenderlo fácilmente, el ejemplo más fácil sería nuestra cuenta de Google: con ella podemos acceder a todos sus servicios como correo, calendario, drive... Es decir, tenemos una única cuenta para acceder a diferentes servicios.

Pero el SSO puede servir no sólo para productos de una misma empresa, sino que podemos usar una misma cuenta para acceder a diferentes servicios de diferentes empresas, como pasa generalmente para acceder a diferentes herramientas internas de una compañía (Jira/GitLab/Herramientas internas...). Todas ellas requieren de la cuenta corporativa para acceder.

Pero yo no tengo ninguna empresa, soy un simple friki

Vale, pero eso no quiere decir que no podamos añadir un extra de seguridad a nuestros servicios.

Como hemos comentado, a veces muchos de los servicios que queremos utilizar (la gran mayoría van a ser OpenSource) están pensados para uso local, y pueden carecer de log-in como tal. Otras veces simplemente queremos añadir ese extra de seguridad, ya que al exponer cosas a Internet toda seguridad es poca.

Por ejemplo, podemos tener nuestro gestor de finanzas, Plex, nuestro gestor de favoritos, nuestro propio buscador, nuestra aplicación de recetas... Aunque algunos puedan tener registro de usuarios, tal vez queremos limitar un poco más el acceso, pudiendo tener el log-in de Authelia + 2FA + log-in de aplicación.

Desplegar Authelia con Docker

💡
En esta entrada se va a usar la configuración más sencilla de Authelia, para gestionar pocos usuarios. No se mostrará la integración con LDAP u otras formas de gestión de usuarios que también están soportadas por Authelia.

La mejor forma de desplegar Authelia es usar Docker, como la inmensa mayoría de servicios actuales. Para empezar, lo que haremos es ir a nuestro proveedor de dominios a crear un subdominio destinado a nuestra plataforma de autenticación. Por ejemplo, lo podemos llamar authelia.midominio.com, o login.midominio.com, eso ya es elección de cada uno.

Ah, y acordarse de apuntarlo a la IP de nuestro servidor.

💡
Para poder usar el dominio, debemos configurarlo también en nuestro Nginx Reverse Proxy, como ya se explicó en un artículo anterior.

Luego procedemos a añadir a nuestro servidor la carpeta correspondiente a Authelia (como siempre donde más nos guste), y dentro debemos crear varias cosas.

Por una parte, el fichero docker-compose.yml

version: '3.3'

services:
  authelia:
    image: authelia/authelia
    container_name: authelia
    volumes:
      - ./config:/config
    ports:
     - "9091:9091"
    expose:
      - 9091
    restart: unless-stopped
    healthcheck:
      disable: true
    environment:
      - TZ=Europe/Madrid

Por otra parte, debemos crear una carpeta llamada config, y dentro debemos añadir los siguientes ficheros:

configuration.yml

Este fichero es la base de la configuración para Authelia, y más abajo se detallan los campos importantes.

###############################################################
#                   Authelia configuration                    #
###############################################################

jwt_secret: a_very_important_secret
default_redirection_url: https://public.example.com

server:
  host: 0.0.0.0
  port: 9091

log:
  level: debug
# This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE

totp:
  issuer: authelia.com

# duo_api:
#  hostname: api-123456789.example.com
#  integration_key: ABCDEF
#  # This secret can also be set using the env variables AUTHELIA_DUO_API_SECRET_KEY_FILE
#  secret_key: 1234567890abcdefghifjkl

authentication_backend:
  file:
    path: /config/users_database.yml

access_control:
  default_policy: deny
  rules:
    # Rules applied to everyone
    - domain: public.example.com
      policy: bypass
    - domain: subdomain1.example.com
      policy: one_factor
    - domain: subdomain2.example.com
      policy: two_factor

session:
  name: authelia_session
  # This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE
  secret: unsecure_session_secret
  expiration: 3600  # 1 hour
  inactivity: 300  # 5 minutes
  domain: example.com  # Should match whatever your root protected domain is

  # redis:
  #  host: redis
  #  port: 6379
    # This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD_FILE
    # password: authelia

regulation:
  max_retries: 3
  find_time: 120
  ban_time: 300

storage:
  encryption_key: you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this
  local:
    path: /config/db.sqlite3

notifier:
  smtp:
    username: test
    # This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE
    password: password
    host: mail.example.com
    port: 25
    sender: admin@example.com

En dicho fichero se deben modificar/añadir las siguientes cosas:

  • jwt_secret: debe ser un "token" autogenerado lo más complejo posible, de hasta 64 caracteres.
  • default_redirection_url: debemos añadir el dominio que hemos creado para nuestro servidor de Authelia
  • access_control: se deben añadir aquellos subdominios que se quieran gestionar con Authelia, dentro del bloque rules, con una de las siguientes políticas:
    • bypass: permite el tráfico abiertamente
    • one_factor: requiere de log-in con las credenciales de Authelia
    • two_factor: además de log-in con credenciales, se requiere un OTP
  • session: en esta sección se deben modificar 2 cosas:
    • secret: debe ser un secreto lo suficientemente complejo, de al menos 16 caracteres
    • domain: aquí debemos añadir el dominio base al que van a pertenecer todos los subdominios que vayamos a gestionar (midominio.com)
  • storage: aquí debemos modificar el siguiente parámetro:
    • encryption_key: como antes, debe ser una clave suficientemente segura
  • notifier: aquí debemos rellenar los campos con los datos de nuestro servidor de correo electrónico. Este punto es muy importante para poder activar el 2FA, y si no está bien configurado el servicio directamente no arrancará.

El resto de valores los podemos dejar por defecto, a menos que sepamos qué estamos haciendo.

users_database.yml

En este fichero es donde vamos a definir los usuarios que van a tener acceso a través de Authelia.

###############################################################
#                         Users Database                      #
###############################################################

# This file can be used if you do not have an LDAP set up.

# List of users
users:
  username1:
    displayname: "John Doe"
    password: "my hashed pass" # yamllint disable-line rule:line-length
    email: me@mydomain.com
    groups:
      - admins
      - dev

Lo importante aquí son varios puntos. Por una parte, dentro del bloque users debemos definir tantos usuarios como queramos siguiendo la estructura del ejemplo de arriba.

username1 será el nombre de usuario para acceder, y será el que inicia el bloque de configuración de dicho usuario.

Lo siguiente relevante es el password, que no debe ser en texto plano, sino que deberá estar encriptado. Para ello podemos usar un pequeño script que proporciona la propia imagen Docker de Authelia, de la siguiente manera:

docker run authelia/authelia:latest authelia hash-password -- 'password'

Que nos generará algo parecido a esto:
$argon2id$v=19$m=65536,t=3,p=4$KzRQdnFKVGxrcFNwRGNheA$Gf4geiNHilF5IE/X1oIyIOQA1ePbICrMbtXKY3Sji7g

Y será este string lo que debemos pegar en el campo password.

Por último, el correo debe ser válido, ya que para activar el 2FA se enviará un mail de activación. Los grupos se pueden dejar como están, porque es para configuraciones más avanzadas.

Y con esto ya estaríamos en disposición de arrancar nuestro servidor de Authelia con docker-compose up -d.

Probar Authelia

Lo mejor que podemos hacer para comprobar que todo funciona correctamente es añadir como regla el propio dominio que hayamos creado y asignarle una política de two_factor en el fichero de configuración de Authelia.

💡
Siempre que se modifique el fichero de configuración (para añadir nuevos dominios, por ejemplo) es necesario reiniciar el contenedor. Lo más fácil es `docker-compose restart`

Luego, accedemos al dominio en cuestión y deberíamos ver algo parecido a la siguiente imagen.

Al poner nuestro usuario y contraseña, la primera vez que accedemos nos pedirá activar 2FA, así que pulsamos sobre "Registrar Dispositivo":

Al hacerlo se nos enviará un mail para verificar la cuenta:

Al pulsar sobre "Register" se abrirá el navegador mostrando algo parecido a esto.

Lo que deberemos hacer es usar la app de 2FA que más nos guste (en mi caso uso Aegis y Bitwarden, por lo de que son OpenSource y esas cosas) para hacer una foto al QR. Alternativamente, podemos copiar y pegar el secreto que aparece abajo.

Tras eso, nos pedirá el código de verificación que se habrá generado con la aplicación para comprobar que todo funciona correctamente:

Y si todo ha ido bien, ya tendremos Authelia configurado.

Preparar Nginx Proxy Manager

Una vez tenemos preparado el servicio de Authelia, debemos añadir cierta configuración al Reverse Proxy. Si hemos configurado Nginx Proxy Manager siguiendo este artículo, será necesario realizar algunas modificaciones. Si lo habéis desplegado de otra forma, bastará adaptar lo siguiente a vuestra configuración.

Cómo configurar Nginx Proxy Manager
En otra ocasión hemos explicado cómo configurar Nginx para usarlo como Reverse Proxy, y nos puede ser suficiente para gestionar pocos servicios. Pero a la que empecemos a tener más y más aplicaciones que gestionar puede ser algo tedioso, sobre todo en lo que al control de certificados y posibles

Lo primero que haremos es crear una carpeta llamada snippets dentro de la carpeta raíz donde tengamos la configuración de Nginx Proxy Manager (donde esté el docker-compose.yml). Dentro de esta carpeta snippets creamos 2 ficheros:

authelia-location.conf

## Virtual endpoint created by nginx to forward auth requests.
location /authelia {
    ## Essential Proxy Configuration
    internal;
    proxy_pass https://authelia.mydomain.com/api/verify;

    ## Headers
    ## The headers starting with X-* are required.
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    proxy_set_header X-Forwarded-Method $request_method;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-Uri $request_uri;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Content-Length "";
    proxy_set_header Connection "";

    ## Basic Proxy Configuration
    proxy_pass_request_body off;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead
    proxy_redirect http:// $scheme://;
    proxy_http_version 1.1;
    proxy_cache_bypass $cookie_session;
    proxy_no_cache $cookie_session;
    proxy_buffers 4 32k;
    client_body_buffer_size 128k;

    ## Advanced Proxy Configuration
    send_timeout 5m;
    proxy_read_timeout 240;
    proxy_send_timeout 240;
    proxy_connect_timeout 240;
}

Aquí debemos cambiar la variable proxy_pass por nuestro dominio de Authelia, dejando "/api/verify" al final.

authelia-authrequest.conf

## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
auth_request /authelia;

## Set the $target_url variable based on the original request.

## Comment this line if you're using nginx without the http_set_misc module.
#set_escape_uri $target_url $scheme://$http_host$request_uri;

## Uncomment this line if you're using NGINX without the http_set_misc module.
set $target_url $scheme://$http_host$request_uri;

## Save the upstream response headers from Authelia to variables.
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;

## Inject the response headers from the variables into the request made to the backend.
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email;

## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal.
error_page 401 =302 https://authelia.mydomain.com/?rd=$target_url;

Aquí debemos modificar la variable error_page 401 por nuestro dominio de Authelia, dejando "/?rd=$target_url" al final.

Lo que nos quedaría es añadir la siguiente línea al docker-compose.yml que ya tenemos de Nginx Proxy Manager, en la sección de volumes, para que cargue los nuevos ficheros:

- ./snippets/:/etc/nginx/snippets

Añadir la configuración a los subdominios de Nginx Proxy Manager

Lo último que nos falta por hacer es incluir estos ficheros en los subdominios que nos interese limitar. Para ello accedemos a las opciones del subdominio en cuestión, dentro de la configuración de Nginx Proxy Manager, y añadimos las siguientes líneas en la sección "Custom Nginx Configuration", dentro de la pestaña "Advanced":

include /etc/nginx/snippets/authelia-location.conf;
include /etc/nginx/snippets/authelia-authrequest.conf;

Quedando de la siguiente manera:

💡
No olvidarse de añadir todos los subdominios que queremos proteger a la configuración de Authelia, incluyendo la política que queremos aplicar, en la sección access_control del fichero "configuration.yml".

Y listo, ya tendríamos el subdominio con una capa extra de seguridad. Para validarlo, abrimos un navegador en modo incógnito y accedemos al dominio que acabamos de configurar.

Conclusiones

Puede que esta haya sido una entrada algo larga y técnica, pero si todo ha salido a pedir de Milhouse, ya tendremos nuestro propio servicio SSO+2FA listo para poner detrás cualquier aplicación que queramos, pudiendo acceder a ella con un único usuario, lo cual es bastante interesante.

Espero que os haya sido útil, y si por lo que fuera no os acaba de funcionar o se ha colado algo, me lo hacéis saber y le echamos un vistazo.

Fuente: Authelia