C4 — Contenedores¶
El nivel de contenedores muestra los procesos ejecutables y los almacenes de datos que componen el sistema. Cada contenedor es un proceso desplegable de forma independiente; en el MVP todos corren como contenedores Docker en un mismo VPS.
Diagrama de contenedores¶
flowchart TD
Browser([Navegador\nHTML / JS])
M2M([Servicio M2M\nJava / Python / etc.])
subgraph VPS["VPS Ubuntu — Docker Compose"]
subgraph frontend["Red Docker: frontend"]
T["Traefik v3\nHTTPS :443 · HTTP :80\nReverse proxy + TLS"]
K["Keycloak 26\nHTTP :8080 (interno)\nIAM / OAuth2 / OIDC"]
W["Spring WebFlux\nHTTP :8081 (interno)\nResource Server"]
end
subgraph data["Red Docker: data"]
K
PG["PostgreSQL 16\nTCP :5432 (interno)\nPersistencia de Keycloak"]
end
end
Browser -->|HTTPS 443| T
M2M -->|HTTPS 443| T
T -->|HTTP interno| K
T -->|HTTP interno| W
K -->|TCP 5432 SQL| PG
W -. "GET /.well-known/jwks.json (caché 5 min)" .-> K
Tabla de contenedores¶
| Contenedor | Imagen | Red(es) | Puerto externo | Responsabilidad |
|---|---|---|---|---|
| Traefik | traefik:v3 |
frontend | 80, 443 | TLS termination, routing por Host, rate limiting, security headers |
| Keycloak | quay.io/keycloak/keycloak:26 |
frontend + data | — (solo interno) | IAM: autenticación, autorización, emisión de JWT, gestión de realms |
| PostgreSQL | postgres:16-alpine |
data | — (solo interno) | Persistencia de todos los datos de Keycloak: usuarios, sesiones, claves, realms |
| Spring WebFlux | Imagen propia | frontend | — (solo interno) | Resource Server: valida JWT, expone APIs de negocio protegidas |
Comunicaciones entre contenedores¶
| Origen | Destino | Protocolo | Propósito |
|---|---|---|---|
| Traefik | Keycloak | HTTP (interno Docker) | Proxy de requests de autenticación y API admin |
| Traefik | Spring WebFlux | HTTP (interno Docker) | Proxy de requests a las APIs de negocio |
| Keycloak | PostgreSQL | TCP / SQL | Lectura y escritura de datos de identidad |
| Spring WebFlux | Keycloak | HTTP (JWKS endpoint) | Descarga y refresco de claves públicas para validar JWT |
Sin comunicación directa entre aplicaciones
Spring WebFlux nunca llama directamente a Keycloak para validar tokens. La validación es local usando la clave pública descargada del JWKS endpoint. Esto elimina latencia y dependencia en tiempo real con Keycloak para cada request.
Variables de entorno clave¶
# compose.yml (fragmento)
keycloak:
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: ${KC_DB_USER}
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_HOSTNAME: ${KC_HOSTNAME}
KC_PROXY: edge
KC_HTTP_ENABLED: "true"
KEYCLOAK_ADMIN: ${KC_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
spring-webflux-app:
environment:
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: >
https://${KC_HOSTNAME}/realms/realm-internal
Flujo completo de autenticación¶
sequenceDiagram
actor B as Navegador
participant T as Traefik
participant K as Keycloak
participant W as Spring WebFlux
participant PG as PostgreSQL
B->>T: GET /app (sin sesión)
T->>B: 302 Redirect → Keycloak /auth
B->>T: GET /realms/realm-internal/protocol/openid-connect/auth?...
T->>K: Proxy
K->>B: Mostrar formulario de login
B->>K: POST credenciales + code_verifier
K->>PG: Verificar usuario y credenciales
PG-->>K: Usuario válido
K->>B: 302 Redirect con authorization_code
B->>K: POST /token (code + code_verifier)
K-->>B: access_token + id_token + refresh_token
B->>T: GET /api/resource Bearer: access_token
T->>W: Proxy (token en header)
W->>K: GET /jwks (cacheado)
K-->>W: Claves públicas RSA
W->>W: Verificar firma + claims
W-->>B: 200 OK + datos