Skip to content

Vault — Infisical self-hosted (ns vault)

What is it?

Infisical is the platform's secrets manager for humans and for agent profiles (machine identities via Universal Auth). ADR-0009 (accepted 2026-08-31) fixes the shape: self-hosted in cluster wetopi3, namespace vault, with persistent PostgreSQL and Redis datastores in the same cluster, the Infisical → Kubernetes Operator for Secret sync, Universal Auth restricted to the private network, and SOPS+age demoted to bootstrap/recovery only. The operator's decision explicitly replaced the dedicated-VM variant with the in-cluster deployment.

How is it mounted?

All objects Git-owned under kubernetes/vault/ (images digest-pinned):

  • Deployment vault — infisical/infisical:v0.164.1@sha256:d3ff9f82… (multi-arch index digest), non-root uid 1001, replicas 1, Recreate strategy, readiness/liveness on :8080/api/status. A ConfigMap vault-entrypoint wrapper builds DB_CONNECTION_URI at container start from Secret env (URL-encoded, never a YAML literal).
  • Datastores: postgres:17.6-alpine3.22 (digest-pinned, non-root uid 70)
  • PVC vault-postgres-data 5Gi; redis:7.4-alpine (digest-pinned, noeviction + appendonly yes, uid 999) + PVC vault-redis-data 1Gi. Both on StorageClass ceph-rbd → depends on Ceph CSI (below).
  • Services: vault (FULL API — in-cluster only, NO edge route) and vault-ui (edge UI backend).
  • Edge: HTTPRoute vault (ns vault) → Gateway wetopi-edge https listener; Certificate vault-tls (LE PROD DNS-01, kube-system).
  • Secrets (references, never values — operator-provisioned on the cluster, excluded from Argo sync): vault-infra-credentials (ENCRYPTION_KEY, AUTH_SECRET) and vault-postgres-credentials (POSTGRES_USER, POSTGRES_PASSWORD); shapes: secret.infra.example.yaml, secret.postgres.example.yaml.
  • Operator (separate lifecycle): infisical/kubernetes-operator:v0.11.8 (digest-pinned) in namespace infisical-operator-system (kubernetes/infisical-operator/); anchoring its Argo Application is a future reviewed change. CRDs are v1beta1 (Secrets Operator v1beta1).

Diagram

flowchart TD
  AGENTS["agents / profiles (machine identities)"]
  HUMANS["operators (browser, login+TOTP)"]
  subgraph CL["cluster wetopi3"]
    subgraph NSV["namespace vault"]
      APP["Deployment vault — Infisical v0.164.1"]
      PG["PostgreSQL 17.6 — PVC 5Gi"]
      RD["Redis 7.4 noeviction — PVC 1Gi"]
      SVAULT["Service vault (FULL API, in-cluster only)"]
      SUI["Service vault-ui (UI edge backend)"]
    end
    OP["Infisical Operator v0.11.8 (ns infisical-operator-system)"]
    GW["Gateway wetopi-edge (kube-system) + cert vault-tls"]
    SC["StorageClass ceph-rbd (Ceph CSI)"]
  end
  CEPH["Ceph cluster 192.168.96.0/20 (VLAN 2)"]
  AGENTS -->|"http://vault.vault.svc.cluster.local (NEVER the edge)"| SVAULT
  HUMANS -->|"https://vault.wetopi.net"| GW --> SUI
  SVAULT --> APP
  SUI --> APP
  APP --> PG
  APP --> RD
  PG --> SC
  RD --> SC
  SC --> CEPH
  OP -->|"pulls from internal endpoint"| SVAULT

How is it deployed?

GitOps: the Argo CD Application vault (anchored in the root path at kubernetes/platform/vault.yaml) syncs kubernetes/vault/ with an include-only filter ({namespace.yaml,postgres.yaml,redis.yaml,infisical.yaml,certificate-vault.yaml,httproute-vault.yaml}) and prune + selfHeal — the two operator Secrets are excluded on purpose and must exist before the first sync (runbook: kubernetes/vault/README.md §provisioning — APPLY card t_89a254ce). TLS: Certificate vault-tls must be Ready BEFORE the Gateway certificateRefs edit is applied (fail-safe order in certificate-vault.yaml header). The Infisical Operator directory has its own lifecycle (not yet anchored to the root app).

Dependencies

Depends on: Ceph CSI / StorageClass ceph-rbd for both PVCs (SVC-CEPH-CSI) — if CSI is not live, the datastores cannot bind; edge Gateway wetopi-edge + the nftables 443→8443 redirect (SVC-TRAEFIK-EDGE); cert-manager for vault-tls (SVC-CERT-MANAGER); DNS A records vault.wetopi.net → edge IPs (tofu-owned, tfvars); operator Secrets vault-infra-credentials + vault-postgres-credentials (APPLY S0–S2, card t_89a254ce); RKE2 etcd encryption at rest is the at-rest story (etcd keys on ops01).

Depended on by: every machine identity (agent profiles) — the ai-hub vault.* contract (vault.get/put/update/delete/list/exists) resolves against http://vault.vault.svc.cluster.local:80; long-term the ceph-csi csi-rbd-secret and other bootstrap secrets migrate from ops01 files into the Vault (ADR-0009).

Operation

  • Access (human): https://vault.wetopi.net — native Infisical login
  • password + TOTP. No basic-auth middleware (deliberate — ADR-0009 decision 2; it would break interactive flows).
  • Access (machine): http://vault.vault.svc.cluster.local:80 only — the public route carries DENY rules (D1–D6: universal-auth login, identity, /api/v1|v2|v3|v4 secret surfaces all point at the nonexistent vault-api-deny Service — fail-closed 5xx by Gateway API spec). The proof contract (public 5xx / internal 200 drill) is in kubernetes/vault/README.md §5 and runs on the ops card after apply.
  • Health: kubectl -n vault get pods (vault/postgres/redis Ready), GET http://vault.vault.svc.cluster.local/api/status from inside the cluster; edge probe: vault-tls Ready + https listener ResolvedRefs.
  • Backup/recovery: ADR-0009 AC8 — pg_dump + age-encrypted off-host backup, restore REHEARSED before the service is considered production (recovery contract compensates the accepted correlated-failure trade-off of in-cluster datastores).

Estado honesto (live, 2026-08-31 20:14Z)

LIVE — vault.wetopi.net serving HTTPS 200 with LE cert. End-to-end verify after the operator's tofu DNS apply (vault.wetopi.net → 79.137.114.193/.194): Web UI 200 via both edge IPs, certificate vault-tls (Let's Encrypt, valid 2026-08-31→2026-11-29), pods 3/3 Running (infisical + postgres + redis on ceph-rbd PVCs), /api/status Ok with Redis configured. Two lead-direct hotfixes landed in Git during the apply: commit 3a87ee94 (entrypoint ConfigMap defaultMode: 0755 — runAsUser 1001 exec permission, crashloop fix) and 7e0b3a0b (infisical memory limit 1Gi→2Gi after an OOMKill at 19:59Z, V8 heap 2.3GB). The deny rules D1–D3 (machine-API paths → nonexistent Service vault-api-deny) are fail-closed BY SPEC — verified: /api/v1/secrets returns 500 from the public edge; the Argo Degraded on the app is the documented false positive of this mechanism (ResolvedRefs=False is the deny working, not a fault). PENDING: operator admin bootstrap + TOTP QR (human step).