Three tasks implementing ADR-027: 1. core/rawkey-decouple-from-iroh: TlsIdentity::RawKey now uses Ed25519SecretKey (alknet-core-owned wrapper over ed25519_dalek) instead of iroh::SecretKey. RawKeyCertResolver and Ed25519SigningKey un-gated from #[cfg(all(quinn, iroh))] to #[cfg(quinn)] only. Quinn-only builds (default) now support RFC 7250 raw-key identity. iroh transport converts via iroh::SecretKey::from_bytes. 2. core/endpoint-request-client-cert: replaced with_no_client_auth() with AcceptAnyCertVerifier — a custom ClientCertVerifier that requests client certs but doesn't require them or verify against a CA. alknet's identity model is fingerprint-based (the authorized_fingerprints set is the trust anchor), not PKI-based. Peer certs are extracted at the TLS layer for fingerprinting; peers without certs connect normally. 3. core/acme-integration: TlsIdentity::Acme variant (domains, cache_dir, directory, contact) + AcmeDirectory enum. TlsSetup two-phase construction: synchronous for X509/RawKey/SelfSigned, async for Acme (spawns AcmeState event loop, builds ServerConfig with ResolvesServerCertAcme). acme-tls/1 ALPN added when ACME is active; dispatch_quinn guard closes challenge connections gracefully (challenge is TLS-layer-handled). acme feature gate keeps rustls-acme out of non-ACME builds. Workspace: build/test/clippy green across all 3 feature configs (quinn-only, quinn+iroh, quinn+acme, all-features). 331 tests, 0 failures, 0 warnings.
4.8 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| core/endpoint-request-client-cert | Switch rustls ServerConfig from with_no_client_auth to request-but-don't-require client certs | completed |
|
narrow | medium | component | implementation |
Description
core/endpoint-client-fingerprint landed the extraction logic: when a
client certificate is presented, dispatch_quinn / dispatch_iroh
extract the fingerprint and populate AuthContext. However, the server
still builds rustls::ServerConfig with with_no_client_auth() in all
three TlsIdentity branches (endpoint.rs:477, 490, 501), so the
server never requests a client cert. Extraction is a safe no-op until
this task changes the server-side TLS config.
This follow-up switches from with_no_client_auth() to a
request-but-don't-require mode so that peers presenting a client cert
(X.509 or RFC 7250 raw Ed25519 key) flow through the extraction path
landed in the predecessor task, while peers without a cert still connect
without regression.
Design decision: how to request-but-not-require
rustls does not have a direct with_optional_client_auth() builder.
The standard approach is:
- Build the config with
.with_client_auth(verifier)whereverifieris a customServerCertVerifierthat accepts any presentation (returnsOk(Certified::yes())when a cert is presented,Ok(Certified::no())when none is presented — rustls 0.23'sWebPkiServerVerifiercannot be used directly for optional auth). - Alternatively, use
rustls::server::WebPkiServerVerifierwith aNoClientAuthfallback — check the exact rustls API available in the pinned version before implementing.
Read the rustls API docs for the pinned version
(rustls::server::ServerConfig::builder_with_provider) and confirm the
correct verifier construction. The key property: a peer may present a
cert, and if it does, peer_identity() returns it; if it doesn't, the
connection still succeeds.
iroh path
iroh's Endpoint builder uses its own TLS session internally. For the
raw-key path (TlsIdentity::RawKey), iroh already advertises
only_raw_public_keys() via RawKeyCertResolver — the server-side half
of RFC 7250. The client-side presentation is set by the client's
rustls::ClientConfig, not the server. So the iroh path may already
receive peer identities when the client is an iroh node (the NodeId is
always in the TLS cert). Verify: does Connection::remote_node_id()
already work for iroh connections today, or does it require the server to
request client certs? If iroh always presents a cert (raw-key mode), no
server-side change is needed for the iroh path — only quinn/X.509 needs
this task. Confirm before implementing.
Acceptance Criteria
build_rustls_server_configuses request-but-don't-require client auth (notwith_no_client_auth()) for at least the X.509 path- Peer presenting a client cert:
peer_identity()returns the cert chain → fingerprint extraction works end-to-end - Peer without a client cert: connection still succeeds,
tls_client_fingerprintisNone(no regression) - iroh path: confirm whether a server-side change is needed; if yes, apply it; if no, document why
- Integration test: quinn endpoint with a client that presents a cert →
AuthContext.tls_client_fingerprintisSome(SHA256:...) - Integration test: quinn endpoint with a client that presents no cert →
AuthContext.tls_client_fingerprintisNoneand connection succeeds cargo test -p alknet-core --all-featuressucceedscargo clippy -p alknet-core --all-features --all-targetssucceeds with no warningsauth.mdupdated: server-config decision documented (request-but-don't-require, not no-client-auth)
References
- tasks/core/endpoint-client-fingerprint.md — predecessor task (landed extraction, deferred this config change)
- crates/alknet-core/src/endpoint.rs:477, 490, 501 — the three
with_no_client_auth()sites - crates/alknet-core/src/endpoint.rs —
extract_quinn_client_fingerprint/extract_iroh_client_fingerprint(already landed, waiting for certs to flow) - docs/architecture/crates/core/auth.md — fingerprint format table and endpoint-level resolution flow
- docs/architecture/decisions/004-auth-as-shared-core.md — ADR-004 (hybrid resolution)
- docs/architecture/open-questions.md — OQ-12 (TLS identity provisioning)
Notes
Split from
core/endpoint-client-fingerprintper the task's own suggestion: extraction is correct either way (returnsNonewhen no cert), so landing it first is a safe no-op. This task is the behavioral change that makes fingerprints actually flow. The risk is medium because it alters the TLS handshake for every connection — ensure the no-cert-peer case has explicit test coverage.