141 lines
5.1 KiB
Markdown
141 lines
5.1 KiB
Markdown
# Design Proposal: Zero-Knowledge Secrets Manager
|
|
|
|
## 1. Architectural Overview
|
|
|
|
**Philosophy:** "Thick Client, Blind Server."
|
|
The system relies on a **Zero-Knowledge Architecture**. The server (Node.js/PostgREST) and the database never see unencrypted secrets; they act solely as a synchronization engine for encrypted blobs. All encryption/decryption occurs on the client device (Tauri/Flutter) using keys that never leave the device.
|
|
|
|
### High-Level Data Flow
|
|
|
|
1. **Client:** Derives Master Key from (Password + Salt). Encrypts data locally.
|
|
2. **Transport:** Sends encrypted blob (text/bytea) over HTTPS.
|
|
3. **Gateway:** Nginx/Traefik handles SSL termination and routing.
|
|
4. **Auth:** Node.js verifies identity (SRP-6a) and issues JWT.
|
|
5. **Persistence:** PostgREST accepts JWT, enforces Row-Level Security (RLS), and writes blob to PostgreSQL.
|
|
|
|
---
|
|
|
|
## 2. Component Design
|
|
|
|
### A. Database Layer (PostgreSQL v18+)
|
|
|
|
* **Role:** Storage and Authorization Engine.
|
|
* **Logic:** Heavily utilizes Row-Level Security (RLS) policies.
|
|
* **Schema Strategy:**
|
|
* `users`: Stores `verifier` (SRP-6a) or hashed credentials (Argon2id).
|
|
* `secrets`: Stores `encrypted_data` (bytea), `iv`, `auth_tag`, `owner_id`, `type` (manual/rotated).
|
|
* `access_policies`: Join table mapping users/groups to secrets.
|
|
* **Security:** RLS policies ensure `SELECT * FROM secrets` only returns rows where `auth.uid() IN (access_policies)`.
|
|
|
|
### B. Backend API (Node.js v24 + PostgREST)
|
|
|
|
* **Node.js Service:**
|
|
* **Auth:** Implements SRP-6a (Secure Remote Password) protocol.
|
|
* **Business Logic:** Handling billing, email alerts, secret rotation jobs, and external webhooks.
|
|
* **Orchestration:** Manages Redis interactions for ephemeral state.
|
|
* **PostgREST:**
|
|
* High-performance CRUD layer.
|
|
* Directly exposes secure views/tables.
|
|
* Consumes JWTs issued by the Node.js service for authorization.
|
|
|
|
### C. Caching & State (Redis Cluster v8)
|
|
|
|
* **Image Source:** Official `redis:alpine` (replacing deprecated Bitnami images).
|
|
* **Revocation Lists:** Stores revoked JWT JTI claims.
|
|
* **Rate Limiting:** Distributed counters to block brute-force attacks.
|
|
* **Pub/Sub:** Real-time event bus to trigger client syncs (e.g., "Vault Updated").
|
|
|
|
### D. Clients (Tauri & Flutter)
|
|
|
|
* **Desktop (Tauri):**
|
|
* **Security:** Encryption logic (AES-256-GCM / XChaCha20-Poly1305) runs in the Rust backend to prevent memory dumping attacks via browser dev tools.
|
|
* **Offline:** Encrypted SQLite local replica.
|
|
* **Mobile (Flutter):**
|
|
* **Storage:** Uses `flutter_secure_storage` for session tokens.
|
|
* **Bio-auth:** Integrates FaceID/Fingerprint to unlock the local vault key.
|
|
|
|
---
|
|
|
|
## 3. Workflows
|
|
|
|
### A. Manual Secret Entry (Core Feature)
|
|
|
|
This is the standard workflow for storing static API keys, passwords, or notes.
|
|
|
|
1. **User Input:** User enters data (e.g., `API_KEY=12345`) into the client form.
|
|
2. **Client Encryption:** The App (Tauri/Flutter) generates a random IV and encrypts the payload using the user's Master Key *in memory*.
|
|
3. **Payload Construction:** Client creates a JSON payload: `{"iv": "...", "data": "encrypted_blob...", "type": "manual"}`.
|
|
4. **Sync:** Client POSTs the payload to PostgREST.
|
|
5. **Storage:** Database stores the blob. The server never sees `12345`.
|
|
|
|
### B. Automated Rotation (Enhancement)
|
|
|
|
1. **Trigger:** Cron job in Node.js wakes up.
|
|
2. **Execution:** Connects to provider (e.g., AWS), generates new keys.
|
|
3. **Encryption:** *Note: This requires a separate "Server-Side Vault" mechanism or Public Key encryption if the server needs to write secrets readable by the user.*
|
|
4. **Update:** Updates the `secrets` row with the new encrypted blob.
|
|
|
|
---
|
|
|
|
## 4. Cryptography Standards
|
|
|
|
* **KDF:** Argon2id (memory-hard) for deriving Master Key.
|
|
* **Symmetric:** AES-256-GCM or XChaCha20-Poly1305 for vault data.
|
|
* **Asymmetric:** RSA-4096 or ECC (Curve25519) for sharing secrets between users.
|
|
* **Transport:** TLS 1.3 only.
|
|
|
|
---
|
|
|
|
## 5. Infrastructure (Docker Compose Prototype)
|
|
|
|
*Note: Switched to official Redis images due to Bitnami Docker Hub deprecation.*
|
|
|
|
```yaml
|
|
version: '3.9'
|
|
services:
|
|
# Load Balancer / SSL Termination
|
|
gateway:
|
|
image: nginx:alpine
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
depends_on:
|
|
- api
|
|
- postgrest
|
|
|
|
# The Logic Core
|
|
api:
|
|
image: node:24-alpine
|
|
environment:
|
|
- DATABASE_URL=postgres://user:pass@db:5432/secrets_db
|
|
- REDIS_URL=redis://redis:6379
|
|
- JWT_SECRET=${JWT_SECRET}
|
|
|
|
# The Data Interface
|
|
postgrest:
|
|
image: postgrest/postgrest:latest
|
|
environment:
|
|
- PGRST_DB_URI=postgres://user:pass@db:5432/secrets_db
|
|
- PGRST_JWT_SECRET=${JWT_SECRET}
|
|
- PGRST_DB_SCHEMA=api
|
|
- PGRST_DB_ANON_ROLE=web_anon
|
|
|
|
# The Vault
|
|
db:
|
|
image: postgres:18
|
|
volumes:
|
|
- pg_data:/var/lib/postgresql/data
|
|
environment:
|
|
- POSTGRES_DB=secrets_db
|
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
|
|
# The High-Speed State (Official Image)
|
|
# For a full cluster in dev, you may need a custom entrypoint or sidecar
|
|
# to bootstrap the slots. For simplicity here, we use a standalone instance
|
|
# that mimics the interface.
|
|
redis:
|
|
image: redis:alpine
|
|
command: redis-server --appendonly yes
|
|
volumes:
|
|
- redis_data:/data
|