Files
docs/services/secrets-manager.md
fkrebs a2f1d49170 migrate dev→CT104, secrets→CT109; add proton-bridge; sunset CT111/CT112
- CT 111 (dev): Gitea + Coder + act-runner migrated to CT 104
  - Gitea uses Redis for queue/cache/session (idmapped NFS LevelDB workaround)
  - Zoraxy routes dev/git updated to 192.168.1.40
  - AdGuard dev.nuclide.lan updated to 192.168.1.40
- CT 112 (secrets): Infisical migrated to CT 109
  - DB dump restored; SITE_URL updated to 192.168.1.8:8200
  - AdGuard secrets.nuclide.lan updated to 192.168.1.8
- Proton Mail Bridge deployed on CT 104 (SMTP :1025, IMAP :1143)
- Homepage updated: Dev group → ct104, Infisical → my-local, Background group added
- portmap, ct-inventory, zoraxy, dev-environment, secrets-manager all updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:08:08 +02:00

6.2 KiB

Secrets Manager — Infisical on CT 109

Status: deployed 2026-05-22; migrated CT 112 → CT 109 on 2026-05-26. Running at http://192.168.1.8:8200 (http://secrets.nuclide.lan:8200). LAN-only, no Zoraxy route — secrets must not be internet-exposed.

Stack: infisical/infisical:latest-postgres + Postgres 16 + Redis 7, all on CT 109 (ops, 192.168.1.8). Compose at /opt/stacks/infisical/ on CT 109. CT 112 ("secrets") is decommissioned; LXC pending removal.

Problem statement

Secrets are currently scattered across:

Location Count Risk
/opt/stacks/ai/.env on CT 104 ~55 keys Not versioned; duplicated across stacks
Per-stack .env files CT 104 ~30 keys Several duplicated (WALG keys, IMMICH_API_KEY, etc.)
CT 101, 111, 113 .env files ~30 keys No central rotation story
Coder main.tf (hardcoded env vars) 6 Committed to Gitea; visible in template history
HA secrets.yaml on HAOS VM 100 unknown Not backed up centrally

Goal: single LAN-only secret store that agents, Docker services, and Coder workspaces pull from programmatically. ~85 unique secrets identified in sweep (2026-05-22).


Candidate solutions

Open-source HashiCorp Vault alternative. Docker-compose deployable. Native integrations for:

  • Docker secrets injection (Infisical agent sidecar)
  • Kubernetes/Coder workspace env injection
  • REST API + OIDC machine identities
  • OIDC SSO (can federate with Pocket-ID)
  • Web UI

Deployment: separate LXC (recommended — see rationale below). Postgres backend → candidate for CT 113 consolidation once second NVMe lands.

Port: 8080 internally; no external exposure needed (LAN-only MCP sidecar pattern).


2. HashiCorp Vault (OSS)

Industry standard. Steeper ops overhead (unsealing, audit logs, lease renewal). Overkill for a homelab unless you need HSM-grade guarantees.


3. Doppler (SaaS)

Managed; free tier; native CLI and Docker integration. Outbound dependency; secrets leave the homelab. Not suitable given the DLR/LUMEN data handling doctrine.


4. Stay with Vaultwarden + manual OIDC

Already deployed. Works for human access. No programmatic injection without writing custom code. Dead end for agent/pipeline automation.


Recommendation: Infisical on a dedicated LXC

Why a separate LXC?

  1. Blast radius isolation — if the CT 104 Docker stack is compromised, the secret store is not on the same attack surface.
  2. Minimal footprint — Infisical + Postgres is the only workload; nothing else can mess with the process space.
  3. Simpler audit — outbound connections from CT 104 are numerous; a dedicated secrets LXC should have near-zero egress.
  4. HA restart independence — CT 104 restarts (image gen, GPU passthrough experiments) don't affect secret availability.

Suggested: CT 112 (next available), 2 vCPU / 2 GB RAM, 8 GB disk on local-zfs.


Architecture

flowchart TD
  subgraph CT112["CT 112 — secrets"]
    infisical["Infisical Server\n:8080"]
    pg_sec["Postgres\n(infisical DB)"]
    infisical --- pg_sec
  end

  subgraph CT104["CT 104 — Docker host"]
    agent["Infisical Agent\n(sidecar per stack)"]
    env_file[".env (templated)\nrendered at startup"]
    agent -->|pull on start| infisical
    agent --> env_file
  end

  subgraph CT111["CT 111 — Coder"]
    coder["Coder server"]
    ws["Workspace containers\n(env injected at provision)"]
    coder -->|agent token| infisical
    coder --> ws
  end

  subgraph CT110["CT 110 — Pocket-ID"]
    oidc["OIDC IdP"]
  end

  oidc -->|machine identity| infisical
  oidc -->|user SSO| infisical

Setup plan

Phase 1 — Deploy Infisical on CT 112

# On Proxmox host
pvesh create /nodes/nuc/lxc \
  --ostemplate local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --vmid 112 --hostname secrets --memory 2048 --cores 2 \
  --rootfs local-zfs:8 --net0 name=eth0,bridge=vmbr0,ip=dhcp

# Inside CT 112
apt-get install -y docker.io docker-compose-plugin
# Deploy from https://github.com/Infisical/infisical (official compose)

Infisical requires a Redis instance alongside Postgres. The official docker-compose.yml bundles both.

Phase 2 — Migrate CT 104 secrets

  1. Create an Infisical project homelab/ct104
  2. Import existing .env keys via Infisical CLI: infisical import --env prod --path /homelab/ct104 < .env
  3. Add Infisical agent to each compose stack as a sidecar that renders .env from templates at container start

Phase 3 — Coder workspace injection

Infisical has a native Coder integration (via machine identity token). Replace hardcoded env vars in main.tf with:

data "external" "secrets" {
  program = ["infisical", "export", "--env", "prod", "--path", "/coder/workspaces", "--format", "dotenv-export"]
}

Or use the Infisical agent installed in the workspace image.

Phase 4 — OIDC SSO via Pocket-ID

Infisical supports OIDC SSO under Settings → Authentication → OIDC. Register a new client in Pocket-ID with callback https://secrets.nuclide.systems/api/v1/sso/oidc/callback (or LAN-only URL).


Prioritisation

Item Effort Value
Create CT 112, deploy Infisical 1h High — removes .env sprawl
Migrate CT 104 .env 30min High
Coder template injection 1h Medium — workspaces currently work
HA secrets.yaml → Infisical 30min (HA add-on) Medium
OIDC SSO via Pocket-ID 30min Low (convenience)

Short-term mitigation (before CT 112 is built)

  1. Remove secrets from Coder main.tf that appear in Gitea history — use Coder template variables or env injection instead.
  2. Keep /opt/stacks/ai/.env mode 0600; ensure it is in .gitignore for any repo that mounts that directory.
  3. Use Vaultwarden as the authoritative human copy; rotate any key that has appeared in Claude Code context (see audit-claude-code-meta.md).

  • postgres-consolidation — CT 113 will consolidate Postgres instances including Infisical's DB
  • feedback_zoraxy_confirm — Zoraxy route for secrets.nuclide.systems will need operator approval when CT 112 is ready
  • security/audit-claude-code-meta.md — lists secrets that transited Claude API this session