From a33580cec722fd7717809ae624342db223d3897d Mon Sep 17 00:00:00 2001 From: John Coffey Date: Fri, 30 Oct 2026 19:06:22 -0700 Subject: [PATCH] Relay: a new link CA without a new join (RL-34) An update of any inbuxa:Relay with replaceLinkCa, sent on its own, acts on the link CA every relay shares: start makes the next CA, finish makes it the link CA, cancel drops it. Every relay shows linkCaReplacing and its own linkCa (current, both, or old: it needs a new join). While one is under way, each node pushes the next CA's pin as nextLinkCaPin, and the relay trusts both, listing them in status.linkCaPins. Once every enabled, joined relay says both, the link service finishes it (relay.link-ca-replaced, event 659): each node takes a certificate from the new CA and links again. Its push of nextLinkCaPin: null, from a link on the next CA, makes that CA the relay's only one: saved in its identity, every link on the old CA ended, and relay.link-ca-replaced recorded. From the current CA, null is a cancel; a link on the next CA can't name another. The link service now sends settings it has cleared as null: the relay merges a push, so one left out stayed as it was. That also fixes a certificate removed here staying on the relay. A join whose token named a CA since replaced shows linkCa old. Tested: the relay's end to end test (both CAs trusted, the refusals, the promotion, the old CA's link cut and refused), the server's relay link test against the real relay binary (start, finished by itself, the relay's identity on the new CA, still joined), relay_tests, and a unit test of the linkCa states. --- crates/features/src/relay/link.rs | 136 +++++++++++++++++ crates/features/src/relay/mod.rs | 149 +++++++++++++++++-- crates/jmap-proto/src/object/inbuxa_relay.rs | 15 ++ crates/jmap/src/inbuxa/relay.rs | 85 ++++++++++- crates/relay-proto/src/types.rs | 9 ++ crates/relay/src/app.rs | 33 ++++ crates/relay/src/link/jmap.rs | 53 ++++++- crates/relay/src/link/mod.rs | 41 ++++- crates/relay/src/link/tls.rs | 40 +++-- crates/relay/src/tests_e2e.rs | 63 ++++++++ crates/services/src/inbuxa_relay_link.rs | 37 ++++- crates/trc/src/event/enums.rs | 6 +- crates/trc/src/event/enums_impl.rs | 7 + docs/spec/features/relay.md | 31 +++- resources/schema/schema.json.gz | Bin 154994 -> 155068 bytes resources/schema/schema.json.sha256 | 2 +- tests/src/system/relay.rs | 57 +++++++ 17 files changed, 717 insertions(+), 47 deletions(-) diff --git a/crates/features/src/relay/link.rs b/crates/features/src/relay/link.rs index 601e9621..bdb5b7f2 100644 --- a/crates/features/src/relay/link.rs +++ b/crates/features/src/relay/link.rs @@ -38,6 +38,8 @@ pub struct NodeCertificate { pub key_der: Vec, pub ca_der: Vec, pub issued_at: u64, + /// The link CA that issued it: a new one means a new certificate. + pub ca_pin: Pin, } fn cert_error(err: rcgen::Error) -> trc::Error { @@ -83,6 +85,7 @@ pub fn issue_node_certificate( key_der: key.serialize_der(), ca_der, issued_at: now, + ca_pin: keys.link_ca_pin, }) } @@ -228,6 +231,8 @@ pub fn pushed_settings( settings.domains = plan.domains.keys().cloned().collect(); settings.sealing_key = Some(keys.sealing_public); settings.recipient_key = Some(relay.recipient_key); + // While the link CA is replaced, the relay trusts the next one too (RL-34) + settings.next_link_ca_pin = keys.next_link_ca.as_ref().map(|next| next.pin); settings.smtp_certificate = match (&relay.smtp_certificate, &relay.smtp_private_key) { (Some(chain_pem), Some(key_pem)) => Some(SmtpCertificate { chain_pem: chain_pem.clone(), @@ -314,6 +319,84 @@ pub async fn statuses(data: &Store, relay_id: u32) -> trc::Result> Ok(links) } +/// Which link CA a relay trusts, as it last said (RL-34). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LinkCa { + /// This server's. + Current, + /// This server's, and the one replacing it. + Both, + /// Only one this server replaced: it needs a new join. + Old, +} + +impl LinkCa { + pub fn as_str(&self) -> &'static str { + match self { + LinkCa::Current => "current", + LinkCa::Both => "both", + LinkCa::Old => "old", + } + } +} + +/// What a relay's links last said of the CAs it trusts, the newest report +/// first; `None` before any has. A relay still joining has said nothing, +/// but a token that named a CA since replaced can't complete. +pub fn link_ca(relay: &Relay, keys: &DownstreamKeys, links: &[NodeLink]) -> Option { + if relay.join.relay_pin.is_none() { + return relay + .join + .ca_pin + .filter(|pin| !pins_match(pin, &keys.link_ca_pin)) + .map(|_| LinkCa::Old); + } + let pins = links + .iter() + .filter_map(|link| Some((link.updated_at, &link.relay.as_ref()?.link_ca_pins))) + .filter(|(_, pins)| !pins.is_empty()) + .max_by_key(|(at, _)| *at)? + .1; + let trusts = |pin: &Pin| { + pins.iter() + .any(|p| *p == inbuxa_relay_proto::b64::encode(pin)) + }; + Some(if !trusts(&keys.link_ca_pin) { + LinkCa::Old + } else if keys + .next_link_ca + .as_ref() + .is_some_and(|next| trusts(&next.pin)) + { + LinkCa::Both + } else { + LinkCa::Current + }) +} + +/// Whether a link CA replacement can finish: every enabled relay that has +/// joined says it trusts the next CA too. A relay that never says so holds +/// it until an administrator finishes it anyway (RL-34). +pub async fn link_ca_ready( + data: &Store, + keys: &DownstreamKeys, + relays: &[Relay], +) -> trc::Result { + if keys.next_link_ca.is_none() { + return Ok(false); + } + for relay in relays + .iter() + .filter(|r| r.enabled && r.join.relay_pin.is_some()) + { + let links = statuses(data, relay.id).await?; + if link_ca(relay, keys, &links) != Some(LinkCa::Both) { + return Ok(false); + } + } + Ok(true) +} + /// Forgets every node's link to a relay that is gone. pub(super) fn clear_statuses(batch: &mut BatchBuilder, relay_id: u32, nodes: &[NodeLink]) { for link in nodes { @@ -519,4 +602,57 @@ mod tests { now + NODE_CERT_LIFETIME ); } + + /// RL-34: which CA a relay trusts, from what its links last said. + #[test] + fn link_ca_states() { + let keys = new_downstream_keys(time::now()).unwrap(); + let mut replacing = keys.clone(); + replacing.next_link_ca = Some(super::super::NextLinkCa { + key_pem: String::new(), + cert_pem: String::new(), + pin: [7; 32], + started_at: 1, + }); + let pin = |pin: &Pin| inbuxa_relay_proto::b64::encode(pin); + let said = |pins: Vec, at: u64| NodeLink { + relay: Some(RelayStatus { + link_ca_pins: pins, + ..Default::default() + }), + updated_at: at, + ..Default::default() + }; + let mut joined = relay(&[]); + joined.join.relay_pin = Some([1; 32]); + // Nothing said yet, or by a relay from before RL-34 + assert_eq!(link_ca(&joined, &keys, &[]), None); + assert_eq!(link_ca(&joined, &keys, &[said(vec![], 5)]), None); + let current = said(vec![pin(&keys.link_ca_pin)], 5); + assert_eq!( + link_ca(&joined, &keys, std::slice::from_ref(¤t)), + Some(LinkCa::Current) + ); + assert_eq!( + link_ca(&joined, &replacing, std::slice::from_ref(¤t)), + Some(LinkCa::Current) + ); + let both = said(vec![pin(&keys.link_ca_pin), pin(&[7; 32])], 9); + // The newest report wins + assert_eq!( + link_ca(&joined, &replacing, &[current.clone(), both.clone()]), + Some(LinkCa::Both) + ); + // Once finished, a relay that never took the new CA is left on the old + let finished = replacing.finished().unwrap(); + assert_eq!(finished.link_ca_pin, [7; 32]); + assert!(finished.next_link_ca.is_none()); + assert_eq!(link_ca(&joined, &finished, &[current]), Some(LinkCa::Old)); + assert_eq!(link_ca(&joined, &finished, &[both]), Some(LinkCa::Current)); + // A join whose token named the old CA can't complete + let mut joining = relay(&[]); + joining.start_join(&keys, time::now()); + assert_eq!(link_ca(&joining, &keys, &[]), None); + assert_eq!(link_ca(&joining, &finished, &[]), Some(LinkCa::Old)); + } } diff --git a/crates/features/src/relay/mod.rs b/crates/features/src/relay/mod.rs index 20ef599f..fab07d45 100644 --- a/crates/features/src/relay/mod.rs +++ b/crates/features/src/relay/mod.rs @@ -87,6 +87,55 @@ pub struct DownstreamKeys { #[serde(with = "b64")] pub sealing_public: [u8; 32], pub created_at: u64, + /// The CA replacing the link CA, while a replacement is under way + /// (RL-34). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_link_ca: Option, +} + +/// A link CA on its way in: pushed to the relays as `nextLinkCaPin` until +/// every joined relay trusts it, then the link CA. +#[derive(Clone, PartialEq, Eq, SerdeSerialize, SerdeDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct NextLinkCa { + pub key_pem: String, + pub cert_pem: String, + #[serde(with = "b64")] + pub pin: Pin, + pub started_at: u64, +} + +/// What an administrator asks of a link CA replacement (RL-34). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LinkCaChange { + Start, + Finish, + Cancel, +} + +impl LinkCaChange { + pub fn parse(text: &str) -> Option { + match text { + "start" => Some(LinkCaChange::Start), + "finish" => Some(LinkCaChange::Finish), + "cancel" => Some(LinkCaChange::Cancel), + _ => None, + } + } +} + +impl DownstreamKeys { + /// The keys with the next link CA as the link CA, if there is one. + pub fn finished(&self) -> Option { + let next = self.next_link_ca.as_ref()?; + Some(DownstreamKeys { + link_ca_key_pem: next.key_pem.clone(), + link_ca_cert_pem: next.cert_pem.clone(), + link_ca_pin: next.pin, + next_link_ca: None, + ..self.clone() + }) + } } impl std::fmt::Debug for DownstreamKeys { @@ -111,6 +160,10 @@ pub struct Join { pub relay_pin: Option, #[serde(skip_serializing_if = "Option::is_none")] pub joined_at: Option, + /// The link CA the token named. A join started before a replacement + /// finished can't complete after it (RL-34). + #[serde(with = "b64::option", skip_serializing_if = "Option::is_none")] + pub ca_pin: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, SerdeSerialize)] @@ -270,6 +323,7 @@ impl Relay { if settings.smtp_certificate.is_some() || settings.sealing_key.is_some() || settings.recipient_key.is_some() + || settings.next_link_ca_pin.is_some() { return invalid( "settings", @@ -353,6 +407,7 @@ impl Relay { expires_at: token.expires_at, relay_pin: None, joined_at: None, + ca_pin: Some(keys.link_ca_pin), }; token.encode() } @@ -370,6 +425,21 @@ pub fn random_key() -> [u8; 32] { /// New downstream keys: a P-256 link CA, and an X25519 sealing key pair. pub fn new_downstream_keys(now: u64) -> trc::Result { + let (key_pem, cert_pem, pin) = new_link_ca()?; + let (sealing_secret, sealing_public) = seal::generate_keypair(); + Ok(DownstreamKeys { + link_ca_pin: pin, + link_ca_key_pem: key_pem, + link_ca_cert_pem: cert_pem, + sealing_secret: *sealing_secret, + sealing_public, + created_at: now, + next_link_ca: None, + }) +} + +/// A new P-256 link CA: its key, its certificate, and its key's pin. +fn new_link_ca() -> trc::Result<(String, String, Pin)> { let failed = |err: rcgen::Error| { trc::EventType::Server(trc::ServerEvent::ThreadError) .into_err() @@ -391,15 +461,11 @@ pub fn new_downstream_keys(now: u64) -> trc::Result { params.not_before = rcgen::date_time_ymd(2026, 1, 1); params.not_after = rcgen::date_time_ymd(2126, 1, 1); let cert = params.self_signed(&key).map_err(failed)?; - let (sealing_secret, sealing_public) = seal::generate_keypair(); - Ok(DownstreamKeys { - link_ca_pin: spki_pin(&rcgen::PublicKeyData::subject_public_key_info(&key)), - link_ca_key_pem: key.serialize_pem(), - link_ca_cert_pem: cert.pem(), - sealing_secret: *sealing_secret, - sealing_public, - created_at: now, - }) + Ok(( + key.serialize_pem(), + cert.pem(), + spki_pin(&rcgen::PublicKeyData::subject_public_key_info(&key)), + )) } struct Json(T); @@ -540,6 +606,71 @@ pub async fn create(data: &Store, relay: &Relay, now: u64) -> trc::Result<(Relay /// stored bytes. struct Versioned(Relay, u64); +struct VersionedKeys(DownstreamKeys, u64); + +impl Deserialize for VersionedKeys { + fn deserialize(bytes: &[u8]) -> trc::Result { + let Json(keys) = Json::::deserialize(bytes)?; + Ok(VersionedKeys(keys, xxhash_rust::xxh3::xxh3_64(bytes))) + } +} + +/// Starts, finishes or cancels a replacement of the link CA (RL-34), and +/// says whether anything changed: starting one already under way, or +/// finishing or cancelling none, changes nothing. Finishing makes the next +/// CA the link CA; each node then takes a certificate from it, and the +/// relays that trust it make it their only one. +pub async fn change_link_ca(data: &Store, change: LinkCaChange, now: u64) -> trc::Result { + keys_or_new(data, now).await?; + loop { + let Some(VersionedKeys(keys, version)) = data + .get_value::(ValueKey::from(class(KIND_KEYS, None))) + .await + .caused_by(trc::location!())? + else { + return Ok(false); + }; + let next = match change { + LinkCaChange::Start if keys.next_link_ca.is_none() => { + let (key_pem, cert_pem, pin) = new_link_ca()?; + DownstreamKeys { + next_link_ca: Some(NextLinkCa { + key_pem, + cert_pem, + pin, + started_at: now, + }), + ..keys + } + } + LinkCaChange::Finish => match keys.finished() { + Some(next) => next, + None => return Ok(false), + }, + LinkCaChange::Cancel if keys.next_link_ca.is_some() => DownstreamKeys { + next_link_ca: None, + ..keys + }, + _ => return Ok(false), + }; + let mut batch = BatchBuilder::new(); + batch.assert_value(class(KIND_KEYS, None), AssertValue::Hash(version)); + batch.set(class(KIND_KEYS, None), Json(&next).serialize()?); + match data.write(batch.build_all()).await { + Ok(_) => { + changed(); + return Ok(true); + } + Err(err) + if matches!( + err.as_ref(), + trc::EventType::Store(trc::StoreEvent::AssertValueFailed) + ) => {} + Err(err) => return Err(err.caused_by(trc::location!())), + } + } +} + impl Deserialize for Versioned { fn deserialize(bytes: &[u8]) -> trc::Result { let Json(relay) = Json::::deserialize(bytes)?; diff --git a/crates/jmap-proto/src/object/inbuxa_relay.rs b/crates/jmap-proto/src/object/inbuxa_relay.rs index a5df6c81..c6589c88 100644 --- a/crates/jmap-proto/src/object/inbuxa_relay.rs +++ b/crates/jmap-proto/src/object/inbuxa_relay.rs @@ -56,6 +56,15 @@ pub enum RelayProperty { /// Whether a linked relay runs an older release than this server /// (RL-28); null before any link has said. OlderThanServer, + /// Update only, sent on its own: `start`, `finish` or `cancel` a + /// replacement of the link CA every relay shares (RL-34). + ReplaceLinkCa, + /// Which link CA the relay last said it trusts: `current`, `both` + /// (the current and the next, during a replacement) or `old` (one this + /// server replaced: it needs a new join); null before it has said. + LinkCa, + /// When a replacement of the link CA began, while one is under way. + LinkCaReplacing, /// In a create or rejoin response only. JoinToken, /// Each node's link to the relay, and what the relay last said. @@ -102,6 +111,9 @@ impl Property for RelayProperty { RelayProperty::Rejoin => "rejoin", RelayProperty::Upgrade => "upgrade", RelayProperty::OlderThanServer => "olderThanServer", + RelayProperty::ReplaceLinkCa => "replaceLinkCa", + RelayProperty::LinkCa => "linkCa", + RelayProperty::LinkCaReplacing => "linkCaReplacing", RelayProperty::JoinToken => "joinToken", RelayProperty::Links => "links", RelayProperty::CreatedBy => "createdBy", @@ -136,6 +148,9 @@ impl RelayProperty { b"rejoin" => RelayProperty::Rejoin, b"upgrade" => RelayProperty::Upgrade, b"olderThanServer" => RelayProperty::OlderThanServer, + b"replaceLinkCa" => RelayProperty::ReplaceLinkCa, + b"linkCa" => RelayProperty::LinkCa, + b"linkCaReplacing" => RelayProperty::LinkCaReplacing, b"joinToken" => RelayProperty::JoinToken, b"links" => RelayProperty::Links, b"createdBy" => RelayProperty::CreatedBy, diff --git a/crates/jmap/src/inbuxa/relay.rs b/crates/jmap/src/inbuxa/relay.rs index 07b66e54..fa3fff1c 100644 --- a/crates/jmap/src/inbuxa/relay.rs +++ b/crates/jmap/src/inbuxa/relay.rs @@ -16,7 +16,8 @@ use crate::blob::download::BlobDownload; use common::{Server, auth::AccessToken, network::acme::relay::relay_certificate_not_after}; use inbuxa_features::relay::{ - self as relays, JoinState, Relay as StoredRelay, certificate::CertificateOrder, link::NodeLink, + self as relays, DownstreamKeys, JoinState, Relay as StoredRelay, certificate::CertificateOrder, + link::NodeLink, }; use inbuxa_relay_proto::{ b64, @@ -66,6 +67,8 @@ const ALL: &[P] = &[ P::LinkKeyPin, P::Links, P::OlderThanServer, + P::LinkCa, + P::LinkCaReplacing, P::CreatedBy, P::CreatedAt, P::UpdatedAt, @@ -86,6 +89,8 @@ const SERVER_SET: &[P] = &[ P::JoinToken, P::Links, P::OlderThanServer, + P::LinkCa, + P::LinkCaReplacing, P::CreatedBy, P::CreatedAt, P::UpdatedAt, @@ -172,6 +177,7 @@ fn to_value( relay: &StoredRelay, links: &[NodeLink], order: Option<&CertificateOrder>, + keys: Option<&DownstreamKeys>, properties: &[P], now: u64, ) -> RValue { @@ -207,6 +213,13 @@ fn to_value( P::LinkKeyPin => text(relay.join.relay_pin.map(|pin| b64::encode(&pin)).as_deref()), P::Links => links_value(links), P::OlderThanServer => older_than_server(links).map_or(Value::Null, Value::Bool), + P::LinkCa => text( + keys.and_then(|keys| relays::link::link_ca(relay, keys, links)) + .map(|ca| ca.as_str()), + ), + P::LinkCaReplacing => keys + .and_then(|keys| keys.next_link_ca.as_ref()) + .map_or(Value::Null, |next| date(next.started_at)), P::CreatedBy => text(Some(&relay.created_by)), P::CreatedAt => date(relay.created_at), P::UpdatedAt => date(relay.updated_at), @@ -225,6 +238,7 @@ struct Sent { settings: JsonMap, rejoin: bool, upgrade: Option, + replace_link_ca: Option, } /// A release to hand the relay: an upload, and its signature file's text. @@ -250,6 +264,21 @@ fn client_json(value: Value<'_, P, RelayValue>) -> Result> { .with_description("true, to start a new join.")); } }, + Key::Property(P::ReplaceLinkCa) => { + match value + .as_str() + .and_then(|text| relays::LinkCaChange::parse(&text)) + { + Some(change) => sent.replace_link_ca = Some(change), + None => { + return Err(SetError::invalid_properties() + .with_property(P::ReplaceLinkCa) + .with_description( + "start, finish or cancel a replacement of the link CA.", + )); + } + } + } Key::Property(P::Upgrade) => { let json: serde_json::Value = value.into(); match (json["blobId"].as_str(), json["signature"].as_str()) { @@ -460,6 +489,7 @@ pub async fn get( }; let data = server.store(); let all = relays::all(data).await?; + let keys = relays::keys(data).await?; let now = now(); let wanted: Vec<&StoredRelay> = match ids { None => all.iter().collect(), @@ -475,7 +505,10 @@ pub async fn get( } }; for relay in wanted { - let links = if properties.contains(&P::Links) || properties.contains(&P::OlderThanServer) { + let links = if properties.contains(&P::Links) + || properties.contains(&P::OlderThanServer) + || properties.contains(&P::LinkCa) + { relays::link::statuses(data, relay.id).await? } else { Vec::new() @@ -485,9 +518,14 @@ pub async fn get( } else { None }; - response - .list - .push(to_value(relay, &links, order.as_ref(), &properties, now)); + response.list.push(to_value( + relay, + &links, + order.as_ref(), + keys.as_ref(), + &properties, + now, + )); } Ok(response) } @@ -516,6 +554,11 @@ pub async fn set( .with_property(P::Upgrade) .with_description("A relay is upgraded once it has joined.")); } + if sent.replace_link_ca.is_some() { + return Err(SetError::invalid_properties() + .with_property(P::ReplaceLinkCa) + .with_description("The link CA is replaced on an existing relay.")); + } if sent .properties .get("smtpPrivateKey") @@ -564,6 +607,38 @@ pub async fn set( continue; } }; + // The link CA every relay shares (RL-34): asked of any one of them + if let Some(change) = sent.replace_link_ca { + let outcome = if sent.rejoin + || sent.upgrade.is_some() + || !sent.properties.is_empty() + || !sent.settings.is_empty() + { + Err(SetError::invalid_properties() + .with_property(P::ReplaceLinkCa) + .with_description("Send replaceLinkCa on its own.")) + } else if relay_id(id).is_none() + || relays::get(data, relay_id(id).unwrap_or_default()) + .await? + .is_none() + { + Err(SetError::not_found()) + } else if relays::change_link_ca(data, change, now()).await? { + Ok(()) + } else { + Err(SetError::invalid_properties() + .with_property(P::ReplaceLinkCa) + .with_description(match change { + relays::LinkCaChange::Start => "A replacement is already under way.", + _ => "No replacement is under way.", + })) + }; + match outcome { + Ok(()) => response.updated.append(id, None), + Err(error) => response.not_updated.append(id, error), + } + continue; + } if let Some(upgrade) = &sent.upgrade { let outcome = if sent.rejoin || !sent.properties.is_empty() || !sent.settings.is_empty() { diff --git a/crates/relay-proto/src/types.rs b/crates/relay-proto/src/types.rs index a44c8247..3884dd7c 100644 --- a/crates/relay-proto/src/types.rs +++ b/crates/relay-proto/src/types.rs @@ -70,6 +70,10 @@ pub struct RelaySettings { /// Addresses and CIDR ranges that may dial the link port; empty for /// anyone (RL-33). pub link_allow_from: Vec, + /// A second link CA to trust while the downstream replaces its own + /// (RL-34). Pushed as null by a link on that CA, it becomes the only one. + #[serde(with = "b64::option", skip_serializing_if = "Option::is_none")] + pub next_link_ca_pin: Option, /// The STARTTLS certificate for port 25. #[serde(skip_serializing_if = "Option::is_none")] pub smtp_certificate: Option, @@ -98,6 +102,7 @@ impl Default for RelaySettings { outbound_expiry: 3 * 86_400, outbound_tls: OutboundTls::default(), link_allow_from: Vec::new(), + next_link_ca_pin: None, smtp_certificate: None, sealing_key: None, recipient_key: None, @@ -239,6 +244,10 @@ pub struct RelayStatus { /// it: what `linkAllowFrom` has to let in (RL-33). #[serde(skip_serializing_if = "Option::is_none")] pub link_from: Option, + /// The link CAs it trusts, base64url pins: its own first, then the one + /// the downstream is moving to, if any (RL-34). + #[serde(default)] + pub link_ca_pins: Vec, } /// Live while a link client has push on for `Inbound` (spec "Live mode"). diff --git a/crates/relay/src/app.rs b/crates/relay/src/app.rs index 51c980b9..8fdfb59c 100644 --- a/crates/relay/src/app.rs +++ b/crates/relay/src/app.rs @@ -15,6 +15,8 @@ use crate::{ }; use inbuxa_relay_proto::{ allow::AllowList, + b64, + pin::Pin, time, types::{Mode, RelaySettings, RelayStatus, SpoolStatus, type_name}, }; @@ -30,6 +32,12 @@ use tokio::sync::{Notify, broadcast, watch}; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +fn trusted_cas(ca: Pin, next: Option) -> Vec { + std::iter::once(ca) + .chain(next.filter(|next| *next != ca)) + .collect() +} + /// A data type's new state, for push. #[derive(Debug, Clone)] pub struct Changed { @@ -69,6 +77,9 @@ pub struct App { /// `run --link-allow-any`: the `linkAllowFrom` to ignore, until the /// downstream sets another (RL-33). pub link_allow_any: Mutex>>, + /// The link CAs trusted: the relay's own, then the downstream's next + /// while it replaces it (RL-34). A link on a CA dropped from it ends. + pub link_ca: watch::Sender>, } impl App { @@ -85,6 +96,10 @@ impl App { } } let (push, _) = broadcast::channel(256); + let link_ca = trusted_cas( + identity.ca_pin, + settings.as_ref().and_then(|s| s.next_link_ca_pin), + ); Ok(App { paths, identity: Mutex::new(identity), @@ -108,6 +123,7 @@ impl App { upgrading: AtomicBool::new(false), restart: Notify::new(), link_allow_any: Mutex::new(None), + link_ca: watch::Sender::new(link_ca), }) } @@ -144,6 +160,17 @@ impl App { } } + /// Trusts `ca` and `next` for the link from now on, ending every link + /// on a CA no longer among them. + pub fn set_link_ca(&self, ca: Pin, next: Option) { + let trusted = trusted_cas(ca, next); + self.link_ca.send_if_modified(|current| { + let changed = *current != trusted; + *current = trusted; + changed + }); + } + pub fn conn_id(&self) -> u64 { self.next_conn.fetch_add(1, Ordering::Relaxed) } @@ -230,6 +257,12 @@ impl App { last_error: self.last_error.lock().expect("last_error").clone(), self_upgrade: self.self_upgrade, link_from: None, + link_ca_pins: self + .link_ca + .borrow() + .iter() + .map(|pin| b64::encode(pin)) + .collect(), } } diff --git a/crates/relay/src/link/jmap.rs b/crates/relay/src/link/jmap.rs index b698ea11..8ba987af 100644 --- a/crates/relay/src/link/jmap.rs +++ b/crates/relay/src/link/jmap.rs @@ -14,6 +14,7 @@ use crate::{app::App, link::tls::cert_pin, outbound::Destroyed, state::JoinStage use inbuxa_relay_proto::{ CAPABILITY, CORE_CAPABILITY, b64, jmap::{Invocation, MethodError, Request, Response, method}, + pin::pins_match, recipient::{RecipientDomain, RecipientHash}, time, token, types::{NewOutbound, RELAY_SINGLETON, RelaySettings, RelayStatus, Verdict, type_name}, @@ -42,6 +43,8 @@ pub struct RequestError { pub struct Caller { pub conn: u64, pub peer: std::net::IpAddr, + /// The link CA its certificate is from (RL-34). + pub ca: inbuxa_relay_proto::pin::Pin, } type Failure = (MethodError, Option); @@ -106,7 +109,7 @@ fn call_one(app: &App, caller: &Caller, call: &Invocation) -> MethodResult { match call.name() { method::RELAY_JOIN => relay_join(app, args), method::RELAY_GET => relay_get(app, caller, args), - method::RELAY_SET => relay_set(app, args), + method::RELAY_SET => relay_set(app, caller, args), method::RELAY_UPGRADE => relay_upgrade(app, args), method::RECIPIENT_DOMAIN_GET => recipient_domain_get(app), method::RECIPIENT_DOMAIN_SET => recipient_domain_set(app, args), @@ -266,7 +269,7 @@ fn relay_get(app: &App, caller: &Caller, args: &Map) -> MethodRes /// Read-only on `Relay`: what only the relay sets. const RELAY_READ_ONLY: &[&str] = &["id", "status", "relayVersion"]; -fn relay_set(app: &App, args: &Map) -> MethodResult { +fn relay_set(app: &App, caller: &Caller, args: &Map) -> MethodResult { let old_state = app.relay_state(); if let Some(if_in) = args.get("ifInState").and_then(Value::as_str) && if_in != old_state @@ -292,7 +295,7 @@ fn relay_set(app: &App, args: &Map) -> MethodResult { ); continue; } - match apply_settings(app, patch) { + match apply_settings(app, caller, patch) { Ok(()) => { updated.insert(id.clone(), Value::Null); } @@ -386,7 +389,7 @@ fn relay_upgrade(app: &App, args: &Map) -> MethodResult { })) } -fn apply_settings(app: &App, patch: &Value) -> Result<(), Value> { +fn apply_settings(app: &App, caller: &Caller, patch: &Value) -> Result<(), Value> { let Value::Object(patch) = patch else { return Err(set_error("invalidPatch", "a patch is an object")); }; @@ -435,10 +438,52 @@ fn apply_settings(app: &App, patch: &Value) -> Result<(), Value> { format!("smtpCertificate: {e}"), )); } + // The link CA (RL-34). A link on the downstream's next CA that pushes + // it as null makes it the relay's only one; nothing else may take it + // from under that link. + let ca = app.identity.lock().expect("identity").ca_pin; + if settings + .next_link_ca_pin + .is_some_and(|next| pins_match(&next, &ca)) + { + return Err(set_error( + "invalidProperties", + "nextLinkCaPin: the relay trusts that CA already", + )); + } + let on_next = !pins_match(&caller.ca, &ca) + && current + .next_link_ca_pin + .is_some_and(|next| pins_match(&next, &caller.ca)); + let promote = match settings.next_link_ca_pin { + None => on_next, + Some(next) if on_next && !pins_match(&next, &caller.ca) => { + return Err(set_error( + "invalidProperties", + "nextLinkCaPin: a link on the next CA can only make it the only one", + )); + } + Some(_) => false, + }; + if promote { + // Saved first: if the relay stops before the settings are, it + // trusts the new CA twice, not the old one alone + let mut identity = app.identity.lock().expect("identity"); + identity.ca_pin = caller.ca; + identity + .save(&app.paths) + .map_err(|e| set_error("serverFail", e.to_string()))?; + } crate::state::save_settings(&app.paths, &settings) .map_err(|e| set_error("serverFail", e.to_string()))?; app.link_allow_from_changed(&settings.link_allow_from); + let next = settings.next_link_ca_pin; app.settings.send_replace(Some(Arc::new(settings))); + // Links on a CA dropped here end now + app.set_link_ca(if promote { caller.ca } else { ca }, next); + if promote { + app.record("relay.link-ca-replaced", Map::new()); + } // The first Relay/set after the proof completes the join. let mut identity = app.identity.lock().expect("identity"); diff --git a/crates/relay/src/link/mod.rs b/crates/relay/src/link/mod.rs index a0a11d43..58241fc1 100644 --- a/crates/relay/src/link/mod.rs +++ b/crates/relay/src/link/mod.rs @@ -29,6 +29,7 @@ use inbuxa_relay_proto::{ self as proto, Account, ClientFrame, ServerFrame, Session, WEBSOCKET_CAPABILITY, websocket_capability, }, + pin::{Pin, pins_match}, types::{Capability, type_name}, }; use std::{ @@ -111,6 +112,24 @@ impl Handshakes { } } +/// Resolves once `ca` is no longer trusted for the link (RL-34): a link on +/// it then ends. +async fn untrusted(app: &App, ca: Pin) { + let mut trusted = app.link_ca.subscribe(); + loop { + if !trusted + .borrow_and_update() + .iter() + .any(|pin| pins_match(pin, &ca)) + { + return; + } + if trusted.changed().await.is_err() { + return std::future::pending().await; + } + } +} + /// Runs the link listener until the process ends. pub async fn serve(app: Arc, listener: TcpListener) -> std::io::Result<()> { let config = { @@ -118,7 +137,7 @@ pub async fn serve(app: Arc, listener: TcpListener) -> std::io::Result<()> tls::server_config( &identity.link_key_pem, &identity.link_cert_pem, - identity.ca_pin, + app.link_ca.subscribe(), ) .map_err(std::io::Error::other)? }; @@ -154,10 +173,18 @@ pub async fn serve(app: Arc, listener: TcpListener) -> std::io::Result<()> // peer gets a TLS alert and nothing more. _ => return, }; + // Which of the trusted CAs it came in on (RL-34) + let Some(ca) = tls.get_ref().1.peer_certificates().and_then(|chain| { + tls::client_ca(chain.get(1..).unwrap_or_default(), &app.link_ca.borrow()) + .map(|(_, pin)| pin) + }) else { + return; + }; let conn = app.conn_id(); let caller = jmap::Caller { conn, peer: peer.ip().to_canonical(), + ca, }; let service = { let app = app.clone(); @@ -166,12 +193,15 @@ pub async fn serve(app: Arc, listener: TcpListener) -> std::io::Result<()> async move { Ok::<_, std::convert::Infallible>(route(app, caller, req).await) } }) }; - let _ = hyper::server::conn::http1::Builder::new() + let connection = hyper::server::conn::http1::Builder::new() .timer(hyper_util::rt::TokioTimer::new()) .header_read_timeout(Duration::from_secs(30)) .serve_connection(TokioIo::new(tls), service) - .with_upgrades() - .await; + .with_upgrades(); + tokio::select! { + _ = connection => {} + _ = untrusted(&app, ca) => {} + } let released = app.spool.lock().expect("spool").release_conn(conn); if released { app.inbound_changed(); @@ -438,8 +468,11 @@ where live: false, }; let idle = Duration::from_secs(120); + let cut = untrusted(&app, caller.ca); + tokio::pin!(cut); loop { tokio::select! { + _ = &mut cut => break, frame = tokio::time::timeout(idle, stream.next()) => { let message = match frame { Ok(Some(Ok(message))) => message, diff --git a/crates/relay/src/link/tls.rs b/crates/relay/src/link/tls.rs index 2ab22518..afed6bde 100644 --- a/crates/relay/src/link/tls.rs +++ b/crates/relay/src/link/tls.rs @@ -17,6 +17,7 @@ use rustls::{ }; use rustls_pki_types::{CertificateDer, PrivateKeyDer, UnixTime, pem::PemObject}; use std::sync::Arc; +use tokio::sync::watch; pub fn provider() -> Arc { Arc::new(rustls::crypto::aws_lc_rs::default_provider()) @@ -47,7 +48,7 @@ pub fn new_link_identity(relay_id: &str) -> Result<(String, String), rcgen::Erro pub fn server_config( key_pem: &str, cert_pem: &str, - ca_pin: Pin, + ca_pins: watch::Receiver>, ) -> Result { let provider = provider(); let cert = CertificateDer::from_pem_slice(cert_pem.as_bytes()) @@ -55,7 +56,7 @@ pub fn server_config( let key = PrivateKeyDer::from_pem_slice(key_pem.as_bytes()).map_err(|e| format!("link key: {e}"))?; let verifier = Arc::new(PinnedCaVerifier { - ca_pin, + ca_pins, algorithms: provider.signature_verification_algorithms, }); let mut config = rustls::ServerConfig::builder_with_provider(provider) @@ -68,12 +69,27 @@ pub fn server_config( Ok(config) } +/// The pinned CA among the certificates a client sent with its own, if one +/// is there. +pub fn client_ca<'a>( + intermediates: &'a [CertificateDer<'a>], + pins: &[Pin], +) -> Option<(&'a CertificateDer<'a>, Pin)> { + intermediates.iter().find_map(|c| { + let pin = cert_pin(c)?; + pins.iter() + .any(|trusted| pins_match(&pin, trusted)) + .then_some((c, pin)) + }) +} + /// Accepts a client certificate only if one of the certificates the client -/// sent has the pinned CA key and signed the client's own certificate, -/// which must be valid now and for client authentication. +/// sent has a pinned CA key and signed the client's own certificate, +/// which must be valid now and for client authentication. The pins are the +/// relay's CA and, while the downstream replaces it, the next one (RL-34). #[derive(Debug)] pub struct PinnedCaVerifier { - ca_pin: Pin, + ca_pins: watch::Receiver>, algorithms: WebPkiSupportedAlgorithms, } @@ -96,12 +112,10 @@ impl ClientCertVerifier for PinnedCaVerifier { intermediates: &[CertificateDer<'_>], now: UnixTime, ) -> Result { - let ca = intermediates - .iter() - .find(|c| cert_pin(c).is_some_and(|pin| pins_match(&pin, &self.ca_pin))) - .ok_or(Error::InvalidCertificate( - rustls::CertificateError::UnknownIssuer, - ))?; + let pins = self.ca_pins.borrow().clone(); + let (ca, _) = client_ca(intermediates, &pins).ok_or(Error::InvalidCertificate( + rustls::CertificateError::UnknownIssuer, + ))?; let anchor = webpki::anchor_from_trusted_cert(ca) .map_err(|_| Error::InvalidCertificate(rustls::CertificateError::BadEncoding))?; let ee = webpki::EndEntityCert::try_from(end_entity) @@ -187,7 +201,7 @@ pub mod tests { fn verifier(pin: Pin) -> PinnedCaVerifier { PinnedCaVerifier { - ca_pin: pin, + ca_pins: watch::channel(vec![pin]).1, algorithms: provider().signature_verification_algorithms, } } @@ -222,6 +236,6 @@ pub mod tests { let (key, cert) = new_link_identity("r1").unwrap(); let der = CertificateDer::from_pem_slice(cert.as_bytes()).unwrap(); assert!(cert_pin(&der).is_some()); - assert!(server_config(&key, &cert, [1; 32]).is_ok()); + assert!(server_config(&key, &cert, watch::channel(vec![[1; 32]]).1).is_ok()); } } diff --git a/crates/relay/src/tests_e2e.rs b/crates/relay/src/tests_e2e.rs index 8d8db001..a729b4ce 100644 --- a/crates/relay/src/tests_e2e.rs +++ b/crates/relay/src/tests_e2e.rs @@ -797,6 +797,69 @@ async fn relay_end_to_end() { assert!(kinds.contains(&kind), "{kind} in {kinds:?}"); } + // RL-T26: a new link CA without a new join. The downstream pushes the + // next CA's pin, and the relay trusts both... + let next_ca = test_ca(true); + let next_pin = cert_pin(&next_ca.ca_der).unwrap(); + let pins = |relay: &Value| relay["list"][0]["status"]["linkCaPins"].clone(); + let set_next = async |ws: &mut Ws, pin: Value| { + let set = ws + .ok( + "Relay/set", + json!({ "update": { "singleton": { "nextLinkCaPin": pin }}}), + ) + .await; + set["updated"] == json!({ "singleton": null }) + }; + assert!( + !set_next(&mut ws, json!(b64::encode(&ca_pin))).await, + "its own CA" + ); + assert!(set_next(&mut ws, json!(b64::encode(&next_pin))).await); + let relay = ws.ok("Relay/get", json!({})).await; + assert_eq!( + pins(&relay), + json!([b64::encode(&ca_pin), b64::encode(&next_pin)]) + ); + let next_down = Downstream::new(&next_ca, link_addr); + let mut next_ws = next_down.ws().await.unwrap(); + // ...a link on the next CA can't point it elsewhere... + assert!(!set_next(&mut next_ws, json!(b64::encode(&[5u8; 32]))).await); + // ...and pushing it as null from there makes it the only one + assert!(set_next(&mut next_ws, Value::Null).await); + let relay = next_ws.ok("Relay/get", json!({})).await; + assert_eq!(pins(&relay), json!([b64::encode(&next_pin)])); + assert_eq!(Identity::load(&paths).unwrap().unwrap().ca_pin, next_pin); + // A link on the old CA ends at once, and can't come back + let ended = tokio::time::timeout(Duration::from_secs(2), async { + loop { + match ws.ws.next().await { + None | Some(Err(_)) | Some(Ok(Message::Close(_))) => return, + Some(Ok(_)) => {} + } + } + }) + .await; + assert!(ended.is_ok(), "the old CA's link was cut"); + // TLS 1.3 tells the client of a refused certificate on its first read + assert!(down.ws().await.is_err()); + assert!(next_down.ws().await.is_ok()); + let events = next_ws.ok("RelayEvent/get", json!({})).await; + assert!( + events["list"] + .as_array() + .unwrap() + .iter() + .any(|e| e["type"] == "relay.link-ca-replaced"), + "{events}" + ); + // A link on the current CA can start and stop a replacement + assert!(set_next(&mut next_ws, json!(b64::encode(&[6u8; 32]))).await); + assert!(set_next(&mut next_ws, Value::Null).await); + let relay = next_ws.ok("Relay/get", json!({})).await; + assert_eq!(pins(&relay), json!([b64::encode(&next_pin)])); + drop(ws); + drop(next_ws); std::fs::remove_dir_all(dir).unwrap(); } diff --git a/crates/services/src/inbuxa_relay_link.rs b/crates/services/src/inbuxa_relay_link.rs index 0f9ee951..c26203c3 100644 --- a/crates/services/src/inbuxa_relay_link.rs +++ b/crates/services/src/inbuxa_relay_link.rs @@ -184,10 +184,20 @@ async fn reconcile( links: &mut HashMap, ) -> trc::Result<()> { let data = server.store(); - let all = match relays::keys(data).await? { - Some(_) => relays::all(data).await?, - None => Vec::new(), + let (keys, all) = match relays::keys(data).await? { + Some(keys) => (Some(keys), relays::all(data).await?), + None => (None, Vec::new()), }; + // A link CA replacement every joined relay is ready for (RL-34) + if let Some(keys) = &keys + && relays::link::link_ca_ready(data, keys, &all).await? + && relays::change_link_ca(data, relays::LinkCaChange::Finish, now()).await? + { + trc::event!( + Relay(trc::RelayEvent::LinkCaReplaced), + Details = "Every relay trusts the new link CA: it is the only one now." + ); + } // Which relays outbound mail goes through (RL-20, RL-24) server.inner.data.relay_senders.set_relays(&all); // The domains' MX and SPF records, when the relays in them changed (RL-25) @@ -259,6 +269,7 @@ impl NodeCertificates { let mut current = self.0.lock().unwrap_or_else(|e| e.into_inner()); if let Some(cert) = current.as_ref() && cert.issued_at + NODE_CERT_RENEW_AFTER > now + && cert.ca_pin == keys.link_ca_pin { return Ok(cert.clone()); } @@ -987,7 +998,8 @@ async fn session( .await?; let mut sent = Sent::default(); let mut relay = relay.clone(); - sync(server, &relay, keys, &dialer, &mut ws, &mut sent).await?; + let mut keys = keys.clone(); + sync(server, &relay, &keys, &dialer, &mut ws, &mut sent).await?; status.set(data, relay.id, LinkState::Linked, None).await; refresh_status(&mut ws, data, &relay, &sent, status).await?; @@ -1065,7 +1077,13 @@ async fn session( } _ => return Ok(()), } - sync(server, &relay, keys, &dialer, &mut ws, &mut sent).await?; + // A new link CA: a link on a certificate from the old one starts + // again with one from the new (RL-34). A next CA, or none, is pushed. + match relays::keys(data).await? { + Some(now_keys) if now_keys.link_ca_pin == cert.ca_pin => keys = now_keys, + _ => return Ok(()), + } + sync(server, &relay, &keys, &dialer, &mut ws, &mut sent).await?; refresh_status(&mut ws, data, &relay, &sent, status).await?; taker.relay_name = relay.name.clone(); taker.take(&mut ws).await?; @@ -1791,7 +1809,14 @@ async fn sync( // Settings, whole, when they changed let settings = pushed_settings(relay, keys, &plan); if sent.settings.as_ref() != Some(&settings) { - let patch = serde_json::to_value(&settings).unwrap_or_default(); + let mut patch = serde_json::to_value(&settings).unwrap_or_default(); + // The relay merges what it is sent: a setting left out stays as it + // was, so one this server has cleared goes as null + if let Some(patch) = patch.as_object_mut() { + for cleared in ["smtpCertificate", "nextLinkCaPin"] { + patch.entry(cleared).or_insert(Value::Null); + } + } let answer = ws .ok( method::RELAY_SET, diff --git a/crates/trc/src/event/enums.rs b/crates/trc/src/event/enums.rs index 3c48b0b5..ae188bf4 100644 --- a/crates/trc/src/event/enums.rs +++ b/crates/trc/src/event/enums.rs @@ -13,10 +13,10 @@ // security.legacy-protocols-changed (LP-8); 644 to 646 are the cluster // coordinator's connection events; 647 and 648 are the audit log's // (audit-hold-lock spec, AU-3, AU-8); 649 is cluster.node-id-reassigned; -// 650 to 658 are inbuxa Relay's (relay spec, RL-23, RL-T23). +// 650 to 659 are inbuxa Relay's (relay spec, RL-23, RL-T23). // TOTAL_EVENT_COUNT sizes the collector's per-event tables, so it is one // more than the highest id; the test below holds it to the variant list -pub const TOTAL_EVENT_COUNT: usize = 659; +pub const TOTAL_EVENT_COUNT: usize = 660; pub const TOTAL_METRIC_COUNT: usize = 369; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -659,6 +659,8 @@ pub enum RelayEvent { Event = 656, LinkDown = 657, LinkUp = 658, + /// Every joined relay trusts the new link CA: it is the only one now (RL-34). + LinkCaReplaced = 659, } // inbuxa: SCIM-54: every write an identity provider makes diff --git a/crates/trc/src/event/enums_impl.rs b/crates/trc/src/event/enums_impl.rs index 18cfa2e1..8c3c6e65 100644 --- a/crates/trc/src/event/enums_impl.rs +++ b/crates/trc/src/event/enums_impl.rs @@ -48,6 +48,7 @@ impl EventType { b"relay.event" => EventType::Relay(RelayEvent::Event), b"relay.link-down" => EventType::Relay(RelayEvent::LinkDown), b"relay.link-up" => EventType::Relay(RelayEvent::LinkUp), + b"relay.link-ca-replaced" => EventType::Relay(RelayEvent::LinkCaReplaced), // inbuxa: SCIM-54 b"scim.resource-created" => EventType::Scim(ScimEvent::ResourceCreated), b"scim.resource-updated" => EventType::Scim(ScimEvent::ResourceUpdated), @@ -719,6 +720,7 @@ impl EventType { EventType::Relay(RelayEvent::Event) => "relay.event", EventType::Relay(RelayEvent::LinkDown) => "relay.link-down", EventType::Relay(RelayEvent::LinkUp) => "relay.link-up", + EventType::Relay(RelayEvent::LinkCaReplaced) => "relay.link-ca-replaced", // inbuxa: SCIM-54 EventType::Scim(ScimEvent::ResourceCreated) => "scim.resource-created", EventType::Scim(ScimEvent::ResourceUpdated) => "scim.resource-updated", @@ -1531,6 +1533,7 @@ impl EventType { EventType::Relay(RelayEvent::Event) => 656, EventType::Relay(RelayEvent::LinkDown) => 657, EventType::Relay(RelayEvent::LinkUp) => 658, + EventType::Relay(RelayEvent::LinkCaReplaced) => 659, // inbuxa: SCIM-54 EventType::Scim(ScimEvent::ResourceCreated) => 637, EventType::Scim(ScimEvent::ResourceUpdated) => 638, @@ -2201,6 +2204,7 @@ impl EventType { 656 => Some(EventType::Relay(RelayEvent::Event)), 657 => Some(EventType::Relay(RelayEvent::LinkDown)), 658 => Some(EventType::Relay(RelayEvent::LinkUp)), + 659 => Some(EventType::Relay(RelayEvent::LinkCaReplaced)), // inbuxa: SCIM-54 637 => Some(EventType::Scim(ScimEvent::ResourceCreated)), 638 => Some(EventType::Scim(ScimEvent::ResourceUpdated)), @@ -3193,6 +3197,7 @@ impl EventType { EventType::Relay(RelayEvent::Event) => Level::Info, EventType::Relay(RelayEvent::LinkDown) => Level::Warn, EventType::Relay(RelayEvent::LinkUp) => Level::Info, + EventType::Relay(RelayEvent::LinkCaReplaced) => Level::Info, // inbuxa: SCIM-54 EventType::Scim(ScimEvent::ResourceCreated) => Level::Info, EventType::Scim(ScimEvent::ResourceUpdated) => Level::Info, @@ -3294,6 +3299,7 @@ impl EventType { EventType::Relay(RelayEvent::Event) => "Relay event", EventType::Relay(RelayEvent::LinkDown) => "Relay link down for an hour", EventType::Relay(RelayEvent::LinkUp) => "Relay link back", + EventType::Relay(RelayEvent::LinkCaReplaced) => "Relay link CA replaced", // inbuxa: SCIM-54 EventType::Scim(ScimEvent::ResourceCreated) => "SCIM resource created", EventType::Scim(ScimEvent::ResourceUpdated) => "SCIM resource updated", @@ -4409,6 +4415,7 @@ impl EventType { EventType::Relay(RelayEvent::Event), EventType::Relay(RelayEvent::LinkDown), EventType::Relay(RelayEvent::LinkUp), + EventType::Relay(RelayEvent::LinkCaReplaced), // inbuxa: SCIM-54 EventType::Scim(ScimEvent::ResourceCreated), EventType::Scim(ScimEvent::ResourceUpdated), diff --git a/docs/spec/features/relay.md b/docs/spec/features/relay.md index a6b6133e..f6468c31 100644 --- a/docs/spec/features/relay.md +++ b/docs/spec/features/relay.md @@ -170,6 +170,30 @@ the protocol the rest of the suite already speaks. CA. From then on a certificate from the old one fails the handshake. A relay that missed the change, because it was down meanwhile, still has the old pin and needs a new join; inbuxa-admin lists it. + + How it goes: + - An update of any `inbuxa:Relay` with `replaceLinkCa`, sent on its + own, acts on the CA they all share: `start` makes the next CA, + `finish` makes it the link CA without waiting, and `cancel` drops it. + One that changes nothing (a second `start`, a `finish` with none + under way) is refused. Every relay shows `linkCaReplacing`, when the + replacement began, and its own `linkCa`: `current`, `both`, or `old` + (only a CA this server replaced, from what its links last said, or a + join whose token named one: it needs a new join), null before it has + said. + - The link service finishes it by itself once every enabled relay that + has joined says `both`, and records `relay.link-ca-replaced`. A + relay that never does holds it until an administrator finishes it. + - Each node then takes a certificate from the new CA and links again + with it. Its settings push carries `nextLinkCaPin: null`; from a link + on the next CA, that makes the next CA the relay's only one: the + relay saves it, ends every link on the old CA, and records + `relay.link-ca-replaced` too. From a link on the current CA, null + just drops the next one (a cancel). A link on the next CA can't name + another. Settings a server has cleared go to the relay as null, as a + setting left out of a push stays as it was. + - A join started before the replacement finished names the old CA in + its token and can't complete: start it again. - **RL-35. The downstream's relay keys, wrapped.** The link CA key, the sealing key and each relay's recipient key are stored encrypted under a wrapping key the store doesn't hold: a file every node reads, named by @@ -261,7 +285,7 @@ with the server's own `inbuxa:` objects. In particular, the relay's | `sealingKey` | downstream | The downstream's public sealing key | | `linkAllowFrom` | downstream | Addresses and CIDR ranges that may dial the link port. Default empty: anyone (RL-33) | | `nextLinkCaPin` | downstream | A second link CA to trust during a replacement (RL-34). Default null | -| `status` | relay | `{startedAt, linkClients, mode, spool: {inbound, outbound, bytes}, recipientState, certificateNotAfter, linkCaPins, lastError, selfUpgrade, linkFrom}`; `linkCaPins` lists the CAs it trusts (RL-34); `selfUpgrade` is false in a container image (RL-29); `linkFrom` is the address the asking link connection comes from (RL-33) | +| `status` | relay | `{startedAt, linkClients, mode, spool: {inbound, outbound, bytes}, recipientState, certificateNotAfter, linkCaPins, lastError, selfUpgrade, linkFrom}`; `linkCaPins` lists the CAs it trusts, its own first (RL-34); `selfUpgrade` is false in a container image (RL-29); `linkFrom` is the address the asking link connection comes from (RL-33) | Settings are cached in the state directory as the downstream last set them, so a reboot without the link comes back with the same settings (RL-22). @@ -513,7 +537,7 @@ When no link is up, or after RL-13: | Object | What it holds | |---|---| -| `inbuxa:Relay` | One per relay: `name`, `address`, `linkPort`, `enabled`, the settings pushed to the relay, `sendThrough` (route outbound mail through relays), `inboundOnly` (takes mail in but sends none, e.g. while its IP isn't warmed up or isn't in SPF yet), `mxPreference` (its MX preference, 10 by default, RL-25), `certificateNotAfter` and `certificateError` (when its certificate runs out, and why the last order failed, RL-27), `olderThanServer` (a link last heard an older release than this server's, RL-28), and read-only status mirrored from the relay: link state per node, version, queue counts, `lastSeen`. Create, or an update with `rejoin: true`, returns `joinToken` once. An update with `upgrade: {blobId, signature}`, sent on its own, hands the relay a release over the node's link (RL-29) | +| `inbuxa:Relay` | One per relay: `name`, `address`, `linkPort`, `enabled`, the settings pushed to the relay, `sendThrough` (route outbound mail through relays), `inboundOnly` (takes mail in but sends none, e.g. while its IP isn't warmed up or isn't in SPF yet), `mxPreference` (its MX preference, 10 by default, RL-25), `certificateNotAfter` and `certificateError` (when its certificate runs out, and why the last order failed, RL-27), `olderThanServer` (a link last heard an older release than this server's, RL-28), `linkCa` and `linkCaReplacing` (RL-34), and read-only status mirrored from the relay: link state per node, version, queue counts, `lastSeen`. Create, or an update with `rejoin: true`, returns `joinToken` once. An update with `upgrade: {blobId, signature}`, sent on its own, hands the relay a release over the node's link (RL-29). An update with `replaceLinkCa`, sent on its own, starts, finishes or cancels a replacement of the shared link CA (RL-34) | | `inbuxa:RelayMessage` | Read-only view of a relay's `Inbound` and `Outbound` queues, fetched over the link when asked. Never message content. `/get`, `/query` (filter `relayId`, `direction`; newest first) and `/set` destroy only: discards held mail, or cancels an outbound message, whose recipients fail here with no DSN as a delivery cancelled in the queue does. Each id is the relay's id and a hash of the relay's own message id, so nothing is stored | Permissions: `sysRelayGet`, `sysRelayUpdate`, `sysRelayQueueGet`, @@ -829,7 +853,8 @@ In progress. Done so far: `relay.event` for any other type), then acknowledges it. A node with no link to a joined relay for an hour raises `relay.link-down` (a warning), once, and `relay.link-up` when it is back (RL-T23). Both reach the log, - webhooks and OpenTelemetry. + webhooks and OpenTelemetry. `relay.link-ca-replaced` (659) records a + finished link CA replacement (RL-34). - `Outbound` on the relay: `Outbound/set` create and destroy, `/get` and `/changes`, with push, in `spool/out/` (RL-19, RL-T10). - Outbound delivery on the relay: MX, DANE, MTA-STS and `outboundTls`, per diff --git a/resources/schema/schema.json.gz b/resources/schema/schema.json.gz index 82cf0cc132546d1503d62b351c5910bafd5abcc1..a70d5df11503b0a11581f8e8675d3b9fd6f68b07 100644 GIT binary patch delta 59767 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 delta 59740 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 RG<9jq{U3GKDG}lj4*(VJPgwu} diff --git a/resources/schema/schema.json.sha256 b/resources/schema/schema.json.sha256 index d476303e..664edecc 100644 --- a/resources/schema/schema.json.sha256 +++ b/resources/schema/schema.json.sha256 @@ -1 +1 @@ -au254ZOQm1gGMzM7Qp3nUep_vmcV_jOrKB7fraBxdNc \ No newline at end of file +bxhi7hGulmWKjZiUh2eaxDH78ZzRFaijGqkp16KrLXc diff --git a/tests/src/system/relay.rs b/tests/src/system/relay.rs index 2b59d554..9331a352 100644 --- a/tests/src/system/relay.rs +++ b/tests/src/system/relay.rs @@ -266,6 +266,19 @@ pub async fn test(test: &mut TestServer) { .is_some_and(|d| d.contains("relay.example is not an address or CIDR range")), "{response}" ); + // The next link CA is the server's to push (RL-34) + let (_, response) = call( + admin, + "inbuxa:Relay/set", + json!({"update": {&id: {"settings/nextLinkCaPin": "K28RckuyO0ozszKIofsQT6Ka3nR2k76IuCMLtio1hEU"}}}), + ) + .await; + assert_eq!( + response["notUpdated"][&id]["properties"][0], "settings", + "{response}" + ); + // A relay still joining has said nothing of its CA + assert_eq!(get(admin, &id).await["linkCa"], Value::Null); let (_, response) = call( admin, "inbuxa:Relay/set", @@ -1396,6 +1409,50 @@ pub async fn link(test: &mut TestServer) { "one DSN, for the refusal" ); + // RL-T26: a new link CA, without a new join. Sent alone, and only + // when it changes something. + let replace = async |change: Value, extra: Value| { + let mut update = json!({"replaceLinkCa": change}); + if let Value::Object(extra) = extra { + update.as_object_mut().unwrap().extend(extra); + } + let (_, response) = call(admin, "inbuxa:Relay/set", json!({"update": {&id: update}})).await; + response["updated"].get(&id).is_some() + }; + assert!(!replace(json!("start"), json!({"description": "and this"})).await); + assert!(!replace(json!("now"), Value::Null).await); + assert!( + !replace(json!("cancel"), Value::Null).await, + "none to cancel" + ); + let relay = get(admin, &id).await; + assert_eq!(relay["linkCa"], "current", "{relay}"); + assert_eq!(relay["linkCaReplacing"], Value::Null, "{relay}"); + let old_pin = relay["links"][0]["relay"]["linkCaPins"][0].clone(); + assert!(old_pin.is_string(), "{relay}"); + assert!(replace(json!("start"), Value::Null).await); + assert!(!replace(json!("start"), Value::Null).await, "one at a time"); + assert!(get(admin, &id).await["linkCaReplacing"].is_string()); + // The relay trusts both, so the server finishes it: every node takes a + // certificate from the new CA, and the relay makes it its only one + let relay = wait_for(admin, &id, "the relay took the new link CA", 90, |relay| { + relay["linkCaReplacing"].is_null() + && relay["linkCa"] == "current" + && relay["links"].as_array().into_iter().flatten().any(|link| { + link["state"] == "linked" + && link["relay"]["linkCaPins"] + .as_array() + .is_some_and(|pins| pins.len() == 1 && pins[0] != old_pin) + }) + }) + .await; + let new_pin = relay["links"][0]["relay"]["linkCaPins"][0].clone(); + let identity: Value = + serde_json::from_str(&std::fs::read_to_string(state.join("identity.json")).unwrap()) + .unwrap(); + assert_eq!(identity["caPin"], new_pin, "the relay keeps the new CA"); + assert_eq!(relay["joinState"], "joined", "without a new join"); + // The relay's events were taken as this server's relay.* trace events, // and acknowledged, so the relay forgot them let events = state.join("events.jsonl"); -- 2.54.0