docs: add secrets-manager design doc; fix mkdocs nav
This commit is contained in:
@@ -0,0 +1,168 @@
|
|||||||
|
# Secrets Manager — Design & Integration
|
||||||
|
|
||||||
|
## Problem statement
|
||||||
|
|
||||||
|
Secrets are currently scattered across:
|
||||||
|
|
||||||
|
| Location | Count | Risk |
|
||||||
|
|---|---|---|
|
||||||
|
| `/opt/stacks/ai/.env` on CT 104 | ~30 keys | Readable by any root-equivalent process; not versioned |
|
||||||
|
| Coder `main.tf` (hardcoded env vars) | 6 | Committed to Gitea; visible in template history |
|
||||||
|
| Zoraxy proxy configs | 0 (plain routes) | Low |
|
||||||
|
| HA `secrets.yaml` on HAOS VM 100 | unknown | Not backed up centrally |
|
||||||
|
| `/root/garage-chat-artifacts.creds` | 1 | `0600`, but on Proxmox host root |
|
||||||
|
| Browser/Vaultwarden (manual) | Many | Human-only access |
|
||||||
|
|
||||||
|
Goal: single secret store that agents, Docker services, and Coder workspaces can all read programmatically, with OIDC-gated access via Pocket-ID.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Candidate solutions
|
||||||
|
|
||||||
|
### 1. Infisical (recommended)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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:
|
||||||
|
```hcl
|
||||||
|
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`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[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
|
||||||
Reference in New Issue
Block a user