Capa de interoperabilidad · Prueba técnica de arquitectura

5. Seguridad

Conclusión. Una sola identidad para todos los partners (OAuth2 y OIDC con Keycloak), tokens cortos de 5 minutos, autenticación de cliente con llave privada en lugar de secretos compartidos, y autorización en tres niveles: scope por ruta, política por partner y canal, y pertenencia del recurso. El gateway valida el token, la malla lo vuelve a validar y aplica la cuota, y el servicio decide sobre el negocio. La capa no toca datos de tarjeta y guarda los datos personales en un solo servicio, cifrados.

5.1 Flujos OAuth2 y OIDC

Quién Flujo Detalle
Marketplaces y sistemas de partners Client Credentials Máquina a máquina. Un cliente de Keycloak por partner
E-commerce, a nombre de un cliente final Authorization Code con PKCE (Proof Key for Code Exchange, la protección del flujo para aplicaciones que no pueden guardar un secreto) El backend del e-commerce llama a la capa con su propio cliente; la identidad del comprador viaja como customerId
Servicios internos Sin tokens de usuario Identidad de carga de trabajo por mTLS (mutual TLS: ambos extremos se autentican con certificado) de la malla

Flujo OAuth2 de un partner

El partner se autentica con private_key_jwt: firma con su llave privada y la capa conoce solo la pública. Así no hay secreto compartido que robar. Para partners que no puedan, secreto de cliente con rotación obligatoria cada 90 días. Trade-off: más fricción de alta para el partner (supuesto S16), a cambio de eliminar la credencial que más se filtra.

El token es un JWT (JSON Web Token: token firmado que lleva los datos de quien llama, llamados claims):

Claim Ejemplo Uso
iss https://auth.andesretail.com/realms/andesretail Validación
aud interop-api Validación
exp 5 minutos Validación
azp partner-mkp-andino Cliente
partner_id mkp-andino Aislamiento de datos y cuota
channels ["mkp-andino"] Canales permitidos
tier gold Nivel de cuota
scope stock:read prices:read orders:write orders:read Autorización por ruta

5.2 Qué se valida en el gateway y qué en los servicios

Validación Gateway Sidecar (malla) Servicio
TLS 1.2 o superior Sí (borde) mTLS interno
Firma del JWT, iss, aud, exp Sí Sí, de nuevo
Scope exigido por la ruta Sí Sí
Tope general y por cliente Sí
Cuota por partner y nivel Sí
Solo el gateway puede llamar al servicio Sí (política por identidad de carga)
Tamaño y tipo de contenido Sí
Esquema del cuerpo Sí
El recurso pertenece al partner_id Sí
Canal, tienda y modo de cobro permitidos Sí
Reglas de negocio Sí

Un sidecar es un contenedor que acompaña a cada servicio y le aporta capacidades sin tocar su código; aquí es el proxy Envoy de Istio. Por qué validar el token dos veces: si alguien alcanza un servicio sin pasar por el gateway, el servicio no confía en cabeceras sino en el token. Los servicios leen partner_id del token validado por su sidecar, nunca de una cabecera puesta por el cliente. Trade-off: unos milisegundos más por salto (dentro del presupuesto de la sección 6.2) a cambio de no tener un perímetro único.

5.3 Autorización por roles, scopes, partner y canal

  1. Scope por ruta: qué operaciones puede invocar (stock:read, prices:read, orders:write, orders:read, webhooks:manage). Ninguna ruta del gateway existe sin scope: lo exige el pipeline (F5).
  2. Política por partner y canal, como configuración versionada: canales, tiendas, listas de precios, modos de cobro, vencimiento de reserva y tope visible de stock.
  3. Pertenencia del recurso: toda consulta a la base filtra por partner_id. Una orden ajena responde 404.

Roles internos para operadores de AndesRetail en Keycloak: support-read (consultar órdenes y entregas de webhooks) y ops-admin (reprocesar colas de fallidos, cambiar cuotas). Toda acción de operador queda en auditoría.

5.4 Rotación de llaves, expiración y secretos

Elemento Vigencia Rotación
Token de acceso 5 minutos No se revoca: vence. Sin tokens de refresco en Client Credentials
Llaves de firma de Keycloak 90 días Con solapamiento: la llave nueva se publica en el JWKS (el documento público de llaves, ver abajo) antes de usarse; la vieja sigue válida hasta que venzan sus tokens
Llaves de los partners (private_key_jwt) 12 meses El partner registra la nueva antes de retirar la vieja
Secreto de cliente (si aplica) 90 días Dos secretos válidos durante el cambio
Secreto de firma de webhooks 12 meses o a demanda Doble firma durante el cambio
Credenciales hacia ERP, WMS, OMS, CRM, pasarela, Kafka 90 días Automática en el gestor de secretos
Certificados mTLS de la malla 24 horas Automática por Istio

El JWKS (JSON Web Key Set) es el documento público con las llaves con las que se verifica la firma de los tokens; el gateway y los sidecars lo cachean y lo refrescan ante una llave desconocida.

Estrategia de secretos:

Trade-off: la rotación toma efecto en el siguiente sondeo del driver y solo si el servicio relee el archivo; por eso el contrato de todo servicio exige leer configuración y secretos de archivos. Se descartó Vault de HashiCorp para no sumar un proyecto de operación (alta disponibilidad, desellado, respaldo); el cambio a Vault solo tocaría la configuración del driver, no el servicio.

5.5 Datos personales y de pago

5.6 Modelo de amenazas

Se documentan 9 amenazas; aquí van las 6 principales y las otras 3 en el anexo A.

# Amenaza Mitigación
A1 Robo o reutilización de un token Vigencia de 5 minutos, private_key_jwt, TLS, audiencia fija, detección de uso anómalo por partner
A2 Un partner accede a órdenes o precios de otro (BOLA, broken object level authorization: falla al comprobar que el objeto pedido es de quien lo pide) Filtro por partner_id en toda consulta, 404 para recursos ajenos, pruebas automáticas de aislamiento entre partners (F7)
A3 Webhook de pago falsificado ("pago aprobado" inventado) Implementado: verificación de firma de la pasarela, deduplicación, y confirmación contra la API de la pasarela antes de confirmar la orden. Diseño, sin implementar en el demo: lista de IP de origen
A4 Abuso o denegación de servicio: scraping de precios, ráfagas Cuota por partner y nivel, tope general, microcache, Cloudflare en el borde, protección del legado por diseño
A5 Órdenes duplicadas o repetición de peticiones Idempotencia con dos barreras, deduplicación de eventos (F9)
A6 Fuga de datos personales por logs, eventos o trazas Sin datos personales en eventos, enmascarado en logs, cifrado por campo, pruebas que fallan si aparece un dato personal en la telemetría (F8)

5.7 Auditoría

Diseño: toda petición externa de escritura, todo cambio de estado de orden, reserva y pago, toda acción de operador y todo fallo de autenticación o autorización genera un registro en el tópico andes.audit.v1: quién (partner_id, cliente o operador), qué, cuándo, resultado y correlationId, y se archiva en almacenamiento de objetos con bloqueo contra modificación.

Implementado hoy: order-service publica el registro, con ese contenido y por outbox, en cuatro acciones (crear orden, confirmarla, reembolsar el pago y cancelarla). Todavía no auditan inventory-service (reservas), payment-service (pagos), webhook-service (alta y baja de suscripciones) ni el borde (fallos de autenticación o autorización), y el archivo con bloqueo contra modificación no está construido. Completar esos tres productores y el archivo es trabajo del roadmap (sección 8.3). Trade-off: costo de almacenamiento que crece sin límite, a cambio de poder reconstruir cualquier disputa con un partner.

5.8 Acceso de los operadores al clúster

La API de Kubernetes de EKS es solo privada: no tiene dirección pública, ni siquiera limitada a una IP. Se entra por un túnel de AWS Systems Manager (SSM, el servicio de AWS para administrar instancias) hacia un bastión: una máquina mínima (t4g.nano) dentro de la red privada, sin llave SSH, sin IP pública y sin reglas de entrada, con un rol que solo le permite hablar con SSM. El bastión no tiene permisos sobre Kubernetes: solo reenvía bytes, y quien se autentica ante la API es kubectl, en el equipo del operador, con su propio rol. Hay dos contextos: administrador (abre el túnel y despliega) y solo lectura, que ve todo menos los Secrets de Kubernetes y es el que se muestra en vivo. La verificación del certificado de la API se mantiene (el cliente valida el nombre del endpoint de EKS aunque se conecte a localhost); nunca se desactiva. CloudTrail registra quién abrió cada sesión.

Se descartaron el endpoint público limitado por IP (la API de un clúster con secretos expuesta a internet aunque esté filtrada, y una variable que cambiar cada vez que cambia la red del operador), el EC2 Instance Connect Endpoint (túneles de una hora como máximo y apuntados a una IP que en EKS cambia) y AWS Client VPN (veinte veces el costo del bastión, con certificados que administrar). Trade-off: un recurso más que cobra por hora (unos 0,005 dólares), que depende del NAT y que puede fallar; todo lo que habla con la API necesita el túnel abierto. Como respaldo, y solo por decisión explícita, el endpoint público limitado a una IP se vuelve a encender con una variable (nunca abierto a todo internet). Fuente: ADR-027, que reemplaza ese punto de ADR-022.