Saltar a contenido

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.