server/downstream/src/via.rs
//! **How each downstream reaches its cloud Holger** (lane HOL-WGDS, owner 2026-10-27).
//!
//! A cloud Holger is three machines: the front, main and the twin, in the estate's own
//! WireGuard. A downstream syncs THROUGH THE FRONT by default (`via: Front`: the front's
//! public door, the repository route to main). A customer who wants no public sync door
//! names the downstream `via: Wireguard(key: "<its public key>")`: the downstream then
//! reaches the front over a WireGuard interface of its own, and through it ONLY main's
//! sync door.
//!
//! ```ron
//! downstreams: [
//! (name: "lab-1", via: Front),
//! (name: "lab-2", via: Wireguard(key: "<the downstream's WireGuard public key, base64>")),
//! ],
//! ```
//!
//! | piece | what it is |
//! |---|---|
//! | [`DownstreamDecl`], [`Via`] | the RON above; an unknown word is a RON error that names it, a bad entry is refused by name ([`validate`], `holger-downstream-5`) |
//! | [`HUB_IFACE`], [`HUB_NET`], [`HUB_UDP_PORT`] | the front's SEPARATE interface for downstreams: its own subnet and UDP port, never the estate's inner tunnel |
//! | [`inbound`], [`outbound`] | the hub port's packet rule: a downstream peer reaches the front's sync door ([`DOOR_PORT`]) and nothing else on the front |
//! | [`door_allows`] | the sync door's call rule: main's sync and read calls only, never admin, the console, the twin or replication |
//!
//! Pure: no I/O, no network.
use std::net::Ipv4Addr;
use holger_errcode::DOWNSTREAM_VIA_REFUSED;
use serde::{Deserialize, Serialize};
/// The front's interface for WireGuard downstreams (never the estate's `holger-hub`).
pub const HUB_IFACE: &str = "holger-ds";
/// Its subnet: the downstreams' own, apart from the estate's `10.44.0.0/24` and the installer's link (`10.45.0.0/30`, nordisk-pairing `installer_link`).
pub const HUB_NET: (Ipv4Addr, u8) = (Ipv4Addr::new(10, 46, 0, 0), 24);
/// The front's address on it.
pub const HUB_ADDR: Ipv4Addr = Ipv4Addr::new(10, 46, 0, 1);
/// Its UDP port (the estate's inner tunnel has its own).
pub const HUB_UDP_PORT: u16 = 51830;
/// The sync door on [`HUB_ADDR`]: the one TCP port a downstream peer reaches.
pub const DOOR_PORT: u16 = 50070;
/// The header the front's sync door names the calling downstream in (the WireGuard key
/// it came in under). The door strips any the caller sent.
pub const DOWNSTREAM_HEADER: &str = "x-holger-downstream";
/// **How one downstream reaches this Holger.**
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Via {
/// Through the front's public door (the default).
#[default]
Front,
/// Over the front's downstream WireGuard interface; `key` is the downstream's
/// WireGuard public key (base64, 32 bytes).
Wireguard { key: String },
}
impl Via {
/// The word the board and the summary line say.
pub fn word(&self) -> &'static str {
match self {
Via::Front => "front",
Via::Wireguard { .. } => "wireguard",
}
}
}
/// **One `downstreams:` entry.**
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DownstreamDecl {
pub name: String,
#[serde(default)]
pub via: Via,
}
/// The `via` of downstream `name` under `decls`: its entry's, else [`Via::Front`].
pub fn via_of<'a>(decls: &'a [DownstreamDecl], name: &str) -> &'a Via {
static FRONT: Via = Via::Front;
decls
.iter()
.find(|d| d.name == name)
.map(|d| &d.via)
.unwrap_or(&FRONT)
}
/// The 32 bytes of a WireGuard public key in base64, or why not.
pub fn wg_key(b64: &str) -> Result<[u8; 32], String> {
use base64::Engine as _;
let raw = base64::engine::general_purpose::STANDARD
.decode(b64.trim())
.map_err(|e| format!("not base64 ({e})"))?;
<[u8; 32]>::try_from(raw.as_slice())
.map_err(|_| format!("{} bytes, a WireGuard key is 32", raw.len()))
}
fn name_ok(name: &str) -> bool {
!name.is_empty()
&& name.len() <= 64
&& !name.starts_with('.')
&& name
.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-'))
}
/// ★ **The `downstreams:` block, or its refusal** (`holger-downstream-5`), every bad entry
/// named: a name that is not one, a name twice, a WireGuard key that is not a key, one
/// key for two downstreams.
pub fn validate(decls: &[DownstreamDecl]) -> Result<(), String> {
let mut reasons = Vec::new();
for (i, d) in decls.iter().enumerate() {
if !name_ok(&d.name) {
reasons.push(format!("downstream {:?} is not a usable name (1-64 of [A-Za-z0-9._-], not starting with '.')", d.name));
}
if decls[..i].iter().any(|e| e.name == d.name) {
reasons.push(format!("downstream '{}' is declared twice", d.name));
}
if let Via::Wireguard { key } = &d.via {
match wg_key(key) {
Err(why) => reasons.push(format!("downstream '{}': `via: Wireguard(key: {key:?})` is not a WireGuard public key: {why}", d.name)),
Ok(k) => {
let twice = decls[..i].iter().find(|e| matches!(&e.via, Via::Wireguard { key: other } if wg_key(other).ok() == Some(k)));
if let Some(e) = twice {
reasons.push(format!("downstream '{}' has the WireGuard key of '{}': one key is one downstream", d.name, e.name));
}
}
}
}
}
if reasons.is_empty() {
Ok(())
} else {
Err(DOWNSTREAM_VIA_REFUSED.tag(format_args!(
"the `downstreams:` block is refused: {}",
reasons.join("; ")
)))
}
}
/// Pure: **is `ip` on the downstreams' subnet?**
pub fn on_hub_net(ip: Ipv4Addr) -> bool {
let (net, bits) = HUB_NET;
let mask = u32::MAX << (32 - u32::from(bits));
u32::from(ip) & mask == u32::from(net) & mask
}
/// IPv4 + TCP facts of a packet: `(src, dst, src_port, dst_port)`, or why it is not one
/// whole TCP segment.
fn tcp_of(p: &[u8]) -> Result<(Ipv4Addr, Ipv4Addr, u16, u16), &'static str> {
if p.len() < 20 || p[0] >> 4 != 4 {
return Err("not IPv4");
}
let ihl = usize::from(p[0] & 0x0f) * 4;
if ihl < 20 || p.len() < ihl + 4 {
return Err("a truncated IPv4 header");
}
if p[9] != 6 {
return Err("not TCP");
}
// A fragment carries no TCP ports to judge (or the wrong ones): never admitted.
let frag = u16::from_be_bytes([p[6], p[7]]);
if frag & 0x3fff != 0 {
return Err("a fragment");
}
let src = Ipv4Addr::new(p[12], p[13], p[14], p[15]);
let dst = Ipv4Addr::new(p[16], p[17], p[18], p[19]);
Ok((
src,
dst,
u16::from_be_bytes([p[ihl], p[ihl + 1]]),
u16::from_be_bytes([p[ihl + 2], p[ihl + 3]]),
))
}
/// ★ **A packet a downstream peer sends to the front**: admitted only when it is TCP to
/// the sync door ([`HUB_ADDR`]:[`DOOR_PORT`]) from the downstreams' subnet. Everything
/// else (the console's 443, the clock, the egress door, the installer's listener, ICMP,
/// UDP, a fragment) is dropped before the front's kernel sees it.
pub fn inbound(p: &[u8]) -> Result<(), &'static str> {
let (src, dst, _, dport) = tcp_of(p)?;
if !on_hub_net(src) {
return Err("a source off the downstreams' subnet");
}
if dst != HUB_ADDR {
return Err("not to the front's downstream address");
}
if dport != DOOR_PORT {
return Err("not the sync door's port");
}
Ok(())
}
/// ★ **A packet the front sends to a downstream peer**: only the sync door's own answers
/// (TCP from [`HUB_ADDR`]:[`DOOR_PORT`]); the front opens nothing toward a downstream.
pub fn outbound(p: &[u8]) -> Result<(), &'static str> {
let (src, dst, sport, _) = tcp_of(p)?;
if src != HUB_ADDR || sport != DOOR_PORT {
return Err("not the sync door's answer");
}
if !on_hub_net(dst) {
return Err("not to the downstreams' subnet");
}
Ok(())
}
/// The gRPC calls the sync door carries to main: the mirror's reads and its sync report.
pub const DOOR_GRPC: [&str; 6] = [
"/holger.v1.RepositoryService/ListArtifacts",
"/holger.v1.RepositoryService/FetchArtifact",
"/holger.v1.RepositoryService/StreamArtifact",
"/holger.v1.ArchiveService/ListArchiveFiles",
"/holger.v1.ArchiveService/ArchiveInfo",
"/holger.grow.v1.GrowService/DownstreamReport",
];
/// ★ **May the sync door carry this call to main?** gRPC: [`DOOR_GRPC`] only (never an
/// `AdminService`, `PromotionService`, the twin's or replication's call). HTTP: a read
/// (`GET`/`HEAD`) of a repository path, the sync manifest and key, and the signed ack
/// (`POST /-/sync/ack`); every other `/-/` door (the admin board, the console's, sign-in,
/// intake, invalidate, apply, the downstreams' own) and every write is refused.
pub fn door_allows(method: &str, path: &str, grpc: bool) -> Result<(), String> {
let path = path.split('?').next().unwrap_or(path);
if grpc {
return if method == "POST" && DOOR_GRPC.contains(&path) {
Ok(())
} else {
Err(format!("the gRPC call {path} is not a sync call"))
};
}
if path.contains("/../") || path.ends_with("/..") {
return Err(format!("{path} is not a plain path"));
}
match (method, path) {
("GET" | "HEAD", "/-/sync/manifest" | "/-/sync/key" | "/healthz") => Ok(()),
("POST", "/-/sync/ack") => Ok(()),
(_, p) if p.starts_with("/-/") || p == "/-" => {
Err(format!("{method} {p} is not a sync door"))
}
("GET" | "HEAD", p) if p.starts_with('/') && p.len() > 1 => Ok(()),
(m, p) => Err(format!("{m} {p}: the sync door only reads")),
}
}
#[cfg(test)]
mod tests {
use super::*;
const KEY_A: &str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=";
const KEY_B: &str = "HyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4=";
fn wg(name: &str, key: &str) -> DownstreamDecl {
DownstreamDecl {
name: name.into(),
via: Via::Wireguard { key: key.into() },
}
}
/// ★ The owner's RON reads as written, `via` defaults to `Front`, an unknown word is
/// refused naming it.
#[test]
fn the_downstreams_ron_reads_and_defaults_to_front() {
let ron = format!(
r#"[ (name: "lab-1", via: Front), (name: "lab-2", via: Wireguard(key: "{KEY_A}")), (name: "lab-3") ]"#
);
let d: Vec<DownstreamDecl> = ron::from_str(&ron).unwrap();
assert_eq!(d[0].via, Via::Front);
assert_eq!(d[1].via, Via::Wireguard { key: KEY_A.into() });
assert_eq!(d[2].via, Via::Front, "the default");
assert!(validate(&d).is_ok());
assert_eq!(
(via_of(&d, "lab-2").word(), via_of(&d, "nobody").word()),
("wireguard", "front")
);
let e = ron::from_str::<Vec<DownstreamDecl>>(r#"[ (name: "x", via: Tailscale) ]"#)
.unwrap_err()
.to_string();
assert!(e.contains("Tailscale"), "{e}");
let e = ron::from_str::<Vec<DownstreamDecl>>(r#"[ (name: "x", via: Front, port: 1) ]"#)
.unwrap_err()
.to_string();
assert!(e.contains("port"), "{e}");
}
/// ★ A bad entry is refused by name: a bad key, a short key, a name twice, a key twice.
#[test]
fn a_bad_downstream_entry_is_refused_by_name() {
let e = validate(&[wg("lab-2", "not-a-key")]).unwrap_err();
assert!(
e.contains("[holger-downstream-5]") && e.contains("lab-2"),
"{e}"
);
let e = validate(&[wg("lab-2", "AAEC")]).unwrap_err();
assert!(e.contains("32"), "{e}");
let e = validate(&[
wg("a", KEY_A),
DownstreamDecl {
name: "a".into(),
via: Via::Front,
},
])
.unwrap_err();
assert!(e.contains("'a' is declared twice"), "{e}");
let e = validate(&[wg("a", KEY_A), wg("b", KEY_A)]).unwrap_err();
assert!(e.contains("'b' has the WireGuard key of 'a'"), "{e}");
assert!(validate(&[wg("a", KEY_A), wg("b", KEY_B)]).is_ok());
assert!(validate(&[DownstreamDecl {
name: "../x".into(),
via: Via::Front
}])
.unwrap_err()
.contains("not a usable name"));
}
fn tcp(src: [u8; 4], dst: [u8; 4], sport: u16, dport: u16) -> Vec<u8> {
let mut p = vec![0u8; 40];
p[0] = 0x45;
p[9] = 6;
p[12..16].copy_from_slice(&src);
p[16..20].copy_from_slice(&dst);
p[20..22].copy_from_slice(&sport.to_be_bytes());
p[22..24].copy_from_slice(&dport.to_be_bytes());
p
}
/// ★ A downstream peer reaches the sync door and nothing else on the front.
#[test]
fn a_downstream_peer_reaches_only_the_sync_door() {
assert!(inbound(&tcp([10, 46, 0, 2], [10, 46, 0, 1], 40000, DOOR_PORT)).is_ok());
for (dst, port, why) in [
([10, 46, 0, 1], 443, "the console"),
([10, 46, 0, 1], 80, "the web face"),
([10, 46, 0, 1], 50123, "the clock"),
([10, 46, 0, 1], 50051, "a gRPC door"),
([10, 44, 0, 1], DOOR_PORT, "the estate's front"),
([10, 44, 0, 3], 50051, "the twin"),
([10, 44, 0, 2], 50051, "main, directly"),
([10, 46, 0, 3], DOOR_PORT, "another downstream"),
] {
assert!(
inbound(&tcp([10, 46, 0, 2], dst, 40000, port)).is_err(),
"{why} must be unreachable"
);
}
assert!(
inbound(&tcp([10, 44, 0, 2], [10, 46, 0, 1], 1, DOOR_PORT)).is_err(),
"a source off the subnet"
);
let mut udp = tcp([10, 46, 0, 2], [10, 46, 0, 1], 1, DOOR_PORT);
udp[9] = 17;
assert_eq!(inbound(&udp), Err("not TCP"));
let mut frag = tcp([10, 46, 0, 2], [10, 46, 0, 1], 1, DOOR_PORT);
frag[6] = 0x20;
assert_eq!(inbound(&frag), Err("a fragment"));
assert!(inbound(&[0x60; 40]).is_err(), "IPv6");
assert!(outbound(&tcp([10, 46, 0, 1], [10, 46, 0, 2], DOOR_PORT, 40000)).is_ok());
assert!(
outbound(&tcp([10, 46, 0, 1], [10, 46, 0, 2], 443, 40000)).is_err(),
"the front opens nothing else toward a downstream"
);
assert!(outbound(&tcp([10, 44, 0, 1], [10, 46, 0, 2], DOOR_PORT, 1)).is_err());
}
/// ★ The sync door carries the sync and nothing of admin, console, twin or replication.
#[test]
fn the_sync_door_carries_sync_calls_only() {
for ok in DOOR_GRPC {
assert!(door_allows("POST", ok, true).is_ok(), "{ok}");
}
for no in [
"/holger.v1.AdminService/Health",
"/holger.v1.AdminService/AdminSnapshot",
"/holger.v1.AdminService/IssueTwinCertificate",
"/holger.v1.RepositoryService/PutArtifact",
"/holger.v1.PromotionService/AdvanceArtifact",
"/holger.v1.ReplicationService/Replicate",
"/holger.grow.v1.GrowService/TwinReport",
"/holger.grow.v1.GrowService/Downstreams",
"/holger.estate.v1.EstateService/Follow",
] {
assert!(door_allows("POST", no, true).is_err(), "{no}");
}
assert!(door_allows("GET", "/-/sync/manifest?downstream=lab-2&since=0", false).is_ok());
assert!(door_allows("POST", "/-/sync/ack", false).is_ok());
assert!(door_allows("GET", "/bundles/a.znippy", false).is_ok());
assert!(door_allows("HEAD", "/crates/index/config.json", false).is_ok());
for (m, p) in [
("GET", "/-/estate.json"),
("GET", "/-/sync/board"),
("POST", "/-/sync/invalidate"),
("POST", "/-/sync/apply"),
("POST", "/-/auth/passkey/invites"),
("GET", "/-/intake/state"),
("POST", "/-/downstreams/revoke"),
("PUT", "/bundles/a.znippy"),
("POST", "/npm/x"),
("GET", "/"),
("GET", "/bundles/../-/estate.json"),
] {
assert!(door_allows(m, p, false).is_err(), "{m} {p}");
}
}
}