Agrega el credito "Laboratorio de investigacion en Software Libre Gugler - FCyT" en el encabezado y la seccion Licencia del README, y como pie en ambos documentos de docs/. Se documenta la exencion institucional en la politica de publicacion anonima: el nombre del laboratorio y git.gugler.com.ar son publicos por decision propia y no se anonimizan. El verificador recibe una lista blanca que separa autores institucionales de los que hay que revisar, y excluye el dominio del laboratorio del filtro de emails.
8.2 KiB
HTTP / HTTPS en Traefik — referencia de etiquetas
Todos los ejemplos son etiquetas de Docker Compose aplicables al servicio web
del proyecto. Traefik las lee por el socket de Docker y construye la cadena
Router → Middleware → Service.
1. Anatomía de una etiqueta
traefik.http.<scope>.<component>.<atributo>=<valor>
│ │ │
│ │ └─ propiedad (rule, entrypoints, tls, port…)
│ └─────────── tipo (routers, services, middlewares)
└──────────────────── ámbito/grupo (ej. "miapp-https")
Componentes usados:
| Componente | Función |
|---|---|
routers |
Decide qué petición llega (Host, Path) y a qué entrypoint responde |
middlewares |
Transformaciones (redirecciones, auth, headers) |
services |
Traduce a con qué puerto interno del contenedor |
tls |
Habilita TLS y define de dónde sale el certificado |
2. Exponer un proyecto (mínimo funcional)
networks:
default:
driver: bridge
proxy_network:
external: true
services:
web:
build: { context: ./app, dockerfile: Dockerfile }
container_name: miapp-web
restart: unless-stopped
networks: [ default, proxy_network ] # ← ambas redes
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy_network"
- "traefik.http.routers.miapp.rule=Host(`miapp.local`)"
- "traefik.http.routers.miapp.entrypoints=websecure"
- "traefik.http.services.miapp.loadbalancer.server.port=80"
Notas:
traefik.enable=truees obligatorio (exposedByDefault: false).traefik.docker.network=proxy_networkfuerza a leer las etiquetas desde esa red cuando el contenedor está en varias.- El puerto del service es el puerto interno, no el publicado.
3. Modo A — Dominio público con Let's Encrypt
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy_network"
# 1. Middleware de redirección
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
# 2. Router HTTP (:80) → redirige
- "traefik.http.routers.miapp-http.rule=Host(`app.ejemplo.com`)"
- "traefik.http.routers.miapp-http.entrypoints=web"
- "traefik.http.routers.miapp-http.middlewares=redirect-to-https"
# 3. Router HTTPS (:443) → sirve el contenido
- "traefik.http.routers.miapp-https.rule=Host(`app.ejemplo.com`)"
- "traefik.http.routers.miapp-https.entrypoints=websecure"
- "traefik.http.routers.miapp-https.tls=true"
- "traefik.http.routers.miapp-https.tls.certresolver=letsencrypt"
- "traefik.http.routers.miapp-https.service=miapp-svc"
# 4. Servicio interno
- "traefik.http.services.miapp-svc.loadbalancer.server.port=80"
Requisitos:
- DNS
A/AAAAapuntando a la IP del servidor. - Puertos 80 y 443 abiertos (el challenge HTTP-01 de Let's Encrypt usa el 80).
~/srv/core/data/acme.jsonexistente y con permisos600.
Traefik emite el certificado la primera vez que recibe tráfico HTTPS por ese Host. Para forzarlo:
docker logs -f core-proxyy hacer uncurlal dominio.
Mejorar el router HTTP con challenge sin romper la redirección
- "traefik.http.routers.miapp-http.tls=true"
- "traefik.http.routers.miapp-http.tls.certresolver=letsencrypt"
- "traefik.http.routers.miapp-http.middlewares=redirect-to-https"
Esto permite que Let's Encrypt valide por el puerto 80 aunque todo el tráfico se redirija a HTTPS.
4. Modo B — Dominio local .local con certificado auto-firmado
4.1 Generar el certificado
cd ~/srv/core/data/certs
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout miapp.local.key -out miapp.local.crt \
-subj "/CN=miapp.local" \
-addext "subjectAltName=DNS:miapp.local"
chmod 600 miapp.local.key
Certificado multi-dominio:
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout intranet.local.key -out intranet.local.crt \
-subj "/CN=intranet.local" \
-addext "subjectAltName=DNS:intranet.local,DNS:app.intranet.local,DNS:api.intranet.local"
4.2 Declararlo en el provider file
~/srv/core/data/dynamic_tls.yml (montado en /etc/traefik/dynamic_tls.yml):
tls:
certificates:
- certFile: /etc/traefik/certs/miapp.local.crt
keyFile: /etc/traefik/certs/miapp.local.key
La ruta es la de dentro del contenedor, porque el directorio se monta en
/etc/traefik/certs.
4.3 Etiquetar el proyecto (sin certresolver)
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy_network"
- "traefik.http.routers.miapp.rule=Host(`miapp.local`)"
- "traefik.http.routers.miapp.entrypoints=websecure"
- "traefik.http.routers.miapp.tls=true"
- "traefik.http.services.miapp.loadbalancer.server.port=80"
Recargar:
cd ~/srv/core && docker compose restart traefik
4.4 Resolver el nombre en el cliente
echo "192.168.1.50 miapp.local" | sudo tee -a /etc/hosts
5. Modo C — Solo HTTP (entornos internos)
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy_network"
- "traefik.http.routers.miapp.rule=Host(`miapp.local`)"
- "traefik.http.routers.miapp.entrypoints=web"
- "traefik.http.services.miapp.loadbalancer.server.port=80"
6. Varios dominios en un mismo proyecto
Dos approaches:
a) Múltiples reglas en un router (OR lógico):
- "traefik.http.routers.miapp.rule=Host(`app.local`) || Host(`www.app.local`)"
b) Un router por dominio, apuntando al mismo service:
- "traefik.http.routers.www.rule=Host(`www.app.local`)"
- "traefik.http.routers.www.entrypoints=websecure"
- "traefik.http.routers.www.tls=true"
- "traefik.http.routers.www.service=miapp-svc"
Ambos necesitan los certificados correspondientes en dynamic_tls.yml o en
Let's Encrypt (agregando los dominios a la regla del router con certresolver).
7. Errores frecuentes
| Error | Causa | Solución |
|---|---|---|
404 en todos los dominios |
traefik.enable ausente |
Agregar la etiqueta |
404 solo en un dominio |
Regla Host() mal escrita |
Verificar comillas invertidas: Host(`dominio`) |
502 Bad Gateway |
Puerto interno incorrecto | Confirmar EXPOSE del Dockerfile y el puerto real |
too many redirects |
Router HTTP con redirectscheme sin HTTPS válido |
Verificar que exista el router websecure |
TLS handshake error / no certificate |
dynamic_tls.yml mal cargado o certificado expirado |
Rutas internas correctas + docker compose restart traefik |
cannot generate ACME certificate |
DNS mal, puertos cerrados o acme.json sin permisos |
dig, revisar firewall y chmod 600 acme.json |
| El router no cambia al editar el compose | Etiquetas cacheadas | docker compose up -d --force-recreate |
Verificación:
curl -sI http://miapp.local # 301 → https://miapp.local
curl -sk https://miapp.local -I # respuesta del backend (-k ignora el cert)
8. Cheatsheet de etiquetas
| Etiqueta | Efecto |
|---|---|
traefik.enable=true |
Publica el contenedor |
traefik.docker.network=<red> |
Red desde la que leer etiquetas |
...routers.X.rule=Host(\d`)` |
Enruta por dominio |
...routers.X.rule=PathPrefix(\/api`)` |
Enruta por ruta |
...routers.X.entrypoints=web|websecure |
Puerto de escucha (80 / 443) |
...routers.X.tls=true |
Habilita TLS en ese router |
...routers.X.tls.certresolver=letsencrypt |
Emite/obtiene el certificado |
...routers.X.middlewares=mw1,mw2 |
Aplica middlewares (orden izquierda→derecha) |
...routers.X.service=svc |
Fuerza el service (si hay más de uno) |
...services.X.loadbalancer.server.port=N |
Puerto interno del contenedor |
...middlewares.X.redirectscheme.scheme=https |
Redirige HTTP → HTTPS |
...middlewares.X.redirectscheme.permanent=true |
301 en vez de 302 |
...middlewares.X.basicauth.users=file |
Protección básica |
Autoría: Laboratorio de investigacion en Software Libre Gugler - FCyT.
Distribuido bajo GPL-3.0 (ver LICENSE).