Skip to content

ADR-0009: Secrets vault harness — Infisical self-hosted in-cluster (ns vault), identidades por perfil y contrato ai-hub

Date: 2026-08-31 Status: Accepted (2026-08-31). Lifecycle: el ADR abrió como proposed; el operador decidió el 2026-08-31 dejar Infisical "totalmente operativo internamente por el cluster service endpoint, con el service provider instalado en Kubernetes", lo que sustituye la VM dedicada por un despliegue IN-CLUSTER (namespace vault) y trae el Operator/CRDs v1beta1 a ESTA fase. Esta transición in-PR (front matter, status line, decisiones 1 y 8 y el índice de decisions, actualizados atómicamente, patrón ADR-0008) registra esa decisión del operador con su fecha; el despliegue concreto se materializa y revisa independiente en infra PR #27 (feat/vault-infra).

Contexto

Hoy hay ocho ficheros plaintext bajo ops01:~/secrets/, copias manuales en .env de perfiles, credenciales GHCR de alcance excesivo, dos credenciales expuestas pendientes de revocación (t_b904ba3f), un acceso argocd admin estático y ausencia de trazabilidad de lecturas. El master plan §10 ya seleccionó Infisical self-hosted, SOPS+age únicamente para bootstrap/recovery y el contrato estable vault.get/put/update/delete/list/exists en ai-hub. La validación de licencia, machine identities e integración Kubernetes (§10.6, t_7459a827) estaba en curso al redactar el handoff y es gate obligatorio antes de producción; su veredicto se registra bajo Estado de la validación §10.6.

Decisión

  1. Ejecutar Infisical self-hosted IN-CLUSTER, en el namespace vault de wetopi3 (decisión del operador, 2026-08-31: Infisical totalmente operativo internamente vía el service endpoint del clúster, con el service provider instalado en Kubernetes). Esta decisión SUSTITUYE la VM dedicada fuera del clúster del borrador proposed: el datastore soportado (PostgreSQL + Redis noeviction persistentes) corre en el mismo clúster, con backup cifrado off-host, restauración ensayada y Universal Auth privado (sólo service interno). El trade-off de fallo correlacionado con wetopi3 se acepta explícitamente y se compensa con el contrato de recovery (pg_dump + age ensayado, AC8) — la alternativa de VM dedicada queda registrada como alternativa considerada y descartada por decisión del operador con fecha 2026-08-31.
  2. Publicar vault.wetopi.net detrás del edge aprobado, con TLS LE production y autenticación nativa Infisical siempre obligatoria. La ruta pública sirva SÓLO la UI humana: el API de machine identities (Universal Auth) NO es alcanzable por el hostname público — enforcement verificable en el edge (path-filter con DENY explícito del resto, infra #27). Basic Auth se evalúa sólo como defensa adicional de la UI humana; no puede sustituir la autenticación del vault ni interceptar/romper el API de machine identities. Si no puede separarse con rutas inequívocas o un hostname administrativo distinto, no se activa.
  3. Crear una machine identity distinta por perfil Hermes activo; no se comparte identidad ni bootstrap secret entre perfiles. Política default-deny y privilegios por proyecto/environment/path. El llamador no puede elegir o suplantar otra identidad.
  4. ai-hub es la única interfaz consumida por agentes. Su adaptador traduce el contrato estable a la API versionada de Infisical. Los agentes no importan SDKs, endpoints ni modelos de Infisical.
  5. El único secreto persistente local de cada perfil es su credencial bootstrap de machine identity, en fichero owner-only fuera de Git y fuera de .env; el access token derivado es corto, sólo en memoria y nunca se registra. Client ID/referencias no secretas pueden declararse en configuración.
  6. Infisical es la autoridad runtime de valores, políticas operativas y eventos de auditoría. Git es autoridad L2 de arquitectura, contratos, referencias y policy-as-code sin valores. El registro canónico de perfiles de Hermes determina el inventario de identidades. SOPS+age sólo es autoridad durante bootstrap/recovery declarado; Kanban sólo refleja estado L1 y no contiene secretos.
  7. La migración no copia deuda: toda credencial expuesta, broad-scope, compartida o sin propietario se reemplaza por una de mínimo alcance, se verifica el consumidor y se revoca la anterior. No se importan credenciales expuestas como estado final.
  8. La materialización de Kubernetes Secrets y el Infisical Secrets Operator suceden EN ESTA FASE (decisión del operador, 2026-08-31, que sustituye el "objetivo posterior" del borrador proposed): el Operator v0.11.8 con CRDs v1beta1 (InfisicalAuth + InfisicalStaticSecret; la superficie InfisicalSecret v1alpha1 está deprecated upstream y NO se usa en instalaciones nuevas) se instala junto al vault in-cluster y sincroniza secrets hacia los K8s Secrets de los workloads vía Universal Auth por el endpoint interno.

Alternativas consideradas

  • Infisical dentro de wetopi3: la versión proposed de este ADR (2026-08-31 por la mañana) lo descartó por fallo correlacionado y pérdida de acceso durante recuperación del clúster, y propuso una VM dedicada. El operador reconsideró esa premisa el mismo día (chat 2026-08-31: dejar Infisical totalmente operativo internamente por el cluster service endpoint, service provider instalado en Kubernetes) y la decisión vigente es IN-CLUSTER; el trade-off de fallo correlacionado se mitiga con el contrato de recovery (decisión 1) y queda registrado aquí para trazabilidad.
  • SOPS+age como vault diario: descartado porque no ofrece lectura dinámica por identidad ni auditoría runtime suficiente; se conserva para bootstrap/recovery.
  • API/SDK Infisical directa desde cada agente: descartado por acoplamiento, proliferación de credenciales y semánticas inconsistentes.
  • Una identidad compartida para Hermes/ai-hub: descartada porque impide atribución y amplía el blast radius.
  • Copiar secretos a .env: prohibido como mecanismo normal; sólo referencias no secretas y una credencial bootstrap local por perfil.
  • Edge Basic Auth como control único: descartado; no expresa autorización por secreto y dificulta atribución de machine identities.

Consecuencias y compliance ADR-0003

El vault in-cluster y su backup pasan a ser infraestructura Tier-0; se exige hardening (securityContexts non-root, secretos nunca en Git), monitorización, parcheo y runbook de recuperación. El fallo correlacionado con wetopi3 es un riesgo aceptado explícitamente (decisión 1) — la disponibilidad del vault queda ligada a la del clúster, y el recovery ensayado es el compensatorio obligatorio. Se añade un adaptador ai-hub y una política por identidad, pero los agentes quedan desacoplados. Las autoridades quedan explícitas: Infisical runtime; Git L2; registro Hermes para perfiles; SOPS+age sólo bootstrap/recovery; auditoría append-only para evidencia; Kanban no es fuente de verdad del diseño.

El architecture pack normativo (boundary y topología, namespaces y referencias, contrato vault.*, matriz de identidades y permisos, bootstrap y ciclo de credenciales, rotación, backup y recovery, migración del estado vivo, auditoría, riesgos y diagramas) vive como pill de arquitectura en ARCH-VAULT-SYSTEM; el ADR registra aquí sólo la decisión y sus consecuencias.

Estado de la validación §10.6

El addendum de integración del handoff THINK (que registraba la validación en curso) se sustituye por el veredicto final de la discovery (t_7459a827, 2026-08-31): GO para Infisical self-hosted como vault del harness, con TRES condicionales. Informe completo: findings-infisical-selfhosted.md (adjunto a las cards t_7459a827 y t_e2292367).

Hechos materiales verificados (con fuente oficial)

# Hecho Fuente oficial
1 Licensing: core MIT Expat; directorio ee/ bajo Infisical Enterprise License; activación self-hosted por LICENSE_KEY (online u offline) https://github.com/Infisical/infisical/blob/main/LICENSE · https://github.com/Infisical/infisical/blob/main/backend/src/ee/LICENSE.md · https://infisical.com/docs/self-hosting/ee
2 Machine identities / Universal Auth completo en MIT core: clientId/secret → JWT de corta vida, TTL/renew, rotación y revocación de client secrets, expiry alerts https://infisical.com/docs/documentation/platform/identities/machine-identities · https://infisical.com/docs/documentation/platform/identities/universal-auth
3 Audit logs en self-hosted es feature de pago (Enterprise license) https://infisical.com/docs/documentation/platform/audit-logs
4 Audit log streams (SIEM/Splunk/Datadog) también paid EE https://infisical.com/docs/documentation/platform/audit-log-streams/audit-log-streams
5 Point-in-Time Recovery (rollback de folder/env a snapshot) es paid EE https://infisical.com/docs/documentation/platform/pit-recovery
6 Trusted IPs sobre client secret / access token de identidades es paid EE https://infisical.com/docs/documentation/platform/identities/universal-auth
7 Contrato vault.* mapea 1:1 a la API v4 (/api/v4/secrets*), verificada contra el OpenAPI oficial https://app.infisical.com/api/docs/json
8 Secrets Operator K8s vivo (v0.11.8; CRDs v1beta1 recomendados; InfisicalSecret v1alpha1 deprecated) https://github.com/Infisical/kubernetes-operator/releases · https://infisical.com/docs/integrations/platforms/kubernetes/infisical-static-secret-crd
9 Datastore: PostgreSQL es la única BD soportada y Redis es dependencia dura y persistente (noeviction, AOF/RDB) https://infisical.com/docs/self-hosting/configuration/requirements.md
10 Recovery SOPS+age viable vía pg_dump + age; no existe export cifrado nativo programático https://infisical.com/docs/self-hosting/configuration/requirements.md · https://infisical.com/docs/self-hosting/guides/rotating-the-encryption-key
11 Secret versioning base (MIT) con lectura por ?version=N https://infisical.com/docs/documentation/platform/secret-versioning

Decisión explícita por condicional

  1. Audit logs (EE, paid) — DECISIÓN: NO-GO sin licencia EE aceptada; sin control compensatorio admisible. El audit append-only sin valores es requisito normativo (AC7) y la alternativa a nivel ingress/infraestructura no cubre actor identity, decisión allow/deny ni retención 365 días. Por tanto, la compra de la licencia Enterprise (LICENSE_KEY) que habilita el audit es condición de cierre de S1. Si el operador no la acepta, S1 es NO-GO y el objetivo se detiene antes de S3. Fuente: https://infisical.com/docs/documentation/platform/audit-logs
  2. PIT recovery (EE, paid) — DECISIÓN: control compensatorio; NO compra. El PIT de Infisical (rollback por folder/env a snapshot) no es requisito del contrato: la ruta de recovery normativa es backup/restore SOPS+age vía pg_dump + age con restore drill aislado (AC8), y la lectura de versiones históricas usa el versioning base MIT. PIT queda como mejora opcional futura. Reapertura: si en S7 el restore drill demostrara que la ventana de recuperación exigida no es alcanzable sin PIT, vuelve aquí la decisión — compra de licencia EE o NO-GO, nunca promesa abierta. Fuente: https://infisical.com/docs/documentation/platform/pit-recovery
  3. Trusted IPs (EE, paid) — DECISIÓN: control compensatorio de red; NO compra. La restricción de IP nativa de Universal Auth no se compra: el binding de origen se compensa a nivel de red. Con la decisión de despliegue in-cluster (2026-08-31) la forma del compensatorio es el AISLAMIENTO DE RED DEL SERVICE: Universal Auth sólo es alcanzable por el service interno vault.vault.svc.cluster.local; la ruta pública vault.wetopi.net sirve exclusivamente la UI humana con DENY explícito de los endpoints de API (infra #27), y los StorageClasses PVC no exponen nada a Internet.

NOTA (2026-08-31) — RESUELTO por la decisión del operador: la exposición de Universal Auth quedó decidida como private-network-only in-cluster ( Universal Auth sólo por vault.vault.svc.cluster.local, ruta pública = sólo UI humana con DENY de API; chat del operador 2026-08-31 e infra #27). Este era el OPEN "a resolverse en S0 antes del primer identity bootstrap"; la decisión de arriba y el enforcement de infra #27 lo cierran. La eficacia del control (login público rechazado / login interno autorizado) se demuestra antes del primer bootstrap.

Consecuencia normativa para S1: la etapa exige firma conjunta approver+operador que fije versión/edición/componentes y acepte explícitamente licencia/coste (licencia base + audit EE, según las decisiones de arriba). No se puede degradar AC7 para usar una edición gratuita. Si el condicional audit no queda cubierto por una licencia aceptada, el resultado es NO-GO y se detiene antes de S3.

OPEN explícitos

  • Requisitos exactos de edición/licencia, audit log, Universal Auth, CRUD API, datastore, export y operator: resueltos por el veredicto §10.6 de t_7459a827 (arriba); pendiente la aceptación explícita de licencia/coste por el operador (gate S1).
  • Inventario exacto de perfiles Hermes activos y scopes; se deriva del registro canónico, no de esta conversación.
  • Decisión operador: activar Basic Auth adicional en UI sólo después de prueba de no interferencia y valorar hostname/ruta administrativa separada.
  • Retención 365 días y periodicidad de restore drill: confirmar coste/operación en S1/S2.
  • OBJ-VAULT-001 — el Objective con ACs, dashboard S0-S10, gates y promotion matrix.
  • ADR-0003 — el patrón de fuentes de verdad separadas y explícitas por ciclo de vida (compliance registrado en Consecuencias).