feat: Sprint 1 — production hardening, TLS lifecycle, CI coverage, lint cleanup
- Fix 3 client panics: replace .unwrap()/.expect() with proper error handling in rpc.rs (AUTH_CONTEXT lock), repl.rs (pending_member), and retry.rs (last_err) - Add --danger-accept-invalid-certs flag with InsecureServerCertVerifier for development TLS bypass, plus mdBook TLS documentation - Add CI coverage job (cargo-tarpaulin) and Docker build validation to GitHub Actions workflow, plus README CI badge - Add [workspace.lints] config, fix 46 clippy warnings across 8 crates, zero warnings on all buildable crates - Update Dockerfile for all 11 workspace members
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
- [Building from Source](getting-started/building.md)
|
||||
- [Running the Server](getting-started/running-the-server.md)
|
||||
- [Running the Client](getting-started/running-the-client.md)
|
||||
- [TLS in quicproquo](getting-started/tls.md)
|
||||
- [Certificate Lifecycle and CA-Signed TLS](getting-started/certificate-lifecycle.md)
|
||||
- [Docker Deployment](getting-started/docker.md)
|
||||
- [Bot SDK](getting-started/bot-sdk.md)
|
||||
|
||||
100
docs/src/getting-started/tls.md
Normal file
100
docs/src/getting-started/tls.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# TLS in quicproquo
|
||||
|
||||
quicproquo uses QUIC (RFC 9000) for all client-server communication. QUIC mandates TLS 1.3, so every connection is encrypted and authenticated at the transport layer — there is no plaintext mode.
|
||||
|
||||
## How it works
|
||||
|
||||
The server holds a TLS certificate and private key (DER format). On startup it either loads existing files or, in development mode, generates a self-signed certificate automatically. The client authenticates the server by verifying its certificate against a trusted root provided via `--ca-cert` (or `QPQ_CA_CERT`).
|
||||
|
||||
The TLS handshake negotiates the ALPN protocol `capnp`, after which the QUIC bi-directional stream carries Cap'n Proto RPC traffic.
|
||||
|
||||
## Certificate pinning with `--ca-cert`
|
||||
|
||||
By default the client trusts exactly the certificate (or CA) in the file given by `--ca-cert`:
|
||||
|
||||
```bash
|
||||
qpq --ca-cert data/server-cert.der --server-name localhost health --server 127.0.0.1:7000
|
||||
```
|
||||
|
||||
This is a form of **certificate pinning**: the client will only connect to a server whose certificate chains to the provided root. For single-server deployments, pass the server's own self-signed certificate. For CA-issued certificates, pass the CA's root certificate instead.
|
||||
|
||||
| Flag / Env var | Purpose |
|
||||
|---|---|
|
||||
| `--ca-cert` / `QPQ_CA_CERT` | Path to trusted root certificate (DER) |
|
||||
| `--server-name` / `QPQ_SERVER_NAME` | Expected TLS server name (must match certificate SAN) |
|
||||
|
||||
## The `--danger-accept-invalid-certs` flag
|
||||
|
||||
For local development and testing you can skip certificate verification entirely:
|
||||
|
||||
```bash
|
||||
qpq --danger-accept-invalid-certs health --server 127.0.0.1:7000
|
||||
```
|
||||
|
||||
Or via the environment:
|
||||
|
||||
```bash
|
||||
export QPQ_DANGER_ACCEPT_INVALID_CERTS=true
|
||||
qpq health --server 127.0.0.1:7000
|
||||
```
|
||||
|
||||
When active, the client prints a warning to stderr:
|
||||
|
||||
```
|
||||
WARNING: TLS verification disabled — insecure mode
|
||||
```
|
||||
|
||||
**Never use this flag in production.** It disables all certificate checks, making the connection vulnerable to man-in-the-middle attacks. It exists solely so that developers can iterate without managing certificates during local testing.
|
||||
|
||||
## Generating self-signed certificates for development
|
||||
|
||||
### Using rcgen (Rust)
|
||||
|
||||
The server generates a self-signed certificate automatically when the cert/key files are missing (unless `QPQ_PRODUCTION=1` is set). The generated files are written to:
|
||||
|
||||
- `data/server-cert.der` — DER-encoded certificate
|
||||
- `data/server-key.der` — DER-encoded PKCS#8 private key
|
||||
|
||||
### Using openssl
|
||||
|
||||
To generate a self-signed certificate manually:
|
||||
|
||||
```bash
|
||||
# Generate a private key and self-signed certificate (PEM)
|
||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
|
||||
-keyout key.pem -out cert.pem -days 365 -nodes \
|
||||
-subj "/CN=localhost" \
|
||||
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||
|
||||
# Convert to DER format (required by quicproquo)
|
||||
openssl x509 -in cert.pem -outform DER -out data/server-cert.der
|
||||
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out data/server-key.der -nocrypt
|
||||
```
|
||||
|
||||
Point the server at the DER files:
|
||||
|
||||
```bash
|
||||
export QPQ_TLS_CERT=data/server-cert.der
|
||||
export QPQ_TLS_KEY=data/server-key.der
|
||||
cargo run -p quicproquo-server
|
||||
```
|
||||
|
||||
And the client at the certificate:
|
||||
|
||||
```bash
|
||||
qpq --ca-cert data/server-cert.der --server-name localhost repl
|
||||
```
|
||||
|
||||
## CA-issued certificates
|
||||
|
||||
For production deployments with a public CA (e.g. Let's Encrypt):
|
||||
|
||||
1. Obtain the certificate and key (e.g. via certbot).
|
||||
2. Convert to DER format as shown above.
|
||||
3. Configure the client to trust the CA root rather than the server certificate directly:
|
||||
|
||||
```bash
|
||||
qpq --ca-cert /etc/ssl/certs/isrg-root-x1.der --server-name chat.example.com repl
|
||||
```
|
||||
|
||||
See [Certificate Lifecycle and CA-Signed TLS](certificate-lifecycle.md) for rotation, OCSP, and operational details.
|
||||
Reference in New Issue
Block a user