ADR-022: infraestructura del demo en AWS con OpenTofu
Aceptado el 15/11/2026 por Andrés (propuesto el 10/11/2026 por el carril `infra`; Andrés revisa además el plan antes de cada apply). Reemplazado en parte por ADR-027 (endpoint de la API de Kubernetes). Complementado por ADR-024 y por ADR-030 (fila 8: la entrada sigue con `Gateway` y `VirtualService` de Istio).
Nota del 14/11/2026: el endpoint de la API de Kubernetes ya no es público. ADR-027 lo reemplaza (solo privado, acceso por túnel de Session Manager); el público limitado por IP queda como respaldo. El resto de este ADR sigue igual.
Contexto
F7 pide red, EKS mínimo, complementos (Istio, Secrets Store CSI Driver, EKS Pod Identity), AWS Secrets Manager y los subdominios de docs/demo/02-subdominios-e-interfaz-de-sistemas.md en Cloudflare. El demo se enciende para validar y para la presentación, y se destruye después (docs/plataforma/04-stack.md). Se escribe el código ahora, sin credenciales, y se aplica después.
Restricciones: costo mínimo por hora, apagado fácil, ningún secreto en el repositorio ni en el estado si se puede evitar, y el mismo diseño de seguridad que el resto del proyecto (ADR-008, ADR-009, ADR-016).
Decisiones
| # | Tema | Decisión | Alternativa descartada y por qué |
|---|---|---|---|
| 1 | Raíces | Cinco raíces con estado propio: base, cluster, plataforma, dns, auditoria. Se buscan entre sí por nombre y etiquetas, no leyendo el estado de otra |
Una sola raíz: los proveedores de Helm y Kubernetes dependen de un clúster que aún no existe en el primer plan, y apagar el clúster obligaría a tocar DNS y secretos |
| 2 | Estado | Local, ignorado por git y cifrado por OpenTofu (AES-GCM, llave derivada de una frase con PBKDF2), obligatorio también para el plan | S3 con bloqueo: un recurso más que crear antes de todo; con una sola persona aplicando no aporta. Queda como paso si el demo pasa a un pipeline |
| 3 | Valores de secretos | Atributos de solo escritura (secret_string_wo) y claves aleatorias efímeras: los valores no quedan en el estado ni en el plan. Las claves externas (Confluent, Stripe) entran por una variable efímera |
Cargar los valores con la CLI de AWS: no está instalada y deja el paso fuera del código. Valores normales: quedarían en el estado |
| 4 | Secretos por carga | Un secreto JSON por carga, un rol de IAM por carga que solo lee el suyo, asociado por EKS Pod Identity a su cuenta de servicio | Un rol compartido: cualquier servicio leería las claves de otro |
| 5 | Complementos | Todo lo posible como complemento administrado de EKS, en su versión por defecto para la versión de Kubernetes: vpc-cni, coredns, kube-proxy, eks-pod-identity-agent, aws-ebs-csi-driver y aws-secrets-store-csi-driver-provider (instala el driver y el proveedor de AWS, con rotación encendida) |
Charts de Helm: más versiones que fijar y requieren acceso al clúster para instalarse |
| 6 | Istio | Charts base, istiod y gateway por Helm en la raíz plataforma, con pedidos de memoria recortados. Las políticas de la malla quedan en platform/k8s (F6) |
Istio en modo ambient: descartado en ADR-008 |
| 7 | Entrada | El gateway de entrada de Istio pide un NLB (Network Load Balancer, balanceador de red de AWS) al controlador de servicios que trae EKS. El NLB solo acepta los rangos de Cloudflare y solo el puerto 443 | AWS Load Balancer Controller: un componente más con su rol y su chart, sin ganancia para un solo balanceador. Cloudflare Tunnel: quita el NLB, pero es una pieza que el diseño no prevé |
| 8 | TLS de origen | Certificado de origen de Cloudflare (modo Full strict) en un Secret de istio-ingress. Su llave queda en el estado cifrado de plataforma |
cert-manager con Let's Encrypt: Cloudflare ya termina el TLS del público; el origen solo necesita confiar en Cloudflare |
| 9 | DNS y Access | Solo los nueve subdominios del clúster (api, hooks, auth, erp, wms, oms, crm, grafana, lab), con proxy. La tienda, www, docs y marketplace son de Pages y del otro frente. Access protege erp, wms, oms, crm, grafana y lab; la ruta de la API de cada sistema queda fuera de Access para que el modo AS-IS funcione, y en TO-BE la cierra Istio |
Access sobre todo el host: el modo AS-IS dejaría de responder a k6 |
| 10 | Tamaño | Dos zonas, un NAT Gateway, dos nodos m7i-flex.xlarge bajo demanda (32 GiB en total). SPOT es una variable para las corridas de validación |
Tres zonas y un NAT por zona: lo de producción, el triple de costo fijo. Graviton: más barato, pero obliga a construir imágenes para ARM |
| 11 | Repositorios | Amazon ECR con etiquetas inmutables, escaneo al subir y retención de 10 imágenes | Docker Hub: límites de descarga y otra credencial |
| 12 | Apagado | Interruptores por raíz (cluster_enabled, node_count, ingress_enabled, dns_enabled) y make infra-off, que destruye dns, plataforma y cluster en ese orden. make infra-audit falla si queda algo cobrando, incluido lo que crea el clúster y no OpenTofu (NLB y discos) |
Confiar en tofu destroy: no ve el NLB ni los discos de los PersistentVolumeClaim, que quedan huérfanos y cobrando |
| 13 | Presupuesto | Dos presupuestos de AWS Budgets (mensual y diario) en base, que se aplica antes que nada |
Solo mensual: avisa tarde de un clúster olvidado un fin de semana |
| 14 | Análisis de seguridad | Trivy sobre infra/tofu en make verify; falla con cualquier hallazgo. Tres excepciones escritas en el código con su razón: endpoint público de EKS limitado a rangos, logs del plano de control solo audit y authenticator, registro de flujos de la VPC apagado. Las dos últimas por costo, con interruptor |
Checkov: requiere Python y un entorno virtual; Trivy es un binario que se baja y valida como las demás herramientas |
Qué se sacrifica
- Un solo NAT: si su zona cae, los nodos de la otra pierden salida a internet.
- El endpoint de la API de Kubernetes es público, limitado a la IP de Andrés. Cambia la IP, cambia la variable.
- La llave del certificado de origen vive en el estado. Está cifrado, pero perder la frase deja el estado ilegible y obliga a importar o recrear.
- El controlador de servicios de EKS es el camino antiguo para crear balanceadores. Si AWS lo retira, se pasa al AWS Load Balancer Controller.
- Los nombres de espacios de nombres y cuentas de servicio del catálogo (
infra/tofu/modules/catalogo) los tienen que respetar los charts de F6.
Lo que se verifica al tener credenciales
Lo que no se puede comprobar sin cuenta va como pendiente en infra/tofu/README.md: el esquema de configuración de los complementos de EKS, la etiqueta que el controlador pone al NLB y el primer tofu plan de cada raíz.