6

Middlewares de autenticación

Vistas: 2
Middlewares de autenticación

En el capítulo anterior viste los middlewares básicos, rate limiting, cabeceras, compresión, etc. Ahora toca hablar de los que realmente controlan quién puede acceder a tus servicios. Porque, seamos sinceros, no quieres que cualquiera entre en según qué servicios, o mejor dicho, solo quieres que a tus servicios entren aquellos que tu quieres. Sobre todo si piensas exponer tus servicios directamente a internet. Los middlewares de autenticación te permiten proteger tus servicios sin tener que modificar el código de los mismos. Sin tener que programar nada. Y esto es precisamente lo que vamos a tratar en este nuevo capítulo del tutorial de Traefik.

BasicAuth

El más simple de todos. Añade autenticación HTTP básica (usuario y contraseña) a un servicio. Es sencillo, funciona en todas partes, y para según qué casos es más que suficiente.

Pero, ¿cómo se configura exactamente? Vamos paso a paso.

Generar usuarios con htpasswd

Para definirlo necesitas un archivo con los usuarios y sus contraseñas hasheadas. Puedes generarlo con htpasswd,

htpasswd -nb usuario contraseña

Esto te devolverá algo como,

usuario:$apr1$xxxxxx$xxxxxxxxxxxxxxx

Ojo aquí, y esto es importante. Por defecto htpasswd usa el hash APR1 (Apache MD5). No está mal, pero te recomiendo usar bcrypt si puedes. Es más seguro y más resistente a ataques de fuerza bruta. Lo haces así,

htpasswd -nB usuario contraseña

La opción -B fuerza el uso de bcrypt. Te devolverá algo como,

usuario:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Si prefieres SHA-512, también puedes con -d y usando mkpasswd o openssl, aunque no lo he probado tanto en producción.

Diferencias entre los formatos de hash

Vale la pena que entiendas qué genera cada opción de htpasswd porque según el sistema operativo puede variar,

  • APR1 (MD5): opción -m o por defecto, formato $apr1$..., seguridad media.
  • bcrypt: opción -B, formato $2y$... o $2b$..., seguridad alta.
  • SHA-1: opción -s, formato {SHA}..., seguridad baja (no lo uses).
  • Plaintext: opción -p, texto plano, seguridad ninguna.

Mi recomendación: usa siempre -B para bcrypt. APR1 vale si tienes un sistema legacy, pero para proyectos nuevos, bcrypt es lo que toca. SHA-1 y plaintext mejor ni los menciones.

Generar usuarios sin htpasswd

Si no tienes htpasswd instalado, puedes generar los hashes con otras herramientas. Por ejemplo con Python,

python3 -c "import crypt; print(crypt.crypt('tu-contraseña', crypt.mksalt(crypt.METHOD_BLOWFISH)))"

O con openssl,

openssl passwd -6 'tu-contraseña'

La opción -6 de openssl genera un hash SHA-512, que también es aceptable aunque yo me quedo con bcrypt.

Configurar el middleware

Con el hash generado, defines el middleware,

labels:
  - "traefik.http.middlewares.auth-basico.basicauth.users=usuario:$$apr1$$xxxxxx$$xxxxxxxxxxxxxxx"

Fíjate en los dobles $$. En los labels de Docker Compose, el $ es un carácter especial, así que tienes que escaparlo. Si lo hicieras en un archivo YAML del File provider, sería con un solo $.

Este es uno de los errores más comunes que te puedes encontrar: copias el hash tal cual de htpasswd y te quedas con un solo $, y entonces Traefik no reconoce la contraseña. Siempre dobles $$ en Docker Compose.

Puedes tener varios usuarios separados por comas,

  - "traefik.http.middlewares.auth-basico.basicauth.users=usuario1:$$apr1$$xxx...,usuario2:$$apr1$$yyy..."

O, mejor aún, cargar los usuarios desde un archivo externo. Esto es más limpio y te permite añadir o quitar usuarios sin tocar el docker-compose.yml,

  - "traefik.http.middlewares.auth-basico.basicauth.usersfile=/etc/traefik/users.txt"

El archivo users.txt tendría el formato de htpasswd, una línea por usuario.

lorenzo:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
admin:$2y$05$yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
invitado:$2y$05$zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

Para usarlo en un router,

  - "traefik.http.routers.mi-app.middlewares=auth-basico"

Opciones adicionales de BasicAuth

BasicAuth tiene algunas opciones extra que te pueden interesar,

  - "traefik.http.middlewares.auth-basico.basicauth.realm=MiRealm"
  - "traefik.http.middlewares.auth-basico.basicauth.headerField=X-Webauth-User"
  - "traefik.http.middlewares.auth-basico.basicauth.removeHeader=true"
  • realm: el texto que ve el usuario en el popup de autenticación del navegador. Ponle algo descriptivo tipo «Acceso restringido – Panel de administración».
  • headerField: cabecera donde se inyecta el usuario autenticado. Muy útil si tu servicio necesita saber qué usuario ha entrado.
  • removeHeader: si lo pones a true, Traefik no envía la cabecera Authorization al servicio destino. Esto te puede interesar si no quieres que el servicio final vea las credenciales.

¿Cuándo usar BasicAuth?

  • Para proteger servicios internos o de administración.
  • Para dar acceso temporal a alguien.
  • Cuando quieres algo simple que funcione sin complicaciones.
  • Para paneles como Portainer, Grafana o phpMyAdmin que ya tienen su propio login pero quieres una capa extra.

¿Cuándo NO usarlo?

  • Para servicios con muchos usuarios.
  • Cuando necesitas integración con proveedores de identidad (Google, GitHub, etc.).
  • Cuando necesitas gestionar sesiones, roles o permisos granularmente.
  • NUNCA sin HTTPS. Si usas BasicAuth sin TLS, la contraseña viaja en base64, que se descodifica en medio segundo. No es cifrado, es codificación. O sea, es como enviar la contraseña en texto plano. Esto te puede ahorrar más de un disgusto: siempre HTTPS.

Errores comunes con BasicAuth

  1. Olvidar los dobles $$ en Docker Compose: el error más típico. Si ves que las credenciales no funcionan, revisa que los $ estén duplicados.
  2. Hash incorrecto: si generas el hash en macOS o Windows, el formato puede variar ligeramente. Te recomiendo generarlo directamente en el servidor Linux donde vas a ejecutar Traefik.
  3. Archivo users.txt con permisos incorrectos: si montas el archivo con :ro (solo lectura), todo bien. Pero asegúrate de que el usuario de Traefik (normalmente el uid 1000 o el que tenga el contenedor) pueda leerlo.
  4. Characteres especiales en la contraseña: si tu contraseña tiene $, :, o \, puede que tengas que escaparlos también. Para evitarte dolores de cabeza, usa contraseñas alfanuméricas o directamente usa una herramienta como apg para generarlas.
  5. Mezclar APR1 y bcrypt: si tienes usuarios generados con distintos algoritmos, Traefik los soporta sin problema. Pero asegúrate de que cada línea del archivo tiene un formato consistente.

DigestAuth

Similar a BasicAuth pero más seguro. El navegador no envía la contraseña en texto plano, sino un hash. Sin embargo, se usa menos porque no todos los clientes lo soportan igual de bien,

labels:
  - "traefik.http.middlewares.auth-digesto.digestauth.users=usuario:realm:hash"

El hash se genera con herramientas específicas de Digest. Por ejemplo,

# Con htdigest (si lo tienes instalado)
htdigest -c users.digest "MiRealm" usuario

O puedes generarlo manualmente si sabes lo que haces. El formato es usuario:realm:MD5(usuario:realm:contraseña).

DigestAuth también soporta opciones como removeHeader,

  - "traefik.http.middlewares.auth-digesto.digestauth.removeHeader=true"

Si te soy sincero, cada vez se usa menos. ¿Por qué? Porque la mayoría de clientes HTTP modernos soportan BasicAuth perfectamente, y si necesitas algo más seguro, directamente pasas a ForwardAuth. DigestAuth queda en tierra de nadie: es más complejo que BasicAuth pero menos potente que ForwardAuth. Te recomiendo BasicAuth para cosas simples y ForwardAuth para cosas más serias.

ForwardAuth (el rey)

Este es el middleware de autenticación más potente de Traefik. En lugar de gestionar la autenticación él mismo, delega la decisión a un servicio externo que tú controlas.

¿Cómo funciona? Cuando llega una petición:

  1. Traefik envía la petición a un servicio de autenticación externo.
  2. Si el servicio responde con un 200 OK, la petición sigue su curso.
  3. Si responde con un 401 o 403, Traefik deniega el acceso.

Esto te permite integrar con cualquier sistema de autenticación: Authelia, Authentik, OAuth2 Proxy, Keycloak, tu propio servicio, etc.

La configuración básica,

labels:
  - "traefik.http.middlewares.auth-forward.forwardauth.address=http://auth:4180"
  - "traefik.http.middlewares.auth-forward.forwardauth.trustForwardHeader=true"
  • address: la URL del servicio de autenticación.
  • trustForwardHeader: confía en las cabeceras de forwarding (IP real del cliente, etc.).

Puedes configurar más opciones,

  - "traefik.http.middlewares.auth-forward.forwardauth.address=http://auth:4180"
  - "traefik.http.middlewares.auth-forward.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-forward.forwardauth.authResponseHeaders=X-Auth-User, X-Auth-Email"
  - "traefik.http.middlewares.auth-forward.forwardauth.authRequestHeaders=X-Forwarded-Proto"
  • authResponseHeaders: cabeceras de la respuesta de autenticación que se pasan al servicio final. Muy útil para pasar el nombre de usuario o email al servicio destino.
  • authRequestHeaders: cabeceras de la petición original que se pasan al servicio de autenticación.

Opciones avanzadas de ForwardAuth

ForwardAuth tiene un par de opciones más que merece la pena conocer,

  - "traefik.http.middlewares.auth-forward.forwardauth.address=http://auth:4180"
  - "traefik.http.middlewares.auth-forward.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-forward.forwardauth.authResponseHeaders=X-Auth-User, X-Auth-Email, X-Auth-Token"
  - "traefik.http.middlewares.auth-forward.forwardauth.authRequestHeaders=Accept, Accept-Language, Cookie, X-Forwarded-Proto, X-Forwarded-Method, X-Forwarded-Uri"
  - "traefik.http.middlewares.auth-forward.forwardauth.tls.ca=/etc/traefik/certs/ca.crt"
  - "traefik.http.middlewares.auth-forward.forwardauth.tls.cert=/etc/traefik/certs/client.crt"
  - "traefik.http.middlewares.auth-forward.forwardauth.tls.key=/etc/traefik/certs/client.key"
  - "traefik.http.middlewares.auth-forward.forwardauth.tls.insecureSkipVerify=false"
  • tls.ca: certificado CA para verificar el servicio de autenticación cuando usas HTTPS.
  • tls.cert y tls.key: certificado y clave para autenticación mutua (mTLS) con el servicio de auth.
  • tls.insecureSkipVerify: no te recomiendo ponerlo a true a no ser que estés en desarrollo. En producción, verifica siempre los certificados.

ForwardAuth con cabeceras personalizadas

A veces el servicio de autenticación necesita información adicional de la petición original. Puedes configurar qué cabeceras se reenvían,

labels:
  - "traefik.http.middlewares.auth-forward.forwardauth.address=http://auth:4180"
  - "traefik.http.middlewares.auth-forward.forwardauth.authRequestHeaders=Accept, Accept-Language, Cookie, X-Forwarded-Proto, X-Forwarded-Method, X-Forwarded-Uri"

Esto es especialmente útil cuando tu servicio de autenticación necesita saber qué URL se está intentando acceder para redirigir de vuelta después del login.

Por ejemplo, OAuth2 Proxy necesita la cabecera X-Forwarded-Uri y X-Forwarded-Method para saber a dónde redirigir al usuario tras la autenticación. Sin esas cabeceras, el flujo OAuth se rompe.

Authelia con ForwardAuth

Uno de los casos de uso más comunes. Authelia es un servicio de autenticación que soporta 2FA, OAuth, LDAP, etc. Es, probablemente, la solución de autenticación self-hosted más completa que tienes hoy en día.

Primero levantas Authelia en tu docker-compose.yml,

  authelia:
    image: authelia/authelia:latest
    container_name: authelia
    volumes:
      - ./authelia:/config
    environment:
      - TZ=Europe/Madrid
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authelia.rule=Host(`auth.dominio.com`)"
      - "traefik.http.routers.authelia.entrypoints=websecure"
      - "traefik.http.routers.authelia.tls=true"
    networks:
      - traefik-net

Luego defines los middlewares de ForwardAuth que apunten a Authelia,

  # Middleware de autenticación (redirige al login si no está autenticado)
  - "traefik.http.middlewares.auth-authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.dominio.com/"
  - "traefik.http.middlewares.auth-authelia.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-authelia.forwardauth.authResponseHeaders=Remote-User, Remote-Groups, Remote-Email"

  # Middleware solo para verificar (sin redirección, para APIs)
  - "traefik.http.middlewares.auth-authelia-verify.forwardauth.address=http://authelia:9091/api/verify"
  - "traefik.http.middlewares.auth-authelia-verify.forwardauth.trustForwardHeader=true"

Fíjate en el detalle del parámetro ?rd=https://auth.dominio.com/. Ese rd es el parámetro de redirección que Authelia entiende: cuando un usuario no está autenticado, Authelia redirige el navegador a esa URL para que haga login. Si no pones ese parámetro, la API de Authelia solo devuelve un 403 y el usuario se queda con la pantalla en blanco.

El segundo middleware (auth-authelia-verify) es para APIs o servicios headless, donde no quieres que haya redirección al login, solo que se deniegue el acceso si no está autenticado.

Y lo aplicas a los servicios que quieras proteger,

  nextcloud:
    image: nextcloud:stable
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.rule=Host(`cloud.dominio.com`)"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.routers.nextcloud.tls=true"
      - "traefik.http.routers.nextcloud.middlewares=auth-authelia"
    networks:
      - traefik-net

De esta forma, cuando alguien intenta acceder a cloud.dominio.com, Traefik primero consulta a Authelia. Si no está autenticado, Authelia redirige al login. Una vez autenticado, la petición llega a Nextcloud con las cabeceras del usuario.

Explicación del endpoint de verificación

El parámetro ?rd= en la URL de ForwardAuth merece una explicación extra. Cuando Authelia recibe una petición en /api/verify sin el parámetro rd, simplemente devuelve 403 Forbidden si el usuario no está autenticado. Con el parámetro rd, Authelia devuelve una redirección HTTP 302 a esa URL para que el usuario haga login.

¿Y qué pasa si el usuario se autentica? Authelia establece una cookie de sesión y redirige de vuelta a la URL original. La próxima vez que Traefik consulte /api/verify, la cookie estará presente, Authelia la validará, y devolverá 200 OK.

Por eso es importante que el dominio del servicio de autenticación (por ejemplo, auth.dominio.com) esté en la misma zona que los servicios protegidos. Si no, las cookies no se compartirán y el usuario tendrá que autenticarse para cada servicio por separado.

El archivo users.yml de Authelia

El archivo de usuarios de Authelia se define en YAML y puede incluir múltiples usuarios con diferentes grupos y atributos,

users:
  lorenzo:
    displayname: "Lorenzo"
    password: "$argon2id$v=19$m=65536,t=3,p=2$..."  # Hash generado con authelia hash-password
    email: lorenzo@dominio.com
    groups:
      - admins
      - users

  ana:
    displayname: "Ana Martínez"
    password: "$argon2id$v=19$m=65536,t=3,p=2$..."
    email: ana@dominio.com
    groups:
      - users

  invitado:
    displayname: "Invitado"
    password: "$argon2id$v=19$m=65536,t=3,p=2$..."
    email: invitado@dominio.com
    groups: []

Luego, en las reglas de access_control, puedes usar los grupos para granular el acceso,

access_control:
  default_policy: deny
  rules:
    - domain: "auth.dominio.com"
      policy: bypass
    - domain: "admin.dominio.com"
      policy: one_factor
      subject:
        - "group:admins"
    - domain: "cloud.dominio.com"
      policy: two_factor
    - domain: "wiki.dominio.com"
      policy: one_factor
      subject:
        - "user:lorenzo"
        - "user:ana"

Fíjate en el nivel de granularidad que tienes: puedes definir políticas por dominio, por usuario, por grupo, o combinaciones. Puedes exigir un solo factor (contraseña) o dos factores (contraseña + TOTP). Y todo esto sin tocar ni una línea de Traefik.

Configuración de Authelia: lo mínimo que necesitas

Para que Authelia funcione, necesitas un archivo configuration.yml en el volumen que montaste. Aquí tienes lo esencial,

############################################################
#                   Authelia configuration                  #
############################################################
host: 0.0.0.0
port: 9091

log:
  level: info

totp:
  issuer: authelia.dominio.com
  period: 30
  skew: 1

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

access_control:
  default_policy: deny
  rules:
    - domain: "auth.dominio.com"
      policy: bypass
    - domain: "publico.dominio.com"
      policy: bypass
    - domain: "cloud.dominio.com"
      policy: two_factor
    - domain: "*.internal.dominio.com"
      policy: one_factor

session:
  name: authelia_session
  secret: UN_SECRETO_MUY_LARGO_Y_SEGURO
  expiration: 3600  # 1 hora
  inactivity: 300   # 5 minutos
  domain: dominio.com

regulation:
  max_retries: 5
  find_time: 2m
  ban_time: 5m

Y el archivo users.yml para los usuarios,

users:
  lorenzo:
    displayname: "Lorenzo"
    password: "$argon2id$v=19$m=65536,t=3,p=2$..."  # Hash generado con authelia hash-password
    email: lorenzo@dominio.com
    groups:
      - admins
      - users

Para generar el hash de la contraseña puedes usar el propio Authelia,

docker run authelia/authelia:latest authelia hash-password 'tu-contraseña'

OAuth2 Proxy con ForwardAuth

Otra combinación muy potente. OAuth2 Proxy actúa como intermediario entre Traefik y proveedores OAuth como Google, GitHub, GitLab, etc.

  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    container_name: oauth2-proxy
    command:
      - "--provider=google"
      - "--client-id=TU_CLIENT_ID"
      - "--client-secret=TU_CLIENT_SECRET"
      - "--cookie-secret=COOKIE_SECRET"
      - "--email-domain=*"
      - "--upstream=static://200"
      - "--http-address=0.0.0.0:4180"
      - "--cookie-secure=true"
      - "--cookie-httponly=true"
      - "--cookie-samesite=lax"
      - "--redirect-url=https://oauth.dominio.com/oauth2/callback"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.oauth.rule=Host(`oauth.dominio.com`)"
      - "traefik.http.routers.oauth.entrypoints=websecure"
      - "traefik.http.routers.oauth.tls=true"
    networks:
      - traefik-net

  miapp-protegida:
    image: nginx:alpine
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.miapp.rule=Host(`app.dominio.com`)"
      - "traefik.http.routers.miapp.entrypoints=websecure"
      - "traefik.http.routers.miapp.tls=true"
      - "traefik.http.routers.miapp.middlewares=auth-oauth"
      - "traefik.http.middlewares.auth-oauth.forwardauth.address=http://oauth2-proxy:4180"
      - "traefik.http.middlewares.auth-oauth.forwardauth.trustForwardHeader=true"
      - "traefik.http.middlewares.auth-oauth.forwardauth.authResponseHeaders=X-Auth-User, X-Auth-Email"
      - "traefik.http.middlewares.auth-oauth.forwardauth.authRequestHeaders=X-Forwarded-Host, X-Forwarded-Uri, X-Forwarded-Method"
    networks:
      - traefik-net

He añadido algunos flags de seguridad importantes en OAuth2 Proxy: --cookie-secure=true para que solo se envíe por HTTPS, --cookie-httponly=true para que JavaScript no pueda acceder a la cookie, y --cookie-samesite=lax para protección CSRF. Esto te puede ahorrar más de un disgusto.

OAuth2 Proxy con GitHub

Si prefieres usar GitHub como proveedor, cambias el provider y las credenciales,

    command:
      - "--provider=github"
      - "--client-id=TU_GITHUB_CLIENT_ID"
      - "--client-secret=TU_GITHUB_CLIENT_SECRET"
      - "--cookie-secret=COOKIE_SECRET"
      - "--email-domain=*"
      - "--upstream=static://200"
      - "--http-address=0.0.0.0:4180"
      - "--github-org=TU_ORGANIZACION"  # Opcional: solo usuarios de tu org
      - "--github-team=TU_EQUIPO"        # Opcional: solo usuarios de un equipo

Esto te permite restringir el acceso solo a miembros de una organización o equipo de GitHub. Muy útil para entornos de trabajo.

OAuth2 Proxy con GitLab

    command:
      - "--provider=gitlab"
      - "--client-id=TU_GITLAB_CLIENT_ID"
      - "--client-secret=TU_GITLAB_CLIENT_SECRET"
      - "--cookie-secret=COOKIE_SECRET"
      - "--email-domain=*"
      - "--upstream=static://200"
      - "--http-address=0.0.0.0:4180"
      - "--gitlab-group=mi-grupo"        # Opcional: solo usuarios de un grupo

El esquema es el mismo para cualquier proveedor OAuth2. La clave está en tener el client-id y client-secret correctos, y configurar bien el callback URL en el proveedor.

El truco del callback URL

Cuando configures OAuth2 Proxy, uno de los pasos que más quebraderos de cabeza da es la URL de callback. En el proveedor (Google, GitHub, etc.) tienes que registrar una URL de redirección autorizada. Esa URL tiene que ser exactamente,

https://tu-dominio-oauth.com/oauth2/callback

No vale con https://tu-dominio-oauth.com/ a secas. Tiene que ser la ruta completa con /oauth2/callback. Y tiene que coincidir exactamente con el valor de --redirect-url que le pases al contenedor.

ForwardAuth con tu propio servicio

¿Y si quieres hacer tu propio servicio de autenticación? Puedes. ForwardAuth solo espera que le devuelvas un 200 o un 401/403. Aquí tienes un ejemplo mínimo en Python con Flask,

from flask import Flask, request, Response

app = Flask(__name__)

API_KEYS={"abc123": "usuario1", "def456": "usuario2"}

@app.route("/verify")
def verify():
    api_key = request.headers.get("X-Api-Key")
    if api_key in API_KEYS:
        user = API_KEYS[api_key]
        return Response(
            status=200,
            headers={"X-Auth-User": user}
        )
    return Response(status=401)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Y el middleware apuntando a tu servicio,

  - "traefik.http.middlewares.auth-propio.forwardauth.address=http://mi-auth:8080/verify"
  - "traefik.http.middlewares.auth-propio.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-propio.forwardauth.authResponseHeaders=X-Auth-User"

Esto te da un control total sobre cómo se autentican los usuarios. Puedes conectar con LDAP, una base de datos, un fichero JSON, o lo que se te ocurra.

Authelia vs Authentik vs OAuth2 Proxy: ¿cuál eliges?

Vale, tienes tres opciones principales para ForwardAuth. ¿Cuál te interesa más? Te resumo las diferencias para que puedas decidir,

Authelia:

  • Lo mejor para entornos personales y familiares.
  • Soporta 2FA con TOTP, DUO Push, y WebAuthn.
  • Tiene su propio gestor de usuarios con políticas de acceso granulares.
  • Ideal si quieres control total sin depender de terceros.
  • Un poco más complejo de configurar que OAuth2 Proxy.

OAuth2 Proxy:

  • Lo mejor si ya usas un proveedor externo (Google, GitHub, GitLab).
  • No gestiona usuarios propios, delega en el proveedor OAuth.
  • Más sencillo de configurar que Authelia.
  • No tiene 2FA propio, depende del que tenga tu proveedor.
  • Ideal para equipos que ya usan GitHub o Google Workspace.

Authentik:

  • Una alternativa muy sólida a Authelia.
  • Soporta SSO, LDAP, OAuth, SAML, y más.
  • Tiene interfaz web para gestionar usuarios y políticas.
  • Más pesado en recursos que Authelia.
  • Ideal si necesitas integración con Active Directory o LDAP.

El middleware Chain: agrupa middlewoods de forma ordenada

Cuando empiezas a tener varios middlewoods de autenticación, llega un momento en que la cadena se hace larga y difícil de mantener. Aquí entra el middleware Chain, que te permite agrupar varios middlewoods bajo un mismo nombre.

  # Defines los middlewoods individuales
  - "traefik.http.middlewares.ip-restrict.ipwhitelist.sourcerange=192.168.1.0/24"
  - "traefik.http.middlewares.auth-authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.dominio.com/"
  - "traefik.http.middlewares.auth-authelia.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-authelia.forwardauth.authResponseHeaders=Remote-User, Remote-Groups"

  # Los agrupas en una cadena
  - "traefik.http.middlewares.auth-completo.chain.middlewares=ip-restrict, auth-authelia"

  # Y aplicas un solo nombre al router
  - "traefik.http.routers.admin.middlewares=auth-completo"

Esto te simplifica mucho los routers, sobre todo cuando tienes varios servicios con la misma combinación de middlewoods. Si cambia la política, solo tienes que modificar la cadena, no todos los routers.

¿Otra ventaja? Puedes reutilizar la misma cadena en varios routers, asegurándote de que todos los servicios protegidos tengan exactamente la misma configuración de seguridad.

Combinar ForwardAuth con IPWhiteList

Una de las mejores prácticas que te recomiendo es combinar ForwardAuth con IPWhiteList. Así tienes doble filtro: primero verificas que la IP está permitida, y luego que el usuario está autenticado.

Para ello, creas una cadena de middlewares,

  # Middleware de IP whitelist
  - "traefik.http.middlewares.ip-restrict.ipwhitelist.sourcerange=192.168.1.0/24, 10.0.0.0/8"

  # Middleware de ForwardAuth
  - "traefik.http.middlewares.auth-authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.dominio.com/"
  - "traefik.http.middlewares.auth-authelia.forwardauth.trustForwardHeader=true"
  - "traefik.http.middlewares.auth-authelia.forwardauth.authResponseHeaders=Remote-User, Remote-Groups"

  # Cadena: primero IP, luego auth
  - "traefik.http.routers.admin.middlewares=ip-restrict, auth-authelia"

El orden importa. La cadena se ejecuta de izquierda a derecha. Si pones auth-authelia, ip-restrict, primero se autentica y luego se comprueba la IP. Si pones ip-restrict, auth-authelia, primero se comprueba la IP y luego se autentica. Normalmente te interesa este segundo orden: si la IP no está permitida, ni siquiera molestas al servicio de autenticación.

Errores comunes con ForwardAuth

Por experiencia, estos son los errores que más veces te vas a encontrar:

  1. Bucle infinito de redirección: el servicio de auth redirige al login, Traefik reenvía la petición al servicio de auth, y este vuelve a redirigir al login. Para solucionarlo, asegúrate de que el dominio del servicio de auth esté en la lista de bypass de tu sistema de autenticación.
  2. URL incorrecta en el address: no uses localhost dentro de un contenedor. localhost en el contenedor de Traefik NO es el mismo que en tu máquina host. Usa el nombre del servicio de Docker (por ejemplo, http://authelia:9091/api/verify).
  3. Cabeceras que no se pasan: si el servicio final no recibe el usuario, revisa que authResponseHeaders tenga los nombres exactos que devuelve tu servicio de auth. Un error tipográfico aquí y las cabeceras no se inyectan.
  4. Timeout en la autenticación: si tu servicio de auth tarda más de 30 segundos en responder (por ejemplo, porque tarda en conectar con un proveedor OAuth), Traefik puede cortar la conexión. Ajusta los timeouts de Traefik si es necesario.
  5. Puerto incorrecto: Authelia usa el puerto 9091 por defecto, no el 9090. OAuth2 Proxy usa el 4180. Asegúrate de que el puerto en la URL del address coincida con el puerto interno del contenedor, no con el puerto mapeado.
  6. Confundir el middleware de auth con el router del servicio de auth: el router de Authelia (auth.dominio.com) sirve la interfaz de login. El middleware de ForwardAuth apunta a http://authelia:9091/api/verify, que es el endpoint interno de verificación. No son la misma URL.

Seguridad: consejos que no deberías ignorar

Voy a ser directo. La autenticación es la primera línea de defensa de tus servicios, y hay cosas que no puedes hacer mal:

  1. BasicAuth solo con HTTPS: ya te lo he dicho antes, pero lo repito. Sin TLS, la contraseña viaja en base64, que es lo mismo que texto plano. Si no tienes HTTPS, mejor no pongas BasicAuth.
  2. No uses contraseñas débiles: admin:admin no es una opción, ni aunque el servicio sea interno. Usa contraseñas generadas con openssl rand -base64 32 o similar.
  3. Aísla el servicio de autenticación: el contenedor de Authelia, OAuth2 Proxy o Keycloak no debería estar en la misma red que servicios no autenticados. Ponlo en su propia red y solo los servicios que necesiten auth deben tener acceso.
  4. Protege el endpoint de callback: en OAuth2 Proxy, el endpoint /oauth2/callback es crítico. Asegúrate de que solo sea accesible a través de Traefik y no directamente.
  5. Usa el mínimo privilegio: no des acceso de administrador a todos los usuarios. Authelia te permite definir políticas de acceso granulares. Úsalas. No todo el mundo necesita acceso a todo.
  6. Rotación de secretos: cambia las contraseñas, hashes y secrets periódicamente. Especialmente el cookie-secret de OAuth2 Proxy y el secret de sesión de Authelia.

Buenas prácticas

  1. Nunca expongas paneles de administración sin autenticación. Por muy «solo los conozco yo» que sea. Esto te puede ahorrar más de un disgusto.
  2. Usa BasicAuth para cosas simples y ForwardAuth para cosas serias. BasicAuth es fácil de configurar pero no escala. ForwardAuth es más complejo pero mucho más potente.
  3. Centraliza la configuración de usuarios. Si usas BasicAuth, pon los usuarios en un archivo externo, no en las labels. Así puedes añadir/quitar usuarios sin tocar el docker-compose.
  4. Combina ForwardAuth con rate limiting. No solo quieres saber quién entra, sino cuánto pueden entrar.
  5. Para servicios críticos, usa doble autenticación: IPWhiteList + ForwardAuth. Más vale prevenir.
  6. Usa nombres de middleware descriptivos: en lugar de auth1 o miauth, usa auth-authelia-verify o auth-basico-admin. Cuando tengas 20 middlewares, lo agradecerás.
  7. Documenta qué hace cada middleware: un comentario en el docker-compose.yml explicando el propósito de cada middleware te ahorrará tener que leer la configuración entera cuando vuelvas a tocarla dentro de 6 meses.

Conclusión

Los middlewares de autenticación son probablemente los más importantes de todo el ecosistema Traefik. Te permiten controlar el acceso a tus servicios sin modificar ni una línea del código de los mismos. Desde algo tan simple como BasicAuth hasta sistemas complejos con Authelia, OAuth o tu propio servicio de autenticación.

Al final, no se trata más que de elegir la herramienta adecuada para cada caso. ¿Un panel interno para ti solo? BasicAuth y listo. ¿Una aplicación con varios usuarios y 2FA? Authelia. ¿Quieres que tus usuarios entren con su cuenta de Google? OAuth2 Proxy. Cada uno tiene su sitio.

En el próximo capítulo veremos TLS y Let’s Encrypt, para que todos estos servicios tengan certificados SSL válidos de forma automática. Porque, como te he dicho, la autenticación sin HTTPS no sirve de nada.


Más información,

Deja una respuesta