Skip to content

ARCH-VAULT-SYSTEM: Vault system — especificación normativa (boundary, contrato, identidades, operación)

Especificación técnica del sistema de secrets/vault del harness (humanos + perfiles Hermes). Contenido normativo del handoff THINK (ADR-0008, rol architect); la decisión formal y sus alternativas viven en ADR-0009. No contiene valores secretos.

Boundary y topología

flowchart LR
  H[Operador] -->|TLS + auth nativa| E[Edge LE production]
  E -->|UI humana SOLAMENTE; DENY de API| I[Infisical in-cluster ns vault]
  P[Perfil Hermes] -->|vault.*| A[Adaptador ai-hub local]
  A -->|Universal Auth, service interno| I
  OP[Operator K8s v0.11.8] -->|Universal Auth, service interno| I
  OP -->|sync v1beta1| KS[K8s Secrets workloads]
  I --> D[(PostgreSQL + Redis in-cluster)]
  D --> B[Backup cifrado off-host]
  I --> L[Audit sink append-only off-host]
  R[SOPS+age recovery bundle] -. sólo desastre declarado .-> I

AJUSTE 2026-08-31 — decisión del operador (ADR-0009 proposed → accepted in-PR): el Infisical corre IN-CLUSTER (namespace vault de wetopi3) con PostgreSQL + Redis persistentes en el mismo namespace y el Infisical Secrets Operator v0.11.8 instalado en el clúster; la versión proposed de este pack colocaba el servicio en una VM dedicada fuera del clúster. Universal Auth es sólo alcanzable por el service interno vault.vault.svc.cluster.local; la ruta pública vault.wetopi.net publica exclusivamente la UI humana con DENY explícito de los endpoints de API. El trade-off de fallo correlacionado con wetopi3 es aceptado y compensado por el contrato de recovery (backup cifrado off-host + restore drill ensayado). El despliegue se fija por versión/digest y documenta upgrade/rollback (materialización: infra PR #27, feat/vault-infra).

Namespaces y referencias

Referencia lógica: vault://<project>/<environment>/<path>#<key>. project, environment y path provienen de allowlists; se normalizan y no admiten traversal, comodines aportados por el llamador ni selección arbitraria de organización. Los valores nunca aparecen en URI, argumentos CLI, Git, Kanban, métricas o logs. La configuración de un consumidor contiene sólo la referencia.

Contrato vault.*

Respuesta común: request_id, operation, referencia canónica redacted, versión/etag cuando exista y resultado tipado; jamás eco del valor salvo el campo de retorno de get al proceso autorizado. Errores estables: INVALID_REFERENCE, FORBIDDEN, NOT_FOUND, ALREADY_EXISTS, VERSION_CONFLICT, UPSTREAM_UNAVAILABLE, RATE_LIMITED. No se traduce FORBIDDEN en detalle que permita enumerar nombres.

Operación Semántica obligatoria Efectos / protección
get(ref) lectura exacta de un valor; no fallback entre environments devuelve el valor sólo al proceso llamador y metadatos mínimos; redacción integral en trazas
exists(ref) comprobación exacta devuelve booleano sólo dentro del scope; fuera de scope responde FORBIDDEN, no revela existencia
list(prefix, cursor, limit) lista nombres/metadatos, nunca valores paginada, prefix allowlisted, sin búsqueda global ni recursive implícito
put(ref, value, metadata) create-only falla ALREADY_EXISTS; requiere permiso write y límites de payload; valor por stdin/memoria, no argv
update(ref, value, expected_version) reemplazo de existente falla NOT_FOUND o VERSION_CONFLICT; precondición obligatoria evita lost update
delete(ref, expected_version) elimina una versión lógica existente deshabilitado por defecto; scope específico, precondición y gate humano/approver; recovery probado antes

Todo intento permitido o denegado emite audit event sin valor. Retries sólo para operaciones idempotentes; put/update/delete usan request-id/idempotency y no repiten a ciegas. Timeouts y circuit breaker evitan bloquear Hermes. El adaptador nunca mantiene una supercredencial compartida: resuelve la identidad del perfil autenticado a una credencial local fija; el perfil no pasa identity=.

Matriz de identidades y permisos máximos

Estos son techos, no grants automáticos. Cada identity además queda limitada por proyecto/environment/path. Todo perfil no listado o nuevo nace deny-all hasta PR de policy y aprobación.

Identidad / actor get list exists put update delete audit
operador secrets-admin (humano, MFA) sí sí sí sí sí gate doble sí
wetopi-architect no metadatos de esquema sí no no no sólo su actividad
wetopi-discovery no no no no no no no
wetopi-approver no metadatos/evidencia sí no no no evidencia de control, sin valores
wetopi-lead sólo namespaces de coordinación aprobados sí, acotado sí no por defecto no por defecto no su actividad
perfil executor/deployer registrado sólo refs requeridas por su función acotado sí no por defecto no por defecto no su actividad
identidad secrets-rotator dedicada, no perfil conversacional sí acotado sí acotado sí sí sí no por defecto su actividad
identidad break-glass/recovery humana durante incidente declarado sí sí restore controlado restore controlado no por defecto sí

OWN debe reemplazar la fila genérica executor/dedeployer por una fila por cada perfil activo del registro canónico antes del gate S2; no se infieren nombres. Es inaceptable usar una identidad wildcard para cerrar ese inventario.

flowchart TD
  N[Perfil nuevo] --> X[deny-all]
  X --> R[PR policy: project/env/path + operaciones]
  R --> J{approval}
  J -->|rechazado| X
  J -->|aceptado| M[Machine identity exclusiva]
  M --> B[Bootstrap local owner-only]
  B --> T[Token corto en memoria]
  T --> V[vault.* dentro de scope]

Bootstrap y ciclo de credenciales

  1. Operador crea identity y policy fuera del canal del agente; registra sólo IDs/referencias no secretas.
  2. La credencial bootstrap se genera/entrega por canal separado y se instala directamente como fichero owner-only fuera del repo y de .env; no se pega en tickets, shell history o logs.
  3. El adaptador valida owner/mode, realiza Universal Auth y mantiene el token corto sólo en memoria. Si el fichero falta o es inseguro, fail closed.
  4. Se ejecutan pruebas positiva y negativa por identidad con secretos sintéticos; después se elimina el fixture y se conserva sólo evidencia redacted.
  5. La renovación rota primero la credencial bootstrap, verifica la nueva, invalida la vieja y registra actor/fecha/resultado. Exposición, cambio de rol o baja fuerzan revocación inmediata.

Rotación de secretos administrados

Cada entrada tiene owner, consumidores, scope, método de rotación, plazo, última rotación y próximo vencimiento como metadatos sin valor. Cuando el proveedor soporte API: crear replacement mínimo, almacenar nueva versión, actualizar consumidor por referencia, probar, revocar anterior y verificar rechazo. Sin API: runbook humano con los mismos gates. Nunca se marca completada sólo por cambiar el valor en Infisical. Alarmas cubren expiración próxima y rotaciones fallidas; no incluyen valores.

Backup y recovery

Se requieren dos mecanismos complementarios: backup consistente y cifrado de todos los datos/configuración criptográfica necesarios para restaurar la instancia, guardado off-host; y export lógico de los namespaces aprobados transformado en streaming a SOPS+age, sin plaintext persistente. Recipients age separados (operador/Lead + recuperación), custodia fuera de la VM y prueba periódica en entorno aislado. El runbook: declarar incidente, aislar origen, restaurar versión compatible, comprobar integridad con secretos sintéticos/checksums no sensibles, reemitir machine credentials, rotar credenciales posiblemente expuestas, cerrar break-glass y adjuntar evidencia. Si la edición/API no permite export fiable o audit suficiente, S1 es NO-GO y no se promueve.

Migración del estado vivo

M0: inventario metadata-only de los ocho ficheros de ~/secrets/ y de K8s Secrets: identificador redacted, owner, consumidor, origen, alcance, exposición, rotabilidad y estado; nunca valores ni dump de Secret. M1: cuarentena y prioridad para las dos credenciales expuestas (t_b904ba3f), GHCR broad-scope y argocd admin estático. M2: emitir replacements mínimos y únicos; no importar lo expuesto como destino final. M3: cargar por canal seguro, cambiar consumidores a referencias vault://, prueba funcional y observación. M4: revocar credencial anterior y demostrar que falla; eliminar copias plaintext y .env sólo tras backup/rollback y evidencia de consumidor sano. M5: reconciliar inventario: cero credencial sin owner, cero broad/shared no justificada, cero copia local salvo bootstrap por perfil. Los K8s Secrets de destino se inventarían y clasifican; su SINCRONIZACIÓN es función del Operator instalado en esta fase (decisión del operador 2026-08-31, vía infra PR #27) y no se borra un Secret del clúster bajo OBJ-VAULT-001 salvo el garbage-collection Owner de los CRs de sync revisados.

Auditoría

Evento mínimo: timestamp UTC, request-id, actor humano o machine identity, operación, proyecto/environment/path redacted (sin valor), decisión allow/deny, policy/version, resultado, origen y correlación Objective/task. No se registran valores, tokens, cuerpos ni query strings sensibles. Retención propuesta: 365 días, con copia append-only off-host y acceso restringido; S1 debe confirmar que la edición elegida lo soporta. Alertas: denegaciones repetidas, acceso fuera de patrón, uso break-glass, cambios de policy/identity, export, delete y audit sink caído. La caída del sink bloquea operaciones destructivas y administrativas; las lecturas siguen sólo si la política de continuidad aprobada lo permite.

Riesgos y mitigaciones

Riesgo Mitigación / gate
fallo correlacionado con wetopi3 (vault in-cluster) riesgo ACEPTADO por decisión del operador 2026-08-31; backup cifrado off-host, health monitoring, restore drill antes de migrar
licencia self-hosted no cubre audit/RBAC/identities S1 GO/NO-GO; no producción sin evidencia y coste aceptado
bootstrap local robado uno por perfil, filesystem owner-only, token corto en memoria, rotación/revocación
ai-hub se convierte en confused deputy binding perfil→identity inmutable, allowlists, no identity= del caller, tests negativos
logs/CLI filtran valores stdin/memoria, redaction tests, no argv/env, scanning de logs
política excesiva o drift default deny, policy-as-code sin valores, review y reconciliación periódica
migración rompe consumidores replacement/dual-run, prueba, rollback y revocación sólo después
credenciales viejas sobreviven verificación explícita de rechazo y reconciliación final
Basic Auth rompe API o da falsa seguridad auth nativa obligatoria; validar separación UI/API; omitir Basic Auth si interfiere
recovery bundle incompleto restore drill aislado incluyendo claves/configuración de cifrado
audit tampering sink append-only off-host, acceso separado, alertas de ausencia
operator/CRDs amplían superficie K8s instalados en esta fase por decisión del operador 2026-08-31 (ADR-0009 decisión 8); revisados en infra PR #27, RBAC mínima upstream verbatim, CRDs v1beta1 con checksum verificado

Diagramas de permisos

flowchart LR
  A[architect] -->|list schema / exists| M[metadata only]
  D[discovery] -->|deny| Z[no vault access]
  J[approver] -->|evidence| M
  L[lead] -->|scoped get/list/exists| S[approved namespace]
  E[executor] -->|scoped get| S
  Q[rotator] -->|put/update| S
  O[operator MFA] -->|admin; delete double gate| S
  • ADR-0009 — la decisión formal (accepted 2026-08-31) y sus alternativas/consecuencias.
  • OBJ-VAULT-001 — el Objective con ACs, etapas S0-S10, gates y promotion matrix.

Repositorio: platform-infisical — manifests: infisical-server, infisical-operator.