Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions guide/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Soft-deleted vault items remain as **tombstones** until peers sync; last-write-w
- Set a strong unique `JWT_SECRET` (min 32 chars; placeholders rejected at startup).
- Terminate **HTTPS** in front of the API in production.
- Keep `CORS_ORIGINS` an explicit allow-list (**never `*`**).
- When running multiple API workers, add **reverse-proxy rate limits** — in-process auth limits are per worker (see [Install the server](./server#production-hardening)).
- Prefer HTTPS with a valid certificate; clients do not pin certs — HTTP or bad TLS enables MITM on sync/login.
- Prefer keeping the vault **locked when idle**; enable biometric unlock carefully.
- Use **Settings → Security → Password health** to find weak/reused passwords; optional HIBP checks send only a SHA-1 hash **prefix** (k-anonymity), never the password.
- Treat exports / backups as secret material — store offline and encrypted.
Expand Down
26 changes: 26 additions & 0 deletions guide/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,32 @@ CORS_ORIGINS=https://openkey.example.com

Point clients at `https://openkey.example.com` (no port). Confirm `https://openkey.example.com/health`.

## Production hardening

Beyond TLS and CORS, self-hosters should plan for **multi-worker deployments** and **client trust**.

### Reverse proxy and TLS

- Terminate **HTTPS** at Caddy, nginx, or your load balancer. Do not expose Postgres or the raw API port publicly.
- Add **HSTS** at the proxy (`Strict-Transport-Security`) so browsers never fall back to HTTP after the first visit.
- Set `TRUST_PROXY_HEADERS=true` only when the proxy overwrites `X-Forwarded-For` and you trust that path; otherwise per-IP rate limits follow the proxy, not the end user.

### Rate limiting

The API applies an in-memory sliding-window limiter on auth endpoints (`AUTH_RATE_LIMIT_*` in `.env`). That limit is **per Uvicorn worker process**. With multiple workers or replicas, effective limits multiply unless you add a shared limiter at the reverse proxy (for example `limit_req` in nginx or Caddy rate limits).

### Email enumeration tradeoffs

`POST /auth/prelogin` and `POST /auth/lookup-public-key` return **404** when the email is unknown. That helps legitimate clients (extension login, org key wrapping) but lets an attacker probe which emails are registered. Mitigations:

- Keep auth rate limits strict at the proxy and API.
- Do not expose the sync API on untrusted networks without TLS.
- For high-sensitivity deployments, place the API behind VPN or IP allow-lists.

### Client MITM on self-hosted URLs

The OpenKey app, extension, and CLI use the platform TLS stack with **no certificate pinning** by default. Users who type `http://` or accept misconfigured certificates are vulnerable to MITM during login and sync. Always serve HTTPS with a valid certificate and document the server URL for your users.

## API overview

Interactive OpenAPI: `http://localhost:8000/docs` on a running server. Full tables live in the `openkey_server` package README. Highlights:
Expand Down
Loading