Wraps quinn 0.11 in cluster/transport.rs with rustls 0.23 + rcgen 0.13 for identity management. Every peer connection is mTLS: both sides must present certs signed by the shared fleet CA, and rustls verifies the peer's cert SAN matches the requested server name. Public API on `cluster::transport`: - `NodeIdentity` (cert chain + private key + trusted CA) - `NodeIdentity::generate_test_pair(a, b)` — ephemeral CA + two signed leaves; shape matches the production PEM-file loader (Phase 1d) - `QuicServer::bind(addr, identity)` — bind mTLS-enforced listener - `QuicServer::accept()` — accept one connection (Option<Result<_>>) - `QuicClient::new(local_addr, identity)` — build client endpoint - `QuicClient::connect(peer_addr, expected_name)` — outbound with SAN check - `ping(&conn, payload)` — bidi stream RPC; server echoes as `pong:<payload>` - `ping_handler_loop(conn)` — server-side accept/echo forever - `CLAWSTOR_RPC_ALPN` constant, single ALPN "clawstor-rpc/1" Config extension: - `bind_rpc_lan`, `bind_rpc_tailscale` on `ClusterConfig` (both optional). - Defaults: gossip port + 1 (so gossip UDP and QUIC UDP don't collide). - Helper: `ClusterConfig::rpc_lan()`, `rpc_tailscale()`, `PeerEntry::rpc_lan()`, `rpc_tailscale()`. Gossip.rs now advertises the RPC address, not the gossip address, on the well-known `clawstor.rpc.lan` / `clawstor.rpc.tailscale` keys. CLI: - `cluster-ping --name <me> --peer <you> --rpc-addr <addr> [--payload X]` Runs a full mTLS handshake and single ping. For dev/loopback use today (both sides need to share a CA); persistent-identity ping lands in Phase 1d. Tests (6 new, real UDP + TLS handshake, no mocks): - generate_test_pair produces two distinct leaves that share a CA - ping_pong_between_two_mtls_peers: real 2-node QUIC round trip with full mTLS chain verification, `open_bi()`/`accept_bi()`, byte-exact response check - client_rejects_peer_with_wrong_ca: TLS chain verification failure when the server presents a cert signed by a different CA - client_rejects_wrong_server_name: SAN mismatch is enforced - server_binds_wildcard_and_reports_concrete_local_addr: port 0 → real - ping_rejects_oversize_payload: MAX_MESSAGE_BYTES cap enforced client-side Fixed-port allocator range 42000+ so transport tests don't conflict with gossip tests (41000+). 72 tests pass. Pre-existing `hot::test_project_target_size_bytes` macOS-only failure unchanged. File sizes (all under 1300-line ceiling): - cluster/transport.rs: 485 - cluster/gossip.rs: 570 - cluster.rs: 275 - config.rs: 480 - main.rs: 564 Follow-on Phase 1 cut (1d): - Load NodeIdentity from persistent PEM files at /etc/claw-store/tls/ - Fleet CA bootstrap ceremony (rcgen → write CA cert; per-node leaf CSR) - Daemon-level RPC server that runs alongside gossip + serves real operations (blob get/put, metadata sync) - cluster-status reads from live daemon via API instead of standalone
51 lines
2.0 KiB
TOML
51 lines
2.0 KiB
TOML
[package]
|
|
name = "claw-store"
|
|
version = "0.3.0"
|
|
edition = "2021"
|
|
|
|
[[bin]]
|
|
name = "claw-store"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
clap = { version = "4", features = ["derive"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
toml = "0.8"
|
|
tokio = { version = "1", features = ["full"] }
|
|
sysinfo = "0.30"
|
|
anyhow = "1"
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
axum = { version = "0.7", features = ["macros"] }
|
|
tower-http = { version = "0.5", features = ["cors", "fs"] }
|
|
tokio-stream = "0.1"
|
|
serde_json = "1"
|
|
# v0.2.0 — flock(2) wrapper for atomic+locked manifest writes
|
|
# (manifest.rs). Already a transitive dep; declaring it directly
|
|
# makes the call site obvious.
|
|
libc = "0.2"
|
|
# v0.2.0 — sibling-tempfile + rename for atomic manifest persistence.
|
|
# Was previously dev-dep only; promoted to main.
|
|
tempfile = "3"
|
|
# v0.11.1 — cluster membership via scuttlebutt gossip + phi-accrual failure
|
|
# detection. Wraps in cluster/gossip.rs. UDP transport, keyed KV state per
|
|
# node, seed_nodes bootstrap from [[cluster.peers]] config.
|
|
chitchat = "0.11"
|
|
# v0.11 — QUIC transport for peer RPC (Phase 1c). UDP-based; runs on a
|
|
# separate port from chitchat gossip. TLS 1.3 by default; we wire mTLS
|
|
# against a fleet root CA in cluster/transport.rs.
|
|
quinn = { version = "0.11", default-features = false, features = ["runtime-tokio", "rustls-ring"] }
|
|
# v0.23 — TLS 1.3 provider driving quinn's crypto. Pinned to the ring
|
|
# provider so a single CryptoProvider is installed process-wide.
|
|
rustls = { version = "0.23", default-features = false, features = ["ring"] }
|
|
# v0.13 — X.509 cert generation for the fleet CA + per-node leaf certs.
|
|
# Used both by production bootstrap (writes PEM to /etc/claw-store/tls/)
|
|
# and by tests (in-memory ephemeral CA).
|
|
rcgen = { version = "0.13", features = ["pem"] }
|
|
|
|
[dev-dependencies]
|
|
tempfile = "3"
|
|
tower = { version = "0.5", features = ["util"] }
|
|
http-body-util = "0.1"
|