API Gateway Pattern¶
El API Gateway es el punto de entrada único a la plataforma: todo el tráfico externo pasa por él antes de llegar a Keycloak o a las APIs protegidas. En esta plataforma, Traefik v3 desempeña ese rol — gestiona la terminación TLS, el enrutamiento dinámico por labels de Docker Compose, los middlewares de seguridad y el passthrough de JWTs hacia los servicios de upstream. Centralizar estas responsabilidades en Traefik permite que cada microservicio permanezca enfocado en lógica de negocio sin reinventar infraestructura de red transversal.
Responsabilidades de Traefik como API Gateway¶
| Responsabilidad | Componente Traefik | Detalle |
|---|---|---|
| Terminación TLS | tls + Let's Encrypt ACME |
Certificados automáticos para todos los dominios |
| Enrutamiento | routers con reglas Host y PathPrefix |
Separa tráfico hacia Keycloak, APIs y BFF |
| Rate limiting | Middleware rateLimit |
Protege endpoints públicos de abuso |
| Headers de seguridad | Middleware headers |
HSTS, CSP, X-Frame-Options |
| JWT passthrough | Sin validación en Traefik | El JWT viaja intacto hasta el servicio de destino |
| Observabilidad | Access logs + métricas Prometheus | Visibilidad completa del tráfico de entrada |
| Health checks | healthCheck en los servicios |
Traefik deja de enrutar a instancias no saludables |
JWT en el API Gateway
Traefik en esta plataforma no valida JWTs. La validación ocurre en cada microservicio Spring WebFlux mediante spring-boot-starter-oauth2-resource-server. Este enfoque evita centralizar lógica de autorización en infraestructura y permite que cada servicio aplique sus propias reglas de scopes y roles.
Topología de red¶
flowchart TB
Internet([Internet]) -->|443 HTTPS| T[Traefik v3\nAPI Gateway]
T -->|id.empresa.com| KC[Keycloak 26.x]
T -->|api.empresa.com/partners| PAPI[partner-api\nSpring WebFlux]
T -->|api.empresa.com/internal| IAPI[internal-api\nSpring WebFlux]
T -->|portal.empresa.com| BFF[partner-portal BFF]
KC -->|JDBC| PG[(PostgreSQL 16)]
PAPI -. valida JWKS .-> KC
IAPI -. valida JWKS .-> KC
subgraph Docker network: platform-net
KC
PAPI
IAPI
BFF
PG
end
Traefik es el único contenedor con los puertos 80 y 443 expuestos al host. El resto de los contenedores se comunican internamente a través de platform-net sin exposición directa a Internet.
Configuración estática (traefik.yml)¶
# traefik.yml — configuración estática
api:
dashboard: true
insecure: false # dashboard solo accesible vía router autenticado
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: ops@empresa.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: platform-net
log:
level: INFO
accessLog:
filePath: /var/log/traefik/access.log
format: json
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
Configuración dinámica — labels en compose.yml¶
Router para Keycloak¶
# compose.yml — servicio keycloak (fragmento de labels)
labels:
- "traefik.enable=true"
# Router principal
- "traefik.http.routers.keycloak.rule=Host(`id.empresa.com`)"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.routers.keycloak.tls.certresolver=letsencrypt"
- "traefik.http.routers.keycloak.middlewares=keycloak-headers,keycloak-ratelimit"
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
# Middleware: cabeceras de seguridad
- "traefik.http.middlewares.keycloak-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.keycloak-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.keycloak-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.keycloak-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.keycloak-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.keycloak-headers.headers.frameDeny=true"
# Middleware: rate limiting
- "traefik.http.middlewares.keycloak-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.keycloak-ratelimit.ratelimit.burst=30"
- "traefik.http.middlewares.keycloak-ratelimit.ratelimit.period=1m"
Router para una API protegida¶
# compose.yml — servicio partner-api (fragmento de labels)
labels:
- "traefik.enable=true"
- "traefik.http.routers.partner-api.rule=Host(`api.empresa.com`) && PathPrefix(`/partners`)"
- "traefik.http.routers.partner-api.entrypoints=websecure"
- "traefik.http.routers.partner-api.tls.certresolver=letsencrypt"
- "traefik.http.routers.partner-api.middlewares=api-headers,api-ratelimit,strip-partners-prefix"
- "traefik.http.services.partner-api.loadbalancer.server.port=8081"
- "traefik.http.services.partner-api.loadbalancer.healthcheck.path=/actuator/health"
- "traefik.http.services.partner-api.loadbalancer.healthcheck.interval=15s"
# Strip prefix para que el servicio reciba /... sin /partners
- "traefik.http.middlewares.strip-partners-prefix.stripprefix.prefixes=/partners"
# Rate limiting más estricto para APIs de datos
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=200"
- "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.api-ratelimit.ratelimit.period=1m"
Forwarded headers y Keycloak¶
Traefik se coloca delante de Keycloak, por lo que Keycloak debe confiar en los headers X-Forwarded-For y X-Forwarded-Proto para generar URLs correctas en los tokens y en los redirects OIDC:
# En el servicio Keycloak en compose.yml — variables de entorno
environment:
KC_PROXY_HEADERS: xforwarded
KC_HOSTNAME: id.empresa.com
KC_HOSTNAME_STRICT: "true"
KC_HOSTNAME_STRICT_HTTPS: "true"
# En traefik.yml — para que Traefik añada los headers correctamente
entryPoints:
websecure:
forwardedHeaders:
trustedIPs:
- "127.0.0.1/32"
- "172.16.0.0/12" # rango Docker
Middlewares de autenticación opcionales¶
Para rutas de administración (panel de Traefik, métricas de Prometheus), se puede añadir Basic Auth como primera línea de defensa antes de que el tráfico llegue al servicio:
# Middleware BasicAuth para el dashboard de Traefik
labels:
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$xyz...$$hash"
- "traefik.http.routers.traefik-dashboard.middlewares=traefik-auth"
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.empresa.com`)"
Nunca expongas el dashboard de Traefik sin autenticación
El dashboard expone la configuración completa de routers, middlewares y servicios. En producción, protégelo con Basic Auth o, mejor, restringe el acceso a una IP de administración específica usando ClientIP.
Diferencia entre API Gateway y BFF¶
flowchart LR
B[Browser] --> T[Traefik\nAPI Gateway]
T --> BFF[BFF\nSpring WebFlux]
T --> API[API\nSpring WebFlux]
BFF --> KC[Keycloak]
BFF --> API
API -. JWKS .-> KC
- Traefik opera en la capa de red/transporte: enruta, protege el perímetro y termina TLS. No tiene estado de usuario.
- BFF opera en la capa de aplicación: mantiene sesión de usuario, agrega datos y adapta respuestas al cliente específico.
Ambos son complementarios — Traefik enruta hacia el BFF, y el BFF usa los tokens de Keycloak para llamar a las APIs con autorización.
Monitorización con Prometheus + Grafana
Traefik expone métricas en /metrics en el puerto 8082 (configurable). Combina estas métricas con las de Keycloak y las de Spring Boot Actuator en un dashboard de Grafana para tener visibilidad end-to-end de la plataforma de identidad.