Spec: nøkkelhåndtering — API-nøkler kryptert i PG, admin-UI
Erstatter .env-filer for API-nøkler. AES-256-GCM kryptert i PG, administrert via admin-UI, injisert av maskinrommet som env ved verktøy-spawning. Audit trail, test-tilkobling, flere nøkler per provider, deaktivering uten sletting. Ingen endring i verktøykode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c30a40e97a
commit
f98ad11081
1 changed files with 162 additions and 0 deletions
162
docs/infra/nøkkelhåndtering.md
Normal file
162
docs/infra/nøkkelhåndtering.md
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
# Nøkkelhåndtering — API-nøkler i PG, administrert via UI
|
||||||
|
|
||||||
|
## Problemstilling
|
||||||
|
|
||||||
|
API-nøkler (OpenRouter, Anthropic, Gemini, xAI, OpenAI, ElevenLabs
|
||||||
|
etc.) ligger i klartekst i `/srv/synops/.env`. Ingen audit trail,
|
||||||
|
ingen granulær tilgang, lett å glemme å oppdatere.
|
||||||
|
|
||||||
|
## Modell
|
||||||
|
|
||||||
|
Maskinrommet er nøkkelforvalter. Nøkler lagres kryptert i PG,
|
||||||
|
administreres via admin-UI, injiseres i verktøy ved oppstart.
|
||||||
|
|
||||||
|
```
|
||||||
|
Admin-UI → maskinrommet → PG (kryptert)
|
||||||
|
↓
|
||||||
|
spawner verktøy med nøkler som env
|
||||||
|
↓
|
||||||
|
synops-agent, synops-ai, synops-clip osv.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database-skjema
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE api_keys (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
provider TEXT NOT NULL, -- 'openrouter', 'anthropic', 'gemini', 'xai', 'openai', 'elevenlabs'
|
||||||
|
label TEXT, -- valgfritt navn ("Vegards OpenRouter", "Prod Anthropic")
|
||||||
|
key_encrypted BYTEA NOT NULL, -- AES-256-GCM kryptert
|
||||||
|
key_hint TEXT, -- siste 4 tegn for identifisering ("...c08b")
|
||||||
|
is_active BOOLEAN DEFAULT true, -- deaktivert uten å slette
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
created_by UUID REFERENCES nodes(id), -- hvem la inn nøkkelen
|
||||||
|
last_used TIMESTAMPTZ, -- sist brukt (oppdateres ved bruk)
|
||||||
|
usage_count BIGINT DEFAULT 0 -- antall ganger brukt
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_api_keys_provider ON api_keys(provider, is_active);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kryptering
|
||||||
|
|
||||||
|
Nøkkelen krypteres med AES-256-GCM. Krypteringsnøkkelen
|
||||||
|
(master key) lever som:
|
||||||
|
- Env-variabel `SYNOPS_MASTER_KEY` på hosten
|
||||||
|
- Eller i systemd credentials (LoadCredential)
|
||||||
|
- Aldri i PG, aldri i .env
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Maskinrommet krypterer ved lagring
|
||||||
|
let encrypted = aes_gcm_encrypt(&api_key, &master_key);
|
||||||
|
|
||||||
|
// Maskinrommet dekrypterer ved bruk
|
||||||
|
let api_key = aes_gcm_decrypt(&encrypted, &master_key);
|
||||||
|
```
|
||||||
|
|
||||||
|
Kun maskinrommet kjenner master key. Admin-UI sender klartekst
|
||||||
|
til maskinrommet via HTTPS, maskinrommet krypterer og lagrer.
|
||||||
|
|
||||||
|
## Nøkkeloppslag
|
||||||
|
|
||||||
|
Når et verktøy trenger en API-nøkkel:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// I maskinrommet, ved spawning av synops-agent:
|
||||||
|
let key = db::get_active_key("openrouter").await?;
|
||||||
|
cmd.env("OPENROUTER_API_KEY", key.decrypt(&master_key)?);
|
||||||
|
```
|
||||||
|
|
||||||
|
Verktøyet ser nøkkelen som vanlig env-variabel. Ingen endring
|
||||||
|
i verktøykoden — de leser allerede fra env.
|
||||||
|
|
||||||
|
Alternativt for synops-agent (som kjører lenge):
|
||||||
|
maskinrommet eksponerer et internt endepunkt:
|
||||||
|
```
|
||||||
|
GET /internal/api-key/{provider}
|
||||||
|
Authorization: Bearer <intern-token>
|
||||||
|
→ { "key": "sk-..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Admin-UI
|
||||||
|
|
||||||
|
### Nøkkelliste
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ API-nøkler ──────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ Provider Label Status Sist │
|
||||||
|
│ OpenRouter Vegards OR ✅ Aktiv nå │
|
||||||
|
│ Anthropic Prod ✅ Aktiv 2t │
|
||||||
|
│ Gemini — ⚫ Inaktiv │
|
||||||
|
│ xAI — ⚫ Mangler │
|
||||||
|
│ OpenAI — ⚫ Mangler │
|
||||||
|
│ ElevenLabs Prod TTS ✅ Aktiv 1d │
|
||||||
|
│ │
|
||||||
|
│ [+ Legg til nøkkel] │
|
||||||
|
└────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legg til / rediger
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ Ny nøkkel ───────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ Provider: [OpenRouter ▼] │
|
||||||
|
│ Label: [Vegards OpenRouter ] │
|
||||||
|
│ Nøkkel: [sk-or-v1-... ] │
|
||||||
|
│ │
|
||||||
|
│ [Test tilkobling] [Lagre] [Avbryt] │
|
||||||
|
└────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
"Test tilkobling" gjør et minimalt API-kall (list models)
|
||||||
|
for å verifisere at nøkkelen fungerer før lagring.
|
||||||
|
|
||||||
|
### Sikkerhetsregler
|
||||||
|
|
||||||
|
- Nøkkelen vises aldri etter lagring — kun hint ("...c08b")
|
||||||
|
- For å endre: slett gammel, legg inn ny
|
||||||
|
- Deaktivering beholder nøkkelen men bruker den ikke
|
||||||
|
- Sletting fjerner den permanent
|
||||||
|
- Audit: created_by, created_at, last_used, usage_count
|
||||||
|
|
||||||
|
## Flere nøkler per provider
|
||||||
|
|
||||||
|
En provider kan ha flere nøkler (f.eks. ulike OpenRouter-kontoer):
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT key_encrypted FROM api_keys
|
||||||
|
WHERE provider = 'openrouter' AND is_active = true
|
||||||
|
ORDER BY usage_count ASC -- round-robin / minst brukt
|
||||||
|
LIMIT 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Gir enkel lastbalansering og mulighet for å rotere nøkler
|
||||||
|
uten nedetid.
|
||||||
|
|
||||||
|
## Migrering fra .env
|
||||||
|
|
||||||
|
1. Admin legger inn nøkler via UI
|
||||||
|
2. Maskinrommet bruker PG-nøkler i stedet for env
|
||||||
|
3. .env-filen beholder nøklene som fallback
|
||||||
|
4. Når alt fungerer: fjern nøkler fra .env
|
||||||
|
5. Beholder kun `SYNOPS_MASTER_KEY` som env
|
||||||
|
|
||||||
|
## Hva som endres
|
||||||
|
|
||||||
|
| Komponent | Endring |
|
||||||
|
|-----------|---------|
|
||||||
|
| PG | Ny `api_keys`-tabell |
|
||||||
|
| Maskinrommet | Kryptering, nøkkeloppslag, API-endepunkt |
|
||||||
|
| Admin-UI | Ny side for nøkkelhåndtering |
|
||||||
|
| synops-agent | Ingen (leser fortsatt fra env) |
|
||||||
|
| CLI-verktøy | Ingen (leser fortsatt fra env) |
|
||||||
|
| .env | Nøkler fjernes gradvis |
|
||||||
|
|
||||||
|
## Prioritet
|
||||||
|
|
||||||
|
Middels. Fungerer med .env nå. Viktigere når flere brukere
|
||||||
|
skal administrere egne nøkler (BYOK-modell) eller når nøkler
|
||||||
|
roteres jevnlig.
|
||||||
Loading…
Add table
Reference in a new issue