Rename all project references from quicproquo/qpq to quicprochat/qpc across documentation, Docker configuration, CI workflows, packaging scripts, operational configs, and build tooling. - Docker: crate paths, binary names, user/group, data dirs, env vars - CI: workflow crate references, binary names, artifact names - Docs: all markdown files under docs/, SDK READMEs, book.toml - Packaging: OpenWrt Makefile, init script, UCI config (file renames) - Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team - Operations: Prometheus config, alert rules, Grafana dashboard - Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths - Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
251 lines
7.5 KiB
Markdown
251 lines
7.5 KiB
Markdown
# Key Rotation Procedures
|
|
|
|
This document provides step-by-step procedures for rotating all cryptographic material in a quicprochat deployment.
|
|
|
|
## Auth Token Rotation
|
|
|
|
The auth token (`QPC_AUTH_TOKEN`) is used for bearer-token authentication (auth version 1). OPAQUE-authenticated sessions are not affected by token rotation.
|
|
|
|
### Procedure
|
|
|
|
```bash
|
|
# 1. Generate a new token (minimum 16 characters for production)
|
|
NEW_TOKEN=$(openssl rand -base64 32)
|
|
echo "New token: $NEW_TOKEN"
|
|
|
|
# 2. Update the config file or environment
|
|
# Option A: TOML config file
|
|
sed -i "s/^auth_token = .*/auth_token = \"$NEW_TOKEN\"/" qpc-server.toml
|
|
|
|
# Option B: Environment variable (systemd)
|
|
systemctl edit qpc-server --force
|
|
# Add: Environment=QPC_AUTH_TOKEN=<new-token>
|
|
|
|
# Option C: Docker Compose
|
|
# Update QPC_AUTH_TOKEN in docker-compose.prod.yml or .env file
|
|
|
|
# 3. Restart the server
|
|
systemctl restart qpc-server
|
|
# or: docker compose restart server
|
|
|
|
# 4. Update all clients with the new token
|
|
# Clients using OPAQUE auth are unaffected.
|
|
# Clients using bearer-token auth must update their QPC_ACCESS_TOKEN.
|
|
```
|
|
|
|
### Impact
|
|
|
|
- Active bearer-token sessions continue until they expire (sessions are in-memory).
|
|
- New bearer-token connections must use the new token.
|
|
- OPAQUE-authenticated clients are not affected.
|
|
|
|
## TLS Certificate Rotation
|
|
|
|
The server uses DER-encoded X.509 certificates for QUIC TLS 1.3. The server validates certificates at startup and warns if expiry is within 30 days.
|
|
|
|
### Procedure
|
|
|
|
```bash
|
|
# 1. Obtain a new certificate (example with Let's Encrypt / certbot)
|
|
certbot certonly --standalone -d chat.example.com
|
|
|
|
# 2. Convert PEM to DER format (qpc-server expects DER)
|
|
openssl x509 -in /etc/letsencrypt/live/chat.example.com/fullchain.pem \
|
|
-outform DER -out /tmp/server-cert.der
|
|
|
|
openssl pkey -in /etc/letsencrypt/live/chat.example.com/privkey.pem \
|
|
-outform DER -out /tmp/server-key.der
|
|
|
|
# 3. Set restrictive permissions on the private key
|
|
chmod 600 /tmp/server-key.der
|
|
|
|
# 4. Back up the current certificates
|
|
cp data/server-cert.der data/server-cert.der.bak
|
|
cp data/server-key.der data/server-key.der.bak
|
|
|
|
# 5. Replace certificates
|
|
cp /tmp/server-cert.der data/server-cert.der
|
|
cp /tmp/server-key.der data/server-key.der
|
|
|
|
# 6. Verify the new certificate
|
|
openssl x509 -inform DER -in data/server-cert.der -noout -text | head -20
|
|
|
|
# 7. Restart the server (QUIC requires restart for new TLS config)
|
|
systemctl restart qpc-server
|
|
|
|
# 8. Verify the server started with the new certificate
|
|
journalctl -u qpc-server --since "1 min ago" | grep -i tls
|
|
```
|
|
|
|
### Self-Signed Certificate (Development)
|
|
|
|
In non-production mode, the server auto-generates a self-signed certificate if none exists. To force regeneration:
|
|
|
|
```bash
|
|
rm data/server-cert.der data/server-key.der
|
|
systemctl restart qpc-server
|
|
# Server will generate a new self-signed cert for localhost/127.0.0.1/::1
|
|
```
|
|
|
|
### Automated Renewal with Certbot
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# /opt/qpc/scripts/renew-cert.sh
|
|
set -euo pipefail
|
|
|
|
DOMAIN="chat.example.com"
|
|
CERT_DIR="/etc/letsencrypt/live/$DOMAIN"
|
|
QPC_DATA="/var/lib/quicprochat"
|
|
|
|
certbot renew --quiet
|
|
|
|
openssl x509 -in "$CERT_DIR/fullchain.pem" -outform DER -out "$QPC_DATA/server-cert.der"
|
|
openssl pkey -in "$CERT_DIR/privkey.pem" -outform DER -out "$QPC_DATA/server-key.der"
|
|
chmod 600 "$QPC_DATA/server-key.der"
|
|
chown qpc:qpc "$QPC_DATA/server-cert.der" "$QPC_DATA/server-key.der"
|
|
|
|
systemctl restart qpc-server
|
|
```
|
|
|
|
```cron
|
|
# Run cert renewal check twice daily
|
|
0 3,15 * * * /opt/qpc/scripts/renew-cert.sh >> /var/log/qpc-cert-renew.log 2>&1
|
|
```
|
|
|
|
## Federation Certificate Rotation
|
|
|
|
Federation uses mutual TLS (mTLS) with a shared CA for server-to-server authentication.
|
|
|
|
### Procedure
|
|
|
|
```bash
|
|
# 1. Generate a new federation certificate signed by the federation CA
|
|
openssl req -new -nodes -keyout /tmp/federation-key.pem \
|
|
-out /tmp/federation.csr -subj "/CN=chat.example.com"
|
|
|
|
openssl x509 -req -in /tmp/federation.csr \
|
|
-CA federation-ca.pem -CAkey federation-ca-key.pem \
|
|
-CAcreateserial -days 365 -out /tmp/federation-cert.pem
|
|
|
|
# 2. Convert to DER
|
|
openssl x509 -in /tmp/federation-cert.pem -outform DER -out data/federation-cert.der
|
|
openssl pkey -in /tmp/federation-key.pem -outform DER -out data/federation-key.der
|
|
chmod 600 data/federation-key.der
|
|
|
|
# 3. Restart the server
|
|
systemctl restart qpc-server
|
|
|
|
# 4. Coordinate with federation peers: they must trust the same CA
|
|
```
|
|
|
|
## Database Encryption Key Rotation
|
|
|
|
The SQLCipher database key (`QPC_DB_KEY`) encrypts all data at rest.
|
|
|
|
### Procedure (SQLCipher PRAGMA rekey)
|
|
|
|
```bash
|
|
# 1. Stop the server
|
|
systemctl stop qpc-server
|
|
|
|
# 2. Back up the database
|
|
cp data/qpc.db /backups/qpc-pre-rekey-$(date +%Y%m%d).db
|
|
|
|
# 3. Rekey the database
|
|
sqlite3 data/qpc.db <<EOF
|
|
PRAGMA key = 'old-encryption-key';
|
|
PRAGMA rekey = 'new-encryption-key';
|
|
EOF
|
|
|
|
# 4. Verify the database opens with the new key
|
|
sqlite3 data/qpc.db "PRAGMA key = 'new-encryption-key'; PRAGMA integrity_check;"
|
|
|
|
# 5. Update the environment/config with the new key
|
|
# Option A: systemd
|
|
systemctl edit qpc-server --force
|
|
# Environment=QPC_DB_KEY=new-encryption-key
|
|
|
|
# Option B: Docker Compose .env
|
|
echo "QPC_DB_KEY=new-encryption-key" >> .env
|
|
|
|
# 6. Start the server
|
|
systemctl start qpc-server
|
|
```
|
|
|
|
### Full Re-encryption (Alternative)
|
|
|
|
If `PRAGMA rekey` is unavailable or you want a fresh database file:
|
|
|
|
```bash
|
|
# 1. Stop the server and back up
|
|
systemctl stop qpc-server
|
|
cp data/qpc.db /backups/qpc-pre-rekey.db
|
|
|
|
# 2. Export with old key, import with new key
|
|
sqlite3 data/qpc.db "PRAGMA key='old-key'; .dump" | \
|
|
sqlite3 data/qpc-new.db "PRAGMA key='new-key'; .read /dev/stdin"
|
|
|
|
# 3. Replace the database
|
|
mv data/qpc-new.db data/qpc.db
|
|
|
|
# 4. Update config and restart
|
|
systemctl start qpc-server
|
|
```
|
|
|
|
## OPAQUE ServerSetup Rotation
|
|
|
|
The OPAQUE ServerSetup is generated once and persisted. Rotating it invalidates all registered OPAQUE credentials.
|
|
|
|
**WARNING: Rotating the OPAQUE ServerSetup requires all users to re-register. Only do this if the setup is compromised.**
|
|
|
|
```bash
|
|
# 1. Stop the server
|
|
systemctl stop qpc-server
|
|
|
|
# 2. Back up the database
|
|
cp data/qpc.db /backups/qpc-pre-opaque-rotate.db
|
|
|
|
# 3. Delete the persisted OPAQUE setup
|
|
# For SQL backend:
|
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; DELETE FROM server_state WHERE key = 'opaque_setup';"
|
|
|
|
# For file backend:
|
|
rm data/opaque_setup.bin 2>/dev/null || true
|
|
|
|
# 4. Start the server (it will generate a new OPAQUE ServerSetup)
|
|
systemctl start qpc-server
|
|
|
|
# 5. All users must re-register (existing OPAQUE credentials are invalid)
|
|
```
|
|
|
|
## Server Signing Key Rotation
|
|
|
|
The Ed25519 signing key is used for delivery proofs. Rotating it means old delivery proofs cannot be verified against the new key.
|
|
|
|
```bash
|
|
# 1. Stop the server
|
|
systemctl stop qpc-server
|
|
|
|
# 2. Back up
|
|
cp data/qpc.db /backups/qpc-pre-sigkey-rotate.db
|
|
|
|
# 3. Delete the persisted signing key seed
|
|
# For SQL backend:
|
|
sqlite3 data/qpc.db "PRAGMA key='${QPC_DB_KEY}'; DELETE FROM server_state WHERE key = 'signing_key_seed';"
|
|
|
|
# 4. Start the server (generates a new Ed25519 signing key)
|
|
systemctl start qpc-server
|
|
```
|
|
|
|
## Rotation Schedule
|
|
|
|
| Key Material | Rotation Frequency | Impact |
|
|
|---|---|---|
|
|
| Auth token | Quarterly or on compromise | Clients using bearer auth must update |
|
|
| TLS certificate | Before expiry (automate with certbot) | Server restart required |
|
|
| Federation cert | Annually or before expiry | Coordinate with peers |
|
|
| DB encryption key | Annually or on compromise | Server downtime required |
|
|
| OPAQUE ServerSetup | Only on compromise | All users must re-register |
|
|
| Server signing key | Only on compromise | Old delivery proofs unverifiable |
|