Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

holger-estate-front/src/wg_only.rs

//! **The front reaches its boxes over the tunnel only** (lane E1, 2026-10-19;
//! Gunnar's WireGuard-only PATTERN, Holger's own code).
//!
//! An estate front (its seed names a WireGuard tunnel) opens its own overlay
//! interface on the estate's relay ([`open_hub`], the overlay's `.1`) and dials
//! main and the twin at their OVERLAY addresses: the console, the repository
//! route, the twin's certification (CA material), the sealed slot (the installer
//! material), the recharge. A dial to any other address is refused by name
//! ([`estate_link`]) before a byte moves. The utility network carries only the
//! installer's link and the tunnel itself; the ONE box's door (`shop_door`) is the
//! one link that leaves the tunnel, and it is not a box address.
//!
//! A front with another tunnel kind (a local rig, `tunnel = none`) dials the
//! utility addresses as before.

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};

use nordisk_pairing::join::JoinReply;
use nordisk_pairing::Role;

/// The refusal's code, the boxes' own (holger errcode `TWIN_ESTATE_NOT_WG`).
pub const NOT_WG: &str = "holger-twin-17";

/// The front's own overlay interface.
pub const HUB_IFACE: &str = "holger-hub";

static WG_ONLY: AtomicBool = AtomicBool::new(false);

/// Set the posture (at start, from the seed's tunnel kind).
pub fn set_wg_only(on: bool) {
    WG_ONLY.store(on, Ordering::SeqCst);
}

/// Is this front WireGuard-only?
pub fn wg_only() -> bool {
    WG_ONLY.load(Ordering::SeqCst)
}

/// Is `ip` on the estate's overlay?
pub fn in_tunnel(ip: IpAddr) -> bool {
    let IpAddr::V4(ip) = ip else { return false };
    let (net, bits) = nordisk_pairing::OVERLAY_NET;
    let mask = if bits == 0 {
        0
    } else {
        u32::MAX << (32 - u32::from(bits.min(32)))
    };
    u32::from(ip) & mask == u32::from(net) & mask
}

/// Pure: **the address the front dials a box of `role` at** — its overlay
/// address under `wg_only`, else its utility address. `None` until fed.
pub fn box_addr(reply: &JoinReply, role: Role, wg_only: bool) -> Option<Ipv4Addr> {
    if wg_only {
        if role == Role::Primary {
            if let Some(o) = reply.primary_overlay {
                return Some(o);
            }
        }
        return reply
            .peers
            .iter()
            .find(|p| p.role == role)
            .map(|p| p.overlay);
    }
    match role {
        Role::Primary => nordisk_pairing::frontside::primary_utility(reply),
        _ => reply
            .peers
            .iter()
            .find(|p| p.role == role)
            .and_then(|p| p.utility),
    }
}

/// Pure: **may the front dial `at` as an estate link?** Under `wg_only`, an
/// overlay host only (`http://h:p`, `h:p`); otherwise anything.
pub fn estate_link(what: &str, at: &str, wg_only: bool) -> Result<(), String> {
    if !wg_only {
        return Ok(());
    }
    let rest = at
        .trim()
        .split_once("://")
        .map(|(_, r)| r)
        .unwrap_or(at.trim());
    let hostport = rest.split('/').next().unwrap_or(rest);
    let ip = hostport
        .parse::<SocketAddr>()
        .map(|s| s.ip())
        .ok()
        .or_else(|| {
            hostport
                .rsplit_once(':')
                .map_or(hostport, |(h, _)| h)
                .trim_matches(['[', ']'])
                .parse()
                .ok()
        });
    match ip {
        Some(ip) if in_tunnel(ip) => Ok(()),
        _ => Err(format!(
            "REFUSED [{NOT_WG}] {what} `{at}` is not on the estate's tunnel ({}/{}): the front reaches its boxes over wg0 only, and never sends a bearer, CA or installer material anywhere else",
            nordisk_pairing::OVERLAY_NET.0,
            nordisk_pairing::OVERLAY_NET.1
        )),
    }
}

/// [`estate_link`] under the process posture.
pub fn check(what: &str, at: &str) -> Result<(), String> {
    estate_link(what, at, wg_only())
}

static HUB: OnceLock<Arc<dyn wg_appliance_core::hubrelay::HubPort>> = OnceLock::new();

/// **Open the front's own overlay interface** ([`HUB_IFACE`], the overlay's `.1`),
/// once — the relay carries it to every listed box. Needs `CAP_NET_ADMIN` (the
/// front is PID 1 of its medium).
pub fn open_hub() -> Result<Ipv4Addr, String> {
    if HUB.get().is_some() {
        return Ok(nordisk_pairing::OVERLAY_FRONT);
    }
    let (_, prefix) = nordisk_pairing::OVERLAY_NET;
    let tun = wg_appliance_core::tun::TunDevice::create(HUB_IFACE, 1420)
        .map_err(|e| format!("{HUB_IFACE}: {e} (CAP_NET_ADMIN?)"))?;
    wg_appliance_core::addr::assign_ipv4(HUB_IFACE, nordisk_pairing::OVERLAY_FRONT, prefix)
        .map_err(|e| {
            format!(
                "{HUB_IFACE} {}/{prefix}: {e}",
                nordisk_pairing::OVERLAY_FRONT
            )
        })?;
    wg_appliance_core::addr::link_up(HUB_IFACE).map_err(|e| format!("{HUB_IFACE} up: {e}"))?;
    let _ = HUB.set(Arc::new(wg_appliance_core::hubrelay::TunPort(tun)));
    Ok(nordisk_pairing::OVERLAY_FRONT)
}

/// The front's overlay interface, when [`open_hub`] opened it.
pub fn hub() -> Option<Arc<dyn wg_appliance_core::hubrelay::HubPort>> {
    HUB.get().cloned()
}

/// **The real-TUN test's switch** (lane HOLGER-WG, A18 §4 #6): set to `1`, the
/// `open_hub` test REFUSES to skip — a runner that cannot open `/dev/net/tun` is
/// then a red test, not a quiet pass. Unset, a runner without the node or without
/// `CAP_NET_ADMIN` skips the test by name (CI's plain job container).
pub const TUN_REQUIRED_ENV: &str = "HOLGER_WG_TUN_REQUIRED";

/// Pure: **is this `open_hub` error the runner's, not the code's?** A missing
/// `/dev/net/tun` (no `CONFIG_TUN`, a container without the node) or a refused
/// ioctl (no `CAP_NET_ADMIN`) says nothing about the hub; anything else does.
pub fn is_runner_limit(err: &str) -> bool {
    [
        "No such file or directory",
        "Operation not permitted",
        "Permission denied",
        "No such device",
    ]
    .iter()
    .any(|w| err.contains(w))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::fed;
    use nordisk_pairing::join::Peer;

    fn estate() -> Vec<Peer> {
        let p = |name: &str, role, last: u8, utility: Option<[u8; 4]>| Peer {
            name: name.into(),
            role,
            tunnel_pubkey: String::new(),
            overlay: Ipv4Addr::new(10, 44, 0, last),
            endpoint: None,
            utility: utility.map(Ipv4Addr::from),
        };
        vec![
            p("vm1", Role::Front, 1, None),
            p("vm2", Role::Primary, 2, Some([10, 13, 8, 2])),
            p("vm3", Role::Twin, 3, Some([10, 13, 8, 3])),
        ]
    }

    /// ★ **An estate front dials its boxes at their overlay addresses**, and the
    /// utility ones only off an estate.
    #[test]
    fn an_estate_front_dials_its_boxes_over_the_tunnel() {
        let r = fed(&[], estate());
        assert_eq!(
            box_addr(&r, Role::Primary, true),
            Some(Ipv4Addr::new(10, 44, 0, 2))
        );
        assert_eq!(
            box_addr(&r, Role::Twin, true),
            Some(Ipv4Addr::new(10, 44, 0, 3))
        );
        assert_eq!(
            box_addr(&r, Role::Primary, false),
            Some(Ipv4Addr::new(10, 13, 8, 2))
        );
        assert_eq!(
            box_addr(&r, Role::Twin, false),
            Some(Ipv4Addr::new(10, 13, 8, 3))
        );
    }

    /// ★ **An estate link to a non-wg address is refused**: under WireGuard-only
    /// the front dials overlay hosts only, whatever carries the bytes.
    #[test]
    fn an_estate_link_to_a_non_wg_address_is_refused() {
        assert!(estate_link("the primary", "10.44.0.2:50051", true).is_ok());
        assert!(estate_link("the primary", "http://10.44.0.2:8443", true).is_ok());
        for bad in [
            "10.13.8.2:50051",
            "http://10.13.8.3:50051",
            "127.0.0.1:50051",
            "main.holger.test:50051",
            "",
        ] {
            let e = estate_link("the primary", bad, true).unwrap_err();
            assert!(e.contains(NOT_WG), "{bad}: {e}");
        }
        assert!(
            estate_link("the primary", "10.13.8.2:50051", false).is_ok(),
            "off an estate: as before"
        );
    }

    /// ★ **`open_hub` opens the front's overlay interface on a REAL TUN** (lane HOLGER-WG,
    /// A18 §4 #6): `holger-hub` exists with its MTU, is up, carries `10.44.0.1/24` (a
    /// socket binds the address), is opened once (a second call is the same hub), and the
    /// kernel ROUTES the overlay into it — a datagram to `10.44.0.2` comes off the hub
    /// port as an IPv4 packet, the one the relay would carry to the box. Needs
    /// `/dev/net/tun` and `CAP_NET_ADMIN` (a privileged container): without them it
    /// SKIPS by name, unless [`TUN_REQUIRED_ENV`] is `1` (`cargo xtask suite-check
    /// front:wg-hub` on a runner that can open the node), when it is red instead.
    #[test]
    fn open_hub_opens_the_overlay_interface_on_a_real_tun() {
        let required = std::env::var(TUN_REQUIRED_ENV).is_ok_and(|v| v.trim() == "1");
        let at = match open_hub() {
            Ok(at) => at,
            Err(e) if !required && is_runner_limit(&e) => {
                eprintln!("SKIPPED (set {TUN_REQUIRED_ENV}=1 to require the real TUN): {e}");
                return;
            }
            Err(e) => panic!("open_hub on this runner: {e}"),
        };
        assert_eq!(at, nordisk_pairing::OVERLAY_FRONT);
        let hub = hub().expect("the hub port is kept once opened");
        let sys = |f: &str| {
            std::fs::read_to_string(format!("/sys/class/net/{HUB_IFACE}/{f}"))
                .unwrap_or_else(|e| panic!("/sys/class/net/{HUB_IFACE}/{f}: {e}"))
                .trim()
                .to_string()
        };
        assert_eq!(sys("mtu"), "1420", "the hub's MTU");
        let flags =
            u32::from_str_radix(sys("flags").trim_start_matches("0x"), 16).expect("flags are hex");
        assert_eq!(flags & 1, 1, "IFF_UP: the hub is up (flags {flags:#x})");
        let sock = std::net::UdpSocket::bind((nordisk_pairing::OVERLAY_FRONT, 0))
            .expect("10.44.0.1 is this host's: the address is on the hub");
        assert_eq!(
            open_hub().unwrap(),
            at,
            "opened once: the second call is the same hub"
        );
        assert!(
            Arc::ptr_eq(&hub, &super::hub().unwrap()),
            "the same port, not a second device"
        );
        // The overlay is routed into the hub: a datagram to a box's address comes off the port.
        let to = std::net::SocketAddrV4::new(Ipv4Addr::new(10, 44, 0, 2), 50051);
        let payload = b"holger-hub probe";
        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
        let mut buf = [0u8; 2048];
        let mut seen = None;
        while std::time::Instant::now() < deadline && seen.is_none() {
            sock.send_to(payload, to)
                .expect("the kernel routes 10.44.0.0/24 via the hub");
            while let Ok(Some(n)) = hub.next(&mut buf, std::time::Duration::from_millis(300)) {
                let p = &buf[..n];
                // IPv4, UDP, to 10.44.0.2:50051, from 10.44.0.1, carrying the probe.
                if n >= 28
                    && p[0] >> 4 == 4
                    && p[9] == 17
                    && p[12..16] == [10, 44, 0, 1]
                    && p[16..20] == [10, 44, 0, 2]
                    && p.ends_with(payload)
                {
                    let ihl = usize::from(p[0] & 0xf) * 4;
                    assert_eq!(
                        u16::from_be_bytes([p[ihl + 2], p[ihl + 3]]),
                        50051,
                        "the box's control port"
                    );
                    seen = Some(n);
                    break;
                }
            }
        }
        assert!(seen.is_some(), "a datagram to 10.44.0.2 never came off {HUB_IFACE}: the overlay is not routed into the hub");
    }

    /// The runner's limits are told from the code's: a missing node or a refused ioctl
    /// skips, anything else is red.
    #[test]
    fn a_missing_tun_or_no_cap_net_admin_is_the_runners_limit_and_nothing_else_is() {
        assert!(is_runner_limit("holger-hub: open /dev/net/tun (…): No such file or directory (os error 2) (CAP_NET_ADMIN?)"));
        assert!(is_runner_limit("holger-hub: TUNSETIFF holger-hub: Operation not permitted (os error 1) (CAP_NET_ADMIN?)"));
        assert!(is_runner_limit(
            "holger-hub: open /dev/net/tun: Permission denied (os error 13) (CAP_NET_ADMIN?)"
        ));
        assert!(!is_runner_limit(
            "holger-hub 10.44.0.1/24: SIOCSIFADDR: Address already in use"
        ));
        assert!(!is_runner_limit(
            "holger-hub: \"\" is not a legal interface name (1..=15 bytes)"
        ));
    }
}