5

Traefik como puerta de entrada segura

Vistas: 1
Traefik como puerta de entrada segura

Los primeros cuatro capítulos fueron la base: SSH blindado, nftables como muralla, fail2ban echando a los pesados y un usuario no-root con los privilegios justos. Tu servidor tiene unos cimientos sólidos. Pero hasta ahora solo hemos protegido el sistema operativo. Los servicios que vas a exponer en internet, esos están desnudos. Y aquí es donde entra Traefik. Traefik no es solo un proxy inverso. Es la puerta de entrada única a todos tus servicios. Cada petición que llega a tu servidor pasa por él. Cada conexión HTTPS la termina él. Cada intento de acceso al dashboard lo filtra él. Un solo punto de entrada, un solo punto que asegurar. Eso es un edge router.

Pero ojo: tener Traefik funcionando no significa tenerlo seguro. Una configuración por defecto de Traefik puede ser tan peligrosa como no tener nada. El dashboard expuesto, el puerto 8080 abierto al mundo, logs desactivados, TLS sin forzar… errores que convierten tu puerta de entrada en una puerta giratoria. Este capítulo no es un tutorial de instalación de Traefik. Si necesitas eso, el tutorial de Traefik v3 de atareao.es es tu referencia. Aquí vamos a asumir que ya lo tienes funcionando o que lo instalas siguiendo ese tutorial. Lo que vamos a hacer es blindarlo. Vamos a configurar entrypoints seguros, TLS con Let’s Encrypt, un dashboard que no sea accesible para cualquiera, logs de acceso para que fail2ban pueda hacer su trabajo, y una base de middlewares que aplicaremos a todo lo que pase por Traefik.

Traefik como edge router

¿Qué significa exactamente que Traefik sea un edge router?

Significa que todas las peticiones que vienen de internet llegan primero a él. No hay Nginx por un lado, Caddy por otro, y un servicio directo en el puerto 3000. Todo pasa por Traefik. Él decide quién entra, quién no, y hacia dónde va cada petición.

Esto tiene varias ventajas de seguridad enormes:

  • Superficie de ataque reducida. Un solo servicio expuesto a internet en lugar de diez. Si tienes una aplicación web en el puerto 3000, un panel de administración en el 8081 y una API en el 4000, tienes tres puertos abiertos que proteger. Con Traefik, solo el 443 está abierto. El resto solo son accesibles a través de Traefik, internamente.
  • TLS centralizado. Toda la terminación SSL ocurre en un solo sitio. Un lugar donde configurar certificados, renovaciones y políticas TLS. No tienes que andar configurando HTTPS en cada servicio.
  • Políticas uniformes. Puedes aplicar middlewares globales que afecten a todas las rutas. Rate limiting, cabeceras de seguridad, autenticación básica, whitelist de IPs. Una vez, en un sitio, para todos los servicios.
  • Observabilidad centralizada. Logs de acceso, métricas, dashboard de estado. Todo en un solo panel.

El modelo mental es este: internet → [Puerto 443] → Traefik → [Red interna Docker] → tus servicios.

Nada más entra. Nada más sale. Un solo punto de control.

Entrypoints seguros

El entrypoint es el puerto por el que Traefik escucha. Por defecto, Traefik expone tres entrypoints si usas la configuración básica: web (puerto 80), websecure (puerto 443) y traefik (puerto 8080, el dashboard).

El problema: la configuración por defecto no es segura.

Vamos a ver cómo deberían quedar los entrypoints en tu archivo de configuración, ya sea en el traefik.yml o en las etiquetas de tu docker-compose.yml.

Solo 443 para el exterior

El entrypoint websecure (443) es el único que debería estar accesible desde internet. Aquí es donde llegan las peticiones HTTPS.

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"

Fíjate en algo: el puerto 80 no desaparece. Sigue siendo necesario porque los clientes suelen intentar HTTP primero antes de redirigir a HTTPS. Pero su única función es redirigir. No sirve contenido. No procesa peticiones. Redirige y punto.

La opción permanent: true devuelve un código 308 (redirección permanente). Los navegadores lo recuerdan y la próxima vez irán directamente a HTTPS. Menos viajes de ida y vuelta.

El puerto 8080 no se toca

El entrypoint traefik (puerto 8080) es el del dashboard. Y aquí viene el error más común: la gente lo mapea directamente al puerto 8080 del host y se olvida.

# Configuración típica (errónea)
ports:
  - "8080:8080"   # MAL: expones el dashboard a internet

No. El dashboard de Traefik no debería estar accesible desde internet. Ni siquiera con autenticación básica, a menos que no tengas otro remedio. La mejor práctica es:

  1. No exponer el puerto 8080 al exterior. En tu docker-compose.yml, no mapees el puerto 8080 del contenedor al host. Si necesitas acceder al dashboard, hazlo a través de una ruta de Traefik con autenticación y restricción de IP, o mejor aún, solo a través de tu VPN WireGuard.
  2. Si lo expones, que sea con un router interno. Puedes definir una ruta en Traefik para el dashboard —por ejemplo, traefik.tudominio.com— y aplicarle middlewares de seguridad: autenticación básica + whitelist de IPs.

Vamos a ver ambas opciones.

Opción recomendada: dashboard solo por VPN

En el docker-compose.yml, no expongas el puerto 8080:

services:
  traefik:
    image: traefik:v3.3
    ports:
      - "80:80"        # Solo para redirigir a HTTPS
      - "443:443"      # El único entrypoint público
    # NADA de 8080

Para acceder al dashboard, te conectas por VPN (WireGuard, que veremos en el bloque 4) y accedes directamente a la IP interna de Traefik: http://traefik:8080 desde el mismo Docker network, o http://172.x.x.x:8080 desde tu máquina local si estás en la misma red.

Opción con autenticación y ruta segura

Si no tienes VPN y necesitas acceder al dashboard desde fuera, hazlo a través de una ruta de Traefik. Primero, genera un usuario para autenticación básica:

echo $(htpasswd -nb lorenzo contraseñasegura)

Eso te da algo como lorenzo:$apr1$xxxx.... Lo pones en un archivo de configuración o en una etiqueta de Docker.

Luego defines la ruta del dashboard con middlewares:

labels:
  - "traefik.http.routers.dashboard.rule=Host(`traefik.tudominio.com`)"
  - "traefik.http.routers.dashboard.service=api@internal"
  - "traefik.http.routers.dashboard.middlewares=auth-dashboard,whitelist-dashboard"
  - "traefik.http.middlewares.auth-dashboard.basicauth.users=lorenzo:$$apr1$$xxxx..."
  - "traefik.http.middlewares.whitelist-dashboard.ipwhitelist.sourcerange=127.0.0.1/32,192.168.1.0/24"

¿Ves la cadena de middlewares? auth-dashboard y whitelist-dashboard. Se ejecutan en orden. Primero comprueba la IP, luego pide autenticación. Si no pasas el primer filtro, ni siquiera llegas a la pantalla de login. Eso evita que bots intenten fuerza bruta contra la autenticación básica.

La whitelist debe tener al menos tu IP de casa o la de tu VPN. Si pones 0.0.0.0/0, no estás protegiendo nada.

TLS con Let’s Encrypt

El certificado SSL es lo que hace que la gente confíe en tu sitio. Sin HTTPS, los navegadores marcan tu página como No segura. Con HTTPS y un candado verde, la comunicación está cifrada de principio a fin.

Traefik integra Let’s Encrypt de forma nativa. No necesitas certbot, ni scripts de renovación, ni nada. Configuras el resolutor ACME y Traefik se encarga de pedir y renovar los certificados automáticamente.

Configuración base

En tu archivo de configuración estática (traefik.yml o en command: dentro del docker-compose):

certificatesResolvers:
  letsencrypt:
    acme:
      email: tu@email.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

Tres parámetros clave:

  • email. Necesario para que Let’s Encrypt te avise cuando un certificado está a punto de caducar (aunque Traefik los renueva automáticamente).
  • storage. Ruta donde se guardan los certificados. Importante: este archivo debe tener permisos 600. Si no, Traefik no podrá leerlo. Si usas Docker, asegúrate de montar un volumen persistente.
  • httpChallenge. Usa el puerto 80 para validar la propiedad del dominio. Por eso el entrypoint web (80) sigue existiendo aunque solo redirija.

Para los detalles finos de la configuración —tipos de challenge, HTTP-01 vs DNS-01, certificados wildcard, resolución por proveedor DNS— te remito al tutorial de Traefik v3 de atareao.es. Allí está todo explicado al detalle.

Certificados wildcard

Si tienes varios subdominios —app1.tudominio.com, app2.tudominio.com, traefik.tudominio.com—, puedes usar un certificado wildcard *.tudominio.com en lugar de un certificado por cada subdominio.

Pero ojo: Let’s Encrypt solo emite wildcards mediante el challenge DNS-01. Necesitas configurar un proveedor DNS (Cloudflare, DigitalOcean, Route53, etc.) para que Traefik pueda crear y eliminar registros TXT de validación.

Es más complejo pero más limpio. Un solo certificado para todos tus subdominios, y si añades uno nuevo, no tienes que esperar a que se genere un certificado nuevo.

¿Y si no uso Let’s Encrypt?

Puedes usar certificados propios. Traefik permite cargar certificados desde archivos locales. No es lo recomendado para producción —a no ser que tengas tu propia CA—, pero para entornos de desarrollo o redes internas funciona perfectamente.

tls:
  certificates:
    - certFile: /certs/tudominio.crt
      keyFile: /certs/tudominio.key

En cualquier caso, si usas certificados autofirmados, los navegadores se van a quejar. Para servicios públicos, Let’s Encrypt es la opción correcta.

Asegurar el dashboard

Ya lo hemos adelantado, pero merece un punto aparte porque es uno de los errores de seguridad más comunes con Traefik.

El dashboard de Traefik muestra información sensible: rutas, servicios, middlewares, estado de los certificados, configuración actual. Si alguien accede a él, tiene un mapa completo de tu infraestructura. Sabe qué servicios tienes, cómo se llaman, qué middlewares usas y hasta dónde están los puntos débiles.

Te confieso que en mi primer despliegue de Traefik tuve el dashboard expuesto durante semanas sin darme cuenta. Lo había mapeado al puerto 8080 del host y, como solo accedía desde casa, nunca reparé en que cualquiera con un escáner de puertos podía verlo. Un día, revisando los logs de acceso, vi peticiones de IPs extrañas a la ruta del dashboard. Desde entonces, el dashboard vive detrás de WireGuard y no tiene dirección pública. Una lección que aprendí por las malas.

Nunca expongas el dashboard sin protección

La regla de oro: si el dashboard es accesible desde internet sin autenticación ni restricción de IP, estás regalando información.

Configuración mínima segura:

labels:
  - "traefik.http.routers.api.rule=Host(`traefik.tudominio.com`)"
  - "traefik.http.routers.api.service=api@internal"
  - "traefik.http.routers.api.middlewares=auth-dashboard"
  - "traefik.http.middlewares.auth-dashboard.basicauth.users=lorenzo:$$apr1$$xxxx..."

Pero insisto: la opción más segura es no exponerlo en absoluto y acceder solo por VPN. En el bloque 4 de este tutorial veremos WireGuard. Cuando tengas la VPN montada, puedes eliminar la ruta del dashboard y acceder directamente por la IP interna. El dashboard no debería tener dirección pública.

Middleware de autenticación básica

Si vas a usar autenticación básica, ten en cuenta lo siguiente:

  • La contraseña viaja en Base64, no cifrada. Por eso necesitas HTTPS sí o sí. Sin TLS, la autenticación básica es equivalente a mandar la contraseña en texto plano.
  • No uses contraseñas comunes. El dashboard es un objetivo jugoso.
  • Cambia la contraseña periódicamente.
  • Combínala siempre con whitelist de IP si puedes. Reduce drásticamente la superficie de ataque.

Whitelist de IP

El middleware ipwhitelist permite restringir el acceso a un rango de IPs específico. Esto es especialmente útil si tienes una IP fija en casa o si siempre te conectas a través de una VPN.

labels:
  - "traefik.http.middlewares.whitelist-dashboard.ipwhitelist.sourcerange=192.168.1.0/24,10.0.0.0/8"

Si usas WireGuard, tu IP dentro de la VPN será algo como 10.0.0.x. Añades ese rango a la whitelist y solo podrás acceder al dashboard desde la VPN.

Logs de acceso en formato common

En el capítulo 3 configuramos fail2ban para proteger SSH. Pero fail2ban no solo sirve para SSH. También puede monitorizar los logs de acceso de Traefik y bloquear IPs que hagan peticiones sospechosas.

Para eso necesitamos que Traefik genere logs de acceso en un formato que fail2ban pueda parsear. El formato estándar es common, el mismo que usa Apache y Nginx.

Configuración de logs en Traefik

En tu archivo de configuración estática:

accessLog:
  filePath: /var/log/traefik/access.log
  format: common

El formato common genera líneas como esta:

192.168.1.100 - - [17/Jun/2026:14:30:22 +0000] "GET / HTTP/1.1" 200 1234 "-" "Mozilla/5.0"

Eso es justo lo que fail2ban espera. IP, fecha, método, ruta, código de estado, tamaño, user-agent.

Puedes afinar más:

accessLog:
  filePath: /var/log/traefik/access.log
  format: common
  filters:
    statusCodes:
      - "400-499"
      - "500-599"
  fields:
    defaultMode: keep
    names:
      ClientUsername: drop

Este ejemplo solo logea peticiones con códigos 4xx y 5xx, ignorando las 200 que son la mayoría y no aportan información de seguridad. También elimina el campo ClientUsername para reducir ruido.

¿Por qué es importante esto? Porque fail2ban puede leer estos logs y detectar patrones de ataque: peticiones a rutas inexistentes de WordPress, intentos de inyección SQL, fuerza bruta en formularios de login. Pero eso lo veremos en el capítulo correspondiente del bloque 3.

De momento, asegúrate de que los logs están activos y accesibles. fail2ban los necesita.

Rotación de logs

Los logs de acceso crecen rápido. En un servidor con varios servicios, puedes tener cientos de megabytes de logs en una semana. Sin rotación, el disco se llena y Traefik deja de funcionar.

Configura logrotate para los logs de Traefik:

sudo cat > /etc/logrotate.d/traefik << 'EOF'
/var/log/traefik/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        docker exec traefik kill -USR1 1
    endscript
}
EOF

La línea docker exec traefik kill -USR1 1 envía una señal a Traefik para que rote los logs sin perder peticiones. Si ejecutas Traefik sin Docker, ajusta el comando según tu configuración de proceso (un kill -USR1 al PID de Traefik basta).

Middleware chain por defecto

Aquí entra uno de los conceptos más potentes de Traefik: los middlewares.

Un middleware es un pequeño módulo que procesa la petición antes de que llegue al servicio. Puede modificar cabeceras, redirigir, limitar velocidad, exigir autenticación, o bloquear según la IP.

Lo interesante es que puedes encadenarlos. Un middleware que añade cabeceras de seguridad, seguido de otro que limita el ratio de peticiones, seguido de otro que comprueba whitelist de IP. La petición pasa por cada uno en orden. Si uno falla, la petición se descarta y no llega al servicio.

Middleware chain global

En capítulos siguientes del bloque 3 vamos a detallar cada middleware: cabeceras de seguridad (HSTS, CSP, X-Frame-Options), rate limiting, autenticación, etc. Pero aquí quiero que configures la base: un middleware chain que se aplique por defecto a todas las rutas.

labels:
  - "traefik.http.middlewares.chain-seguridad.chain.middlewares=middleware-headers,middleware-ratelimit,middleware-compress"

O, si usas archivos de configuración dinámica (TOML o YAML):

http:
  middlewares:
    chain-seguridad:
      chain:
        middlewares:
          - middleware-headers
          - middleware-ratelimit
          - middleware-compress

Después aplicas esta cadena por defecto desde el entrypoint:

entryPoints:
  websecure:
    address: ":443"
    http:
      middlewares:
        - chain-seguridad

Así todas las peticiones que entren por HTTPS pasan por la cadena de seguridad. No puedes olvidarte de aplicar un middleware a un nuevo servicio. Es automático.

¿Qué middlewares incluir en la cadena por defecto? Depende de tus necesidades, pero una base sólida sería:

  • Headers de seguridad: cabeceras HTTP que refuerzan la seguridad desde el navegador (HSTS, X-Content-Type-Options, Referrer-Policy)
  • Compresión: mejora el rendimiento sin afectar a la seguridad
  • Rate limiting básico: protección contra fuerza bruta a nivel global

Iremos viendo cada uno en detalle en los próximos capítulos. De momento, deja la cadena preparada aunque los middlewares individuales estén vacíos o sean simples placeholders. La estructura es lo que importa ahora.

Verificación

Has configurado los entrypoints, el TLS, los logs, la cadena de middlewares. ¿Está todo bien? Vamos a comprobarlo.

Solo el puerto 443 está abierto

Desde tu máquina local (o desde un VPS de prueba), escanea los puertos de tu servidor:

nmap -p 1-65535 tu-servidor.com

El resultado debería mostrar solo el puerto 443 como abierto (y quizá el 80 si no has aplicado la redirección). Nada de 8080, nada de puertos aleatorios de servicios.

Si ves algo más, revisa tu configuración de Docker y de Traefik. Puede que tengas un contenedor exponiendo un puerto directamente al host sin pasar por Traefik.

También puedes comprobarlo desde el propio servidor:

sudo ss -tlnp

Esto muestra los puertos en los que hay procesos escuchando. Deberías ver:

  • :80 → solo Traefik
  • :443 → solo Traefik
  • :8080 → solo Traefik (pero no mapeado al host si sigues la opción VPN)

El dashboard no es accesible públicamente

Intenta acceder a https://traefik.tudominio.com (o el dominio que hayas configurado) desde un navegador que no esté en tu whitelist. ¿Usando una VPN diferente? ¿Con el móvil desde datos? Si ves la página de login o, peor aún, el dashboard directamente, algo falla.

Si no tienes whitelist, al menos deberías ver la pantalla de autenticación básica. Si ves el dashboard sin login, para ya y revisa la configuración.

La prueba definitiva: usa un servicio como SSL Labs para analizar la configuración TLS de tu servidor. Te dará una nota de la A a la F. Busca la A. Si tienes menos, revisa las opciones TLS de Traefik.

Los logs se están generando

Comprueba que los logs de acceso existen y tienen contenido:

docker exec traefik cat /var/log/traefik/access.log | tail -20

Si no hay logs, revisa la configuración de accessLog en tu archivo estático. Si los logs están llenos de peticiones 200, plantéate filtrar solo códigos 4xx y 5xx como vimos antes. El ruido no ayuda a detectar ataques.

fail2ban puede leer los logs

Asegúrate de que fail2ban tiene permisos para leer los logs de Traefik. Si Traefik corre en Docker y los logs están dentro del contenedor, necesitas montar un volumen compartido.

# En docker-compose.yml
volumes:
  - /var/log/traefik:/var/log/traefik

Y en el host:

sudo chown -R root:adm /var/log/traefik
sudo chmod 750 /var/log/traefik

fail2ban corre como root pero sus procesos hijos pueden tener restricciones. Con permisos 750 y grupo adm, tanto root como los usuarios del grupo adm pueden leer los logs.

Resumen

En este capítulo has aprendido a convertir Traefik en una puerta de entrada segura, no solo en un proxy inverso funcional.

  • Edge router: un solo punto de entrada, un solo punto que asegurar.
  • Entrypoints seguros: solo el 443 abierto al exterior. El 80 solo redirige. El 8080 no se expone.
  • TLS con Let’s Encrypt: certificados automáticos, renovación sin intervención, y la opción de wildcards con DNS-01.
  • Dashboard protegido: autenticación básica + whitelist de IP, o mejor aún, solo accesible por VPN.
  • Logs de acceso: formato common para que fail2ban pueda leerlos, con rotación para que no se acumulen.
  • Middleware chain por defecto: estructura preparada para aplicar seguridad a todas las rutas automáticamente.
  • Verificación: comprobaciones prácticas para asegurarte de que todo está bien configurado.

En el próximo capítulo entraremos en materia con los middlewares de seguridad. Cabeceras HTTP, rate limiting, autenticación, whitelist de IP, y cómo configurarlos como middlewares de Traefik para que todos tus servicios las hereden sin esfuerzo.

Tu servidor ya tiene una puerta de entrada. Ahora vamos a ponerle los candados.


Más información

Deja una respuesta