Logotipo de Bitdoze

Autenticación Básica en Traefik: Protege Tus Servicios Docker

Aprende a añadir autenticación básica en Traefik para proteger tus servicios Docker. Guía paso a paso con hashing bcrypt, etiquetas de Docker Compose y…

Dragos

Actualizado Publicado 19 min de lectura

Autenticación Básica en Traefik: Protege Tus Servicios Docker

Si ejecutas servicios Docker autoalojados detrás de Traefik, probablemente tengas algunos endpoints que no tienen autenticación propia: el panel de Traefik, un contenedor whoami, quizá algunas herramientas internas de desarrollo. Exponerlos a internet sin ningún control de acceso es un riesgo que no necesitas asumir.

El middleware de autenticación básica de Traefik añade un aviso de usuario/contraseña delante de cualquier servicio con solo un par de etiquetas Docker. No sustituye a una autenticación propia a nivel de aplicación, pero para herramientas de desarrollo y servicios internos es una primera capa rápida.

Esta guía cubre la configuración completa: generar contraseñas bcrypt seguras, añadir etiquetas de autenticación en Docker Compose, usar usersFile para configuraciones con varios servicios, personalizar el aviso de inicio de sesión, encadenar middlewares para defensa en profundidad y proteger el propio panel de Traefik. Todo está pensado para Traefik v3.x (la rama estable actual).

Si buscas más contenedores Docker que proteger, echa un vistazo a los mejores contenedores Docker autoalojados para un home server.

Middleware de autenticación básica de Traefik protegiendo servicios Docker con un aviso de inicio de sesión

Entendiendo el middleware de autenticación básica de Traefik

El middleware de Traefik se sitúa entre el cliente y tu servicio backend. Cuando llega una petición, Traefik la pasa por la cadena de middlewares antes de reenviarla al backend. La autenticación básica es uno de los muchos tipos de middleware. Intercepta la petición, comprueba las credenciales y deja pasar la petición o devuelve una respuesta 401 Unauthorized.

El flujo es así:

  1. El cliente envía la petición a https://myapp.example.com
  2. Traefik comprueba los middlewares: encuentra un middleware de autenticación básica en esta ruta
  3. ¿Sin credenciales? Traefik devuelve 401 Unauthorized con una cabecera WWW-Authenticate
  4. El navegador muestra el aviso de inicio de sesión: el usuario introduce usuario y contraseña
  5. El cliente reenvía la petición con la cabecera Authorization: Basic <base64-encoded>
  6. Traefik verifica el hash contra las entradas htpasswd configuradas
  7. ¿Credenciales válidas? Traefik reenvía la petición al backend
  8. ¿Credenciales inválidas? Traefik devuelve 401 de nuevo

Las credenciales de autenticación básica van codificadas en Base64, no cifradas. Eso significa que cualquiera que intercepte el tráfico (sin HTTPS) puede decodificarlas sin esfuerzo. Usa siempre la autenticación básica sobre HTTPS. Si tus servicios están detrás de Traefik, ya deberías tener TLS configurado.

Cuándo NO usar la autenticación básica

Si tu aplicación tiene autenticación propia (Grafana, Portainer, Nextcloud), prefiere la autenticación nativa de la app en lugar de envolverla dos veces con la autenticación básica de Traefik. La autenticación básica es ideal para herramientas de desarrollo sin autenticación integrada: el panel de Traefik, contenedores whoami, utilidades internas sencillas. No hay mecanismo de cierre de sesión. El navegador guarda las credenciales en caché hasta que cierras la pestaña.

Cómo configurar la autenticación básica de Traefik en Docker Compose

Esto asume que Traefik ya se ejecuta en Docker con el proveedor de Docker activado. Si todavía no lo has configurado, consulta primero la guía de Traefik como proxy inverso en Docker.

Requisitos previos

  • Traefik v3.x ejecutándose en Docker con el proveedor de Docker activado
  • Docker Compose v2 instalado
  • Un servicio ya expuesto a través de Traefik con una ruta HTTPS funcionando
  • Un nombre de dominio con el DNS apuntando a tu VPS
  • Certificados de Let’s Encrypt o Cloudflare configurados. Consulta certificados wildcard en Traefik
  • Redirección de HTTP a HTTPS configurada. Consulta redirección HTTP a HTTPS en Traefik
  • apache2-utils (Debian/Ubuntu) o httpd-tools (RHEL/Fedora) instalado para htpasswd

Paso 1: Genera contraseñas bcrypt con htpasswd

Usa bcrypt, no MD5

El método antiguo usaba htpasswd -nb, que genera hashes APR1/MD5. Son criptográficamente débiles. Usa siempre htpasswd -nB para bcrypt (prefijo $2y$). La documentación oficial de Traefik ahora recomienda bcrypt.

Instala la utilidad htpasswd:

bash
# Debian/Ubuntu
sudo apt update && sudo apt install apache2-utils

# RHEL/Fedora
# sudo dnf install httpd-tools

Genera un hash bcrypt:

Interactivo (recomendado)

bash
# The interactive prompt keeps the password out of your shell history
echo $(htpasswd -nB admin) | sed -e 's/\$/\$\$/g'

Te pedirá que escribas la contraseña. La salida se ve así:

text
admin:$$2y$$05$$...

Comando rápido de una línea

bash
# Fine for testing. Avoid in production, password ends up in shell history.
echo $(htpasswd -nBC 12 admin MySecurePass) | sed -e 's/\$/\$\$/g'

Qué está pasando aquí:

  • -nB: -n imprime en stdout en lugar de un archivo, -B usa bcrypt
  • -C 12: factor de coste opcional (2^12 rondas). El valor por defecto suele ser 5. Un valor más alto es más lento pero más resistente a la fuerza bruta. 12 es un valor razonable para producción.
  • sed -e 's/\$/\$\$/g': duplica cada $ porque Docker Compose interpreta $VAR como sustitución de variables de entorno. Sin esto, el hash queda corrupto al arrancar.

Cuándo no necesitas el escape $$

El doblado de $$ solo es necesario dentro de las etiquetas de docker-compose.yml. Si usas un usersFile (archivo de texto plano, cubierto en el paso 4), escribes el hash con un solo $ porque el archivo no se evalúa como YAML. Herramientas como el módulo docker_container de Ansible tampoco necesitan el doblado. Consulta variables de entorno en Docker Compose para más información.

Paso 2: Añade las etiquetas de autenticación básica a tu servicio Docker

Necesitas dos etiquetas por servicio: una para referenciar el middleware y otra para definirlo.

yaml
services:
  nginx:
    image: nginx:latest
    restart: unless-stopped
    env_file: .env
    networks:
      - traefik-net
    labels:
      - traefik.enable=true
      - traefik.http.routers.nginx.rule=Host(`nginx.example.com`)
      - traefik.http.routers.nginx.entrypoints=https
      - traefik.http.services.nginx.loadbalancer.server.port=80
      # Basic auth middleware
      - traefik.http.routers.nginx.middlewares=nginx-auth
      - traefik.http.middlewares.nginx-auth.basicauth.users=${TRAEFIK_USER_PASS}
      - traefik.http.middlewares.nginx-auth.basicauth.realm=Restricted Area
      - traefik.http.middlewares.nginx-auth.basicauth.removeheader=true
networks:
  traefik-net:
    external: true

Puntos clave:

  • El nombre del middleware debe coincidir entre la referencia del router (nginx-auth) y la definición del middleware (nginx-auth). Esta es la causa n.º 1 de los errores de «la autenticación no funciona». Una errata aquí hace que Traefik ignore el middleware sin avisar.
  • Usa nombres de middleware únicos por servicio. Si dos servicios usan el mismo nombre de middleware con listas de usuarios distintas, uno sobrescribirá al otro sin avisar.
  • Pon las etiquetas entre comillas. Sin comillas, YAML puede fallar con los caracteres especiales.

Comprueba que funciona:

bash
# Should return 401 Unauthorized
curl -I https://nginx.example.com

# Should return 200 with correct credentials
curl -I -u admin:yourpassword https://nginx.example.com

Paso 3: Guarda las credenciales en un archivo .env

Escribir el hash de la contraseña directamente en docker-compose.yml funciona, pero mete credenciales en el control de versiones y hace que rotarlas sea un dolor. Muévelas a un archivo .env en su lugar.

¿Por qué .env en lugar de valores fijos?

Mantiene las credenciales fuera del control de versiones, facilita la rotación y separa la configuración de los secretos. Para entornos de producción, considera los secretos de Docker Compose o un vault para un control aún más estricto.

bash
# .env (in the same directory as docker-compose.yml)
TRAEFIK_USER_PASS=admin:$$2y$$05$$KJ3RixvQ.Zabc123...rest_of_hash

Fíjate en el doblado de $$. Docker Compose sigue evaluando este archivo como sustitución de variables de entorno. Tu docker-compose.yml lo referencia con ${TRAEFIK_USER_PASS}, como se muestra en el paso 2.

Paso 4: Usa usersFile para varios servicios

Cuando tienes varios servicios que comparten las mismas credenciales, las etiquetas users en línea se vuelven repetitivas. El enfoque con usersFile te permite mantener un único archivo de texto plano que Traefik lee al arrancar.

Crea el archivo de usuarios:

bash
# ./traefik-data/users.txt
# Each line is username:hash, standard htpasswd format
# NO $$ doubling needed here, this is a plain text file, not YAML
admin:$2y$05$KJ3RixvQ.Zabc123...
viewer:$2y$05$AnotherHash...

Móntalo en el contenedor de Traefik (no en el contenedor del servicio):

yaml
# In Traefik's docker-compose.yml
services:
  traefik:
    image: traefik:v3.3
    volumes:
      - ./traefik-data/users.txt:/etc/traefik/users.txt:ro
      # ... other volumes

Referéncialo en las etiquetas del servicio:

yaml
# In any service's docker-compose.yml
labels:
  - traefik.http.routers.myapp.middlewares=myapp-auth
  - traefik.http.middlewares.myapp-auth.basicauth.usersfile=/etc/traefik/users.txt
  - traefik.http.middlewares.myapp-auth.basicauth.realm=My App

Móntalo en el contenedor de Traefik

El error n.º 1 de la comunidad: usersFile debe ser legible por el contenedor de Traefik, no por el contenedor del servicio. Traefik lee el archivo al arrancar. Si lo montas en el contenedor equivocado, obtendrás un error de configuración silencioso y la autenticación no se aplicará.

Comprueba que el archivo está bien montado:

bash
docker exec traefik cat /etc/traefik/users.txt

Nota sobre precedencia: si defines users y usersFile en el mismo middleware, los valores de users tienen prioridad sobre usersFile en el Traefik v3.x actual. De v3.1 a v3.3, usersFile tenía prioridad — el cambio se produjo en una versión posterior de v3.x, así que el comportamiento depende de tu versión.

Paso 5: Personaliza el aviso de inicio de sesión con realm

Por defecto, el diálogo de autenticación básica del navegador muestra «traefik» como nombre del realm. Puedes personalizarlo con la opción realm:

yaml
labels:
  - "traefik.http.middlewares.my-auth.basicauth.realm=My App - Restricted Access"

Es una pequeña mejora de UX, pero hace que el aviso parezca intencionado y no el de fábrica. Los usuarios ven «My App - Restricted Access» en lugar de «traefik» en el diálogo de inicio de sesión.

Paso 6: Elimina la cabecera Authorization por seguridad

Por defecto, Traefik reenvía la cabecera Authorization (que contiene las credenciales codificadas en Base64) a tu servicio backend. Si tu backend no la necesita, quítala:

yaml
labels:
  - "traefik.http.middlewares.my-auth.basicauth.removeheader=true"

Por qué importa: si tu servicio backend registra las cabeceras de las peticiones (y la mayoría lo hace), no quieres credenciales acabando en esos logs. Defensa en profundidad: elimina lo que no necesitas.

Paso 7: Pasa el nombre de usuario autenticado a los backends

Si tu backend necesita saber quién se autenticó, usa headerField para establecer una cabecera personalizada con el nombre de usuario:

yaml
labels:
  - "traefik.http.middlewares.my-auth.basicauth.headerField=X-WebAuth-User"

Esto añade X-WebAuth-User: <username> a las peticiones proxied. Útil para logging, comportamiento por usuario o control de acceso sencillo en aplicaciones sin autenticación propia.

Encadenar middlewares de Traefik para defensa en profundidad

La autenticación básica por sí sola es una sola capa. Para servicios expuestos a internet, encadénala con limitación de tasa y listas blancas de IPs.

Sintaxis para encadenar middlewares

Usa referencias de middleware separadas por comas: my-auth@docker,rate-limit@docker. El orden importa. Traefik los aplica de izquierda a derecha. Pon primero los middlewares que fallan más rápido (límite de tasa, lista blanca de IPs) antes que los más costosos (autenticación).

Autenticación básica + limitación de tasa

yaml
labels:
  - traefik.enable=true
  - traefik.http.routers.myapp.rule=Host(`myapp.example.com`)
  - traefik.http.routers.myapp.entrypoints=https
  - traefik.http.routers.myapp.middlewares=rate-limit@docker,my-auth@docker
  # Rate limiting
  - traefik.http.middlewares.rate-limit.ratelimit.average=100
  - traefik.http.middlewares.rate-limit.ratelimit.burst=50
  # Basic auth
  - traefik.http.middlewares.my-auth.basicauth.users=${TRAEFIK_USER_PASS}
  - traefik.http.middlewares.my-auth.basicauth.removeheader=true
  - traefik.http.middlewares.my-auth.basicauth.realm=My App

Esto limita el servicio a una media de 100 peticiones/segundo con picos de hasta 50, y exige autenticación.

Autenticación básica + lista blanca de IPs

yaml
labels:
  - traefik.http.routers.myapp.middlewares=local-only@docker,my-auth@docker
  # Allow only private networks
  - traefik.http.middlewares.local-only.ipallowlist.sourcerange=10.0.0.0/8,192.168.0.0/16
  # Basic auth
  - traefik.http.middlewares.my-auth.basicauth.users=${TRAEFIK_USER_PASS}

Nota: ipWhiteList pasó a llamarse ipAllowList en Traefik v3. Si migras desde v2, actualiza los nombres de las etiquetas.

Para una seguridad de servidor más amplia incluyendo la integración con CrowdSec, consulta cómo securizar un VPS con CrowdSec.

Proteger solo rutas específicas

Caso de uso: proteger /admin pero dejar /public abierto. Define dos routers en el mismo host:

yaml
labels:
  # Public router — no auth
  - traefik.http.routers.myapp.rule=Host(`myapp.example.com`)
  - traefik.http.routers.myapp.entrypoints=https
  # Admin router — with auth, higher priority
  - traefik.http.routers.myapp-admin.rule=Host(`myapp.example.com`) && PathPrefix(`/admin`)
  - traefik.http.routers.myapp-admin.entrypoints=https
  - traefik.http.routers.myapp-admin.priority=10
  - traefik.http.routers.myapp-admin.middlewares=admin-auth
  - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_USER_PASS}

El router más específico (/admin) necesita un valor de prioridad más alto para coincidir primero. La prioridad por defecto de Traefik se calcula a partir de la longitud de la regla, pero los valores explícitos son más fiables.

Proteger el panel de Traefik con autenticación básica

Uno de los casos de uso más comunes: securizar el propio panel de Traefik. Añade estas etiquetas al contenedor de Traefik:

yaml
services:
  traefik:
    image: traefik:v3.3
    command:
      - --api.dashboard=true
      - --providers.docker=true
      # ... other args
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.entrypoints=https
      - traefik.http.routers.dashboard.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_DASHBOARD_USERS}
      - traefik.http.middlewares.dashboard-auth.basicauth.realm=Traefik Dashboard
      - traefik.http.middlewares.dashboard-auth.basicauth.removeheader=true
    networks:
      - traefik-net

Usa una variable de entorno distinta para las credenciales del panel (TRAEFIK_DASHBOARD_USERS): no compartas la misma contraseña entre todos tus servicios.

Comprueba: abre https://traefik.example.com en tu navegador. Deberías ver un aviso de inicio de sesión. Tras introducir las credenciales, el panel de Traefik debería cargar.

Problemas comunes y solución de errores

401 Unauthorized: las credenciales no funcionan

Síntoma: el aviso de inicio de sesión aparece, pero las credenciales siempre se rechazan.

Causas habituales:

  • Hash generado con htpasswd -nb (MD5) en lugar de htpasswd -nB (bcrypt). Regenéralo con bcrypt.
  • Contraseña incorrecta: el hash de .env no coincide con lo que escribes.
  • $$ no duplicado en el archivo .env: Docker Compose eliminó los caracteres $ del hash.
  • Formato de hash incorrecto: Traefik espera usuario:hash, no solo el hash.

Solución: regenera y verifica:

bash
# Generate a fresh bcrypt hash
echo $(htpasswd -nBC 12 admin) | sed -e 's/\$/\$\$/g'

# Test with curl (should return 200)
curl -I -u admin:yourpassword https://myapp.example.com
404 Not Found: el aviso de autenticación no aparece

Síntoma: no hay aviso de inicio de sesión, solo un 404.

Causas habituales:

  • El nombre del middleware no coincide entre la referencia del router y la definición del middleware. Es el error más común.
yaml
# WRONG — names don't match:
- "traefik.http.routers.myapp.middlewares=traefik-auth"
- "traefik.http.middlewares.traefiknas-auth.basicauth.users=..."

# RIGHT — names match exactly:
- "traefik.http.routers.myapp.middlewares=myapp-auth"
- "traefik.http.middlewares.myapp-auth.basicauth.users=..."
  • Etiquetas en el contenedor equivocado: las etiquetas deben ir en el contenedor del servicio, no en el de Traefik (salvo que estés configurando la autenticación del panel).
  • El router no coincide con la petición: revisa la regla Host() y los entrypoints.
usersFile no se carga

Síntoma: la autenticación no funciona con usersFile, pero sí con users en línea.

Causa: el archivo está montado en el contenedor del servicio en lugar del contenedor de Traefik. Traefik lee el archivo, no tu backend.

Solución: comprueba que el archivo es legible dentro de Traefik:

bash
docker exec traefik cat /etc/traefik/users.txt

Si el archivo no está, móntalo en el docker-compose.yml de Traefik:

yaml
volumes:
  - ./traefik-data/users.txt:/etc/traefik/users.txt:ro
Errores de configuración: el middleware no se aplica

Síntoma: la autenticación funciona en un servicio pero no en otro, o el middleware parece ignorado.

Causas habituales:

  • Nombres de middleware duplicados entre servicios: si dos servicios definen un middleware llamado auth, uno sobrescribe al otro sin avisar. Usa nombres únicos: nginx-auth, grafana-auth, etc.
  • Etiquetas en el contenedor equivocado: las etiquetas deben ir en el servicio que necesita autenticación, no en un contenedor proxy compartido.
  • Erratas en las claves de las etiquetas: basicauth y no basicAuth, usersfile y no usersFile (las etiquetas no distinguen mayúsculas, pero sé consistente).

Comprueba: revisa la configuración detectada por Traefik:

bash
docker logs traefik 2>&1 | grep -i "middleware"

O revisa el panel (cuando puedas acceder) en la pestaña Middleware.

Hash bcrypt incompatible: la autenticación siempre falla

Síntoma: el hash parece correcto, pero la autenticación siempre lo rechaza.

Causa: la imagen Docker httpd:alpine puede producir hashes bcrypt incompatibles en algunas versiones.

Solución: genera el hash con apache2-utils en el host o en un contenedor Alpine completo:

bash
# On the host (Debian/Ubuntu)
sudo apt install apache2-utils
echo $(htpasswd -nBC 12 admin) | sed -e 's/\$/\$\$/g'

# Or via Docker
docker run --rm -it alpine:latest sh -c "apk add --no-cache apache2-utils && htpasswd -nBC 12 admin"

Lista rápida de verificación

Después de configurar la autenticación, ejecuta estos comandos para confirmar que todo funciona:

bash
# Should return 401 Unauthorized (no credentials)
curl -I https://myapp.example.com

# Should return 200 OK (with correct credentials)
curl -I -u admin:yourpassword https://myapp.example.com

# Check Traefik logs for auth-related entries
docker logs traefik 2>&1 | grep -i "authentication"

Limitaciones de la autenticación básica

La autenticación básica es una primera capa, no una solución completa

La autenticación básica tiene limitaciones reales. Conócelas antes de confiar en ella para algo sensible:

  • Sin mecanismo de cierre de sesión — el navegador guarda las credenciales en caché hasta que cierras la pestaña
  • Sin soporte de MFA — solo contraseña de un factor
  • Sin gestión de sesiones — las credenciales se envían en cada petición
  • Sin permisos granulares — el acceso es todo o nada
  • Las credenciales van en Base64, no cifradas — sin HTTPS, son legibles sin esfuerzo

Para algo realmente sensible (paneles de producción, aplicaciones de cara al usuario), usa un proxy de autenticación como Authelia o Authentik, o la autenticación nativa de la app. La autenticación básica es una buena primera capa para herramientas de desarrollo, utilidades internas y servicios sin autenticación propia.

Conclusión

El middleware de autenticación básica de Traefik es una forma rápida de añadir control de acceso a servicios Docker que no tienen autenticación propia. Con hashing de contraseñas bcrypt, el enfoque usersFile para credenciales compartidas y el encadenamiento de middlewares con limitación de tasa o listas blancas de IPs, obtienes una primera capa de protección sólida.

Esto es lo que hemos visto:

  • Generar contraseñas bcrypt seguras con htpasswd -nB
  • Añadir etiquetas de autenticación a servicios de Docker Compose
  • Usar archivos .env y usersFile para gestionar credenciales
  • Personalizar el aviso de inicio de sesión con realm
  • Eliminar la cabecera Authorization con removeHeader
  • Pasar el nombre de usuario autenticado a los backends con headerField
  • Encadenar middlewares para defensa en profundidad
  • Proteger el panel de Traefik

Si todavía estás configurando Traefik, empieza con la guía de Traefik como proxy inverso en Docker y configura certificados wildcard. Para más servicios que proteger detrás de autenticación, consulta los mejores contenedores Docker para un home server. Si prefieres una plataforma de despliegue gestionada, echa un vistazo a autoalojar con Dokploy.

¿Necesitas un VPS fiable donde ejecutar todo esto? Hetzner ofrece hosting VPS asequible con un rendimiento sólido para stacks Docker autoalojados.

Los precios de los VPS subieron en toda la línea en 2026 — si estás replanteándote un servidor alquilado, consulta qué cambió y cuándo gana un mini PC.