holger-estate-front/examples/downstream_lab.rs
//! **The front's downstream half, alone, for the lab proof** (lane HOL-WGDS).
//!
//! The real front program (PID 1 of its medium) needs the installer's seed and the ONE box;
//! this example runs exactly the front's production pieces of the downstream feature on a
//! plain machine (a VM or a container with `CAP_NET_ADMIN` and `/dev/net/tun`), so the lab
//! can put real downstream Holgers in front of it:
//!
//! * `holger-ds` (`10.46.0.1/24`, udp/51830): [`DownstreamHub`] on a real TUN, isolated,
//! behind the filtered port;
//! * the sync door on `10.46.0.1:50070` ([`door`]), carrying to main's doors;
//! * the look at main's list (`GET /-/sync/downstreams/wg`, the bearer of `--bearer-file`);
//! * the `via: Front` road: a plain TCP forward of `--front-bind` to main's gRPC door (edda
//! `estate-front::forward`), standing in for the public route on :443.
//!
//! ```text
//! downstream_lab --root DIR --main-http IP:PORT --main-grpc IP:PORT --bearer-file F --front-bind 0.0.0.0:8443
//! ```
//!
//! It writes `<root>/hub.pub` (the interface's WireGuard key, for the invites) and says the
//! relay's counters every 10 s.
use std::net::SocketAddrV4;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use holger_downstream_rules::via;
use holger_estate_front::downstreams::{self, DownstreamHub, MainNow, Targets};
fn arg(args: &[String], name: &str) -> Option<String> {
args.iter()
.position(|a| a == name)
.and_then(|i| args.get(i + 1).cloned())
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
)
.init();
let args: Vec<String> = std::env::args().collect();
let need = |n: &str| arg(&args, n).unwrap_or_else(|| panic!("{n} is required"));
let root = PathBuf::from(need("--root"));
let http: SocketAddrV4 = need("--main-http").parse().expect("--main-http ip:port");
let grpc: SocketAddrV4 = need("--main-grpc").parse().expect("--main-grpc ip:port");
let bearer = arg(&args, "--bearer-file").map(|f| {
std::fs::read_to_string(f)
.expect("the bearer file")
.trim()
.to_string()
});
let front_bind = arg(&args, "--front-bind").unwrap_or_else(|| "0.0.0.0:8443".into());
let id = downstreams::identity(&root).expect("the downstreams' key");
let tun = downstreams::open_tun().expect("holger-ds (CAP_NET_ADMIN, /dev/net/tun)");
let hub =
Arc::new(DownstreamHub::start(via::HUB_UDP_PORT, id.private, tun).expect("udp/51830"));
std::fs::write(root.join("hub.pub"), &hub.relay.public_b64).expect("hub.pub");
println!(
"LAB holger-ds up: {} udp/{} key {}",
via::HUB_ADDR,
via::HUB_UDP_PORT,
hub.relay.public_b64
);
let targets: Targets = Arc::new(move || Some((http, grpc)));
let names = Arc::clone(&hub);
let at = downstreams::serve_door(
(via::HUB_ADDR, via::DOOR_PORT).into(),
downstreams::door(targets, Arc::new(move |ip| names.name_at(ip))),
)
.await
.expect("the sync door");
println!("LAB sync door on {at}");
let main: MainNow = Arc::new(move || Some((http, bearer.clone())));
downstreams::follow(Arc::clone(&hub), main);
let fwd = estate_front::forward::Forward::bind(
front_bind.parse().expect("--front-bind"),
"via-front",
)
.await
.expect("the front's public road");
fwd.retarget(Some(grpc));
println!("LAB the front's public road {} -> main {grpc}", fwd.local);
loop {
tokio::time::sleep(Duration::from_secs(10)).await;
let v = hub.relay.verdict();
println!(
"LAB relay: {} | door-port drops in {} out {}",
wg_appliance_core::hubrelay::describe(&v),
hub.port
.dropped_in
.load(std::sync::atomic::Ordering::Relaxed),
hub.port
.dropped_out
.load(std::sync::atomic::Ordering::Relaxed)
);
}
}