HTTPS y proxy inverso
Ver en MarkdownEl contenedor web escucha en HTTP sin cifrar (puerto 8080 por defecto). Pon delante un proxy inverso que termine el TLS. Todo pasa por web: el proxy solo necesita un destino y nunca habla directamente con api ni con gateway.
Qué tiene que hacer el proxy
Sección titulada «Qué tiene que hacer el proxy»| Ruta | Tráfico | Requisito del proxy |
|---|---|---|
/ |
Interfaz estática | Nada especial. |
/api/ |
API REST y eventos del servidor (progreso en directo de altas y trabajos) | No guardar las respuestas en búfer. Permitir conexiones inactivas de al menos 1 hora. |
/gateway/ |
WebSocket de la terminal web | Pasar las cabeceras Upgrade y Connection. Permitir conexiones de hasta 9 horas (el gateway cierra las sesiones inactivas a los 15 minutos y cualquier sesión a las 8 horas). |
Además:
- Pon en
INFRAWARD_PUBLIC_URL(en.env) la URL pública exacta, por ejemplohttps://infraward.example.com, y ejecutadocker compose up -ddespués de cambiarla. La API compara con ella la cabeceraOriginde las escrituras y el comando de alta se construye a partir de ella. - Publica
websolo en la interfaz de loopback (INFRAWARD_HTTP_PORT=127.0.0.1:8080) cuando el proxy esté en el mismo host, para que el puerto 8080 no sea accesible desde la red. - Envía
X-Forwarded-ForyX-Forwarded-Proto, y pon enINFRAWARD_TRUSTED_PROXY(en.env) la dirección IP (o CIDR) del proxy tal como la ve el contenedorweb. El contenedorwebsolo se cree esas cabeceras si llegan desde esa dirección, y pasa a la API y al gateway una única IP del cliente. La usan el registro de auditoría, los límites de intentos de login y las sesiones de la terminal. Mientras no la definas, registran la IP del proxy. Consulta Averigua la dirección del proxy. - No escribas en el log de accesos del proxy las peticiones a
/api/enroll/: el token de alta, de un solo uso, viaja en la ruta. El contenedorwebya las excluye de su propio log. - El contenedor
webya envíaX-Frame-Options,Content-Security-Policy: frame-ancestors 'none',Strict-Transport-Security,X-Content-Type-OptionsyReferrer-Policy. No las vuelvas a añadir en el proxy.
Ejemplos
Sección titulada «Ejemplos»Caddy obtiene y renueva el certificado automáticamente y gestiona la actualización a WebSocket sin ajustes adicionales.
infraward.example.com { # Caddy no escribe logs de accesos salvo que actives la directiva log. # Si lo haces, excluye /api/enroll/*: el token de alta va en la ruta. reverse_proxy 127.0.0.1:8080 { # Eventos del servidor: envía cada evento en cuanto llega. flush_interval -1 }}server { listen 80; server_name infraward.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl; http2 on; server_name infraward.example.com;
ssl_certificate /etc/letsencrypt/live/infraward.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/infraward.example.com/privkey.pem;
proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
location / { proxy_pass http://127.0.0.1:8080; }
# Script de alta: el token de un solo uso va en la ruta, fuera del log. location /api/enroll/ { proxy_pass http://127.0.0.1:8080; access_log off; }
# API REST y eventos del servidor. location /api/ { proxy_pass http://127.0.0.1:8080; proxy_buffering off; proxy_read_timeout 1h; }
# Terminal web (WebSocket). location /gateway/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_buffering off; proxy_read_timeout 9h; proxy_send_timeout 9h; }}Conecta web a la red de Traefik y declara el router con etiquetas en compose.override.yml (añade el fichero a COMPOSE_FILE en .env). Quita el puerto publicado para que solo Traefik llegue al contenedor.
services: web: ports: !reset [] networks: [default, traefik] labels: traefik.enable: "true" traefik.docker.network: traefik traefik.http.routers.infraward.rule: Host(`infraward.example.com`) traefik.http.routers.infraward.entrypoints: websecure traefik.http.routers.infraward.tls.certresolver: letsencrypt traefik.http.services.infraward.loadbalancer.server.port: "8080"
networks: traefik: external: trueTraefik reenvía la actualización a WebSocket y transmite las respuestas sin búfer por defecto. Si has cambiado los respondingTimeouts del entrypoint, comprueba que no cortan las conexiones largas.
Averigua la dirección del proxy
Sección titulada «Averigua la dirección del proxy»El valor de INFRAWARD_TRUSTED_PROXY es la dirección desde la que llegan las conexiones dentro del contenedor, que no siempre es la que esperas:
- Proxy en el mismo host (Caddy o nginx conectando a
127.0.0.1:8080): con Docker suele ser la puerta de enlace de la red de Compose, no127.0.0.1. - Proxy en un contenedor (Traefik): la IP del contenedor del proxy en la red que comparte con
web, o el CIDR de esa red si solo hay contenedores de proxy conectados a ella.
Para averiguarla, abre InfraWard a través del proxy y lee la dirección de origen de las peticiones en el log de web:
docker compose logs --tail 5 webDefínela en .env y vuelve a crear web:
INFRAWARD_TRUSTED_PROXY=172.18.0.1docker compose up -d webA partir de ahí, el mismo log muestra las IP reales de los clientes. El valor por defecto (127.0.0.1) no confía en ningún proxy. No uses nunca un rango desde el que los clientes puedan conectarse directamente: cualquiera dentro de él podría elegir la IP que aparece en la auditoría.
Comprueba el proxy
Sección titulada «Comprueba el proxy»- Abre
https://infraward.example.comy entra. Si la página de login carga pero al entrar vuelves a ella, el navegador está descartando la cookie de sesión: comprueba que el sitio se sirve por HTTPS. - Da de alta un servidor o lanza un escaneo y mira la página de progreso. Si los pasos solo se actualizan al recargar, el proxy está guardando
/api/en búfer. - Abre una terminal en un servidor con acceso de administración. Si muestra enseguida Se ha perdido la conexión con el gateway, el proxy no está reenviando la actualización a WebSocket en
/gateway/. - Busca tu acceso en Ajustes → Auditoría. Si muestra la IP del proxy en lugar de la tuya,
INFRAWARD_TRUSTED_PROXYno está definida o no coincide con el proxy.