feat(core): document three remote roles and client-side verifier selection (core/three-remote-roles-docs)
This commit is contained in:
@@ -1,7 +1,53 @@
|
|||||||
//! Authentication: `AuthContext`, `Identity`, `IdentityProvider`, `AuthToken`,
|
//! Authentication: `AuthContext`, `Identity`, `IdentityProvider`, `AuthToken`,
|
||||||
//! `ConfigIdentityProvider`.
|
//! `ConfigIdentityProvider`.
|
||||||
//!
|
//!
|
||||||
//! See `docs/architecture/crates/core/auth.md` for the full specification.
|
//! See `docs/architecture/crates/core/auth.md` for the full specification and
|
||||||
|
//! [ADR-034](../../../docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md)
|
||||||
|
//! for the three-remote-roles decision.
|
||||||
|
//!
|
||||||
|
//! # Three remote roles (ADR-034 §1)
|
||||||
|
//!
|
||||||
|
//! The three credential types (`PeerEntry.fingerprints` entries) describe how
|
||||||
|
//! a *single* `PeerEntry` can be authenticated. Separately, there are three
|
||||||
|
//! distinct remote roles that must not be conflated:
|
||||||
|
//!
|
||||||
|
//! | Role | Identity | alknet peer? | `PeerEntry` on local side? |
|
||||||
|
//! |------|----------|--------------|----------------------------|
|
||||||
|
//! | **Public X.509 endpoint** | Domain + CA-issued X.509 | No (local node is a client) | No |
|
||||||
|
//! | **Transport relay** (iroh's DERP-equivalent) | iroh `NodeId` (Ed25519) | No (infrastructure) | No |
|
||||||
|
//! | **Hub / hosting node** | Ed25519 raw key **and/or** X.509 | Yes (full peer) | Yes |
|
||||||
|
//!
|
||||||
|
//! `PeerEntry` (and the `PeerId` it resolves to) is the model for peers in
|
||||||
|
//! the call-protocol peer graph (ADR-029) — peers that get a stable logical
|
||||||
|
//! identity, are addressable via `PeerRef::Specific`, and whose ops land in
|
||||||
|
//! the peer-keyed overlay. A pure-client connection to a public X.509
|
||||||
|
//! endpoint (e.g. a third-party API) is **not** in that graph on the client
|
||||||
|
//! side: no `PeerEntry`, no `PeerId`, no `PeerRef::Specific` routing. The
|
||||||
|
//! asymmetry is deliberate — a public domain's operator can change hands, so
|
||||||
|
//! there is no stable logical identity to attach.
|
||||||
|
//!
|
||||||
|
//! The hub case is an ordinary `PeerEntry` that happens to expose both an
|
||||||
|
//! Ed25519 fingerprint (P2P path) and an X.509 fingerprint
|
||||||
|
//! (`SHA256:<hex>`, WebTransport/HTTPS path) — already supported by
|
||||||
|
//! `PeerEntry.fingerprints: Vec<String>` (ADR-030).
|
||||||
|
//!
|
||||||
|
//! # Client-side verifier selection (ADR-034 §3)
|
||||||
|
//!
|
||||||
|
//! The `CallClient` / `from_openapi` / `from_mcp` client-side
|
||||||
|
//! `ServerCertVerifier` is selected by **whether the local node has a
|
||||||
|
//! `PeerEntry` for the remote**, not by key type alone:
|
||||||
|
//!
|
||||||
|
//! | Local has `PeerEntry` for remote? | Remote cert type | Client verifier |
|
||||||
|
//! |----------------------------------|------------------|-----------------|
|
||||||
|
//! | No (public X.509 endpoint) | X.509 | `WebPkiServerVerifier` (CA verification) |
|
||||||
|
//! | No | Ed25519 raw key | fails closed (no CA to fall back to) |
|
||||||
|
//! | Yes (hub, Ed25519 path) | Ed25519 raw key | fingerprint match (`ed25519:<hex>`) |
|
||||||
|
//! | Yes (hub, X.509 path) | X.509 | fingerprint match (`SHA256:<hex>`) |
|
||||||
|
//!
|
||||||
|
//! This is the key-type-aware verifier from OQ-29, with the peer-model
|
||||||
|
//! criterion (ADR-034) made explicit. The client-side verifier selection is
|
||||||
|
//! a `CallClient` concern (`call/call-client-verifier-selection`), not an
|
||||||
|
//! `IdentityProvider` concern — `IdentityProvider` is unchanged by ADR-034.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|||||||
@@ -1,6 +1,31 @@
|
|||||||
//! Endpoint: `AlknetEndpoint`, `HandlerRegistry`, `EndpointError`.
|
//! Endpoint: `AlknetEndpoint`, `HandlerRegistry`, `EndpointError`.
|
||||||
//!
|
//!
|
||||||
//! See `docs/architecture/crates/core/endpoint.md` for the full specification.
|
//! See `docs/architecture/crates/core/endpoint.md` for the full specification
|
||||||
|
//! and [ADR-034](../../../docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md)
|
||||||
|
//! for the three-remote-roles decision.
|
||||||
|
//!
|
||||||
|
//! # Server-side vs client-side verifier concerns (ADR-034)
|
||||||
|
//!
|
||||||
|
//! This module's `AcceptAnyCertVerifier` is a **server-side** `ClientCertVerifier`
|
||||||
|
//! used in "request-but-don't-require" mode: the server asks for a client TLS
|
||||||
|
//! cert (X.509 or RFC 7250 raw key) so it can extract the fingerprint via
|
||||||
|
//! `peer_identity()`, but it does not require one and does not verify the
|
||||||
|
//! presented cert against a CA. The cert bytes are hashed to a fingerprint
|
||||||
|
//! string and matched against `PeerEntry.fingerprints` by
|
||||||
|
//! `IdentityProvider::resolve_from_fingerprint()`. Alknet's identity model is
|
||||||
|
//! fingerprint-based, not PKI-based — the `PeerEntry` set is the trust anchor,
|
||||||
|
//! not a root CA store.
|
||||||
|
//!
|
||||||
|
//! ADR-034 does **not** change the server-side endpoint. The **client-side**
|
||||||
|
//! `ServerCertVerifier` (for outgoing connections) is selected by `PeerEntry`
|
||||||
|
//! presence (ADR-034 §3): known peer (`PeerEntry` present) → fingerprint pin;
|
||||||
|
//! unknown X.509 remote (`PeerEntry` absent) → CA verification
|
||||||
|
//! (`WebPkiServerVerifier`); unknown Ed25519 raw-key remote → fail closed.
|
||||||
|
//! That selection is a `CallClient` concern
|
||||||
|
//! (`call/call-client-verifier-selection`), not an endpoint concern —
|
||||||
|
//! `AcceptAnyCertVerifier` here is only safe for raw-key fingerprint
|
||||||
|
//! extraction on the *server* side and must not be reused as a client-side
|
||||||
|
//! verifier.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -740,6 +765,19 @@ fn generate_self_signed_cert() -> Result<SelfSignedCert, EndpointError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "quinn")]
|
#[cfg(feature = "quinn")]
|
||||||
|
/// Server-side "request-but-don't-require" client cert verifier (ADR-034).
|
||||||
|
///
|
||||||
|
/// Asks for a client TLS cert (X.509 or RFC 7250 raw key) so the endpoint can
|
||||||
|
/// extract the fingerprint via `peer_identity()`, but does not require one
|
||||||
|
/// and does not verify the presented cert against a CA. The fingerprint is
|
||||||
|
/// matched against `PeerEntry.fingerprints` by
|
||||||
|
/// `IdentityProvider::resolve_from_fingerprint()`.
|
||||||
|
///
|
||||||
|
/// **Server-side only.** This must not be reused as a client-side
|
||||||
|
/// `ServerCertVerifier` — the client-side verifier is selected by `PeerEntry`
|
||||||
|
/// presence (ADR-034 §3): CA verification for unknown X.509 remotes,
|
||||||
|
/// fingerprint pinning for known peers. See the module docs and
|
||||||
|
/// `call/call-client-verifier-selection`.
|
||||||
struct AcceptAnyCertVerifier;
|
struct AcceptAnyCertVerifier;
|
||||||
|
|
||||||
#[cfg(feature = "quinn")]
|
#[cfg(feature = "quinn")]
|
||||||
|
|||||||
Reference in New Issue
Block a user