crates/gunnar-ssh/tests/tunnel_address_verb.rs
//! **`gunnar-tunnel-address`: the sealed box's WireGuard tunnel address, over
//! the SSH door it already has** (2026-09-21, T17).
//!
//! The sealed installer (`serve --no-grpc --admin-key @/data/admin.pub`) runs
//! no control plane: its SSH listener is the only door. The tunnel address is
//! per box and is not on the medium, so it has to arrive through that door —
//! as ONE exec verb, held to server-wide Admin, answered by the same function
//! the gRPC doors call (`gunnar-server`'s `appliance::tunnel_address_answer`,
//! injected here as a closure because this crate may not link the server).
//!
//! Both directions, every time: an admin gets the door's answer; a principal
//! with only a repository grant never reaches the door (and an anonymous
//! session is refused the channel: `tests/anonymous.rs`) (the recorder proves the door was not
//! called, not merely that the answer was an error).
mod common;
use std::sync::{Arc, Mutex};
use common::*;
use gunnar_core::{Grant, Level};
use gunnar_ssh::seam::BannerTransport;
use gunnar_ssh::{Access, GunnarSsh, SshConfig, TUNNEL_ADDRESS_VERB};
/// A door that records what it was asked and answers like the real one.
fn recording_door() -> (gunnar_ssh::TunnelAddressDoor, Arc<Mutex<Vec<String>>>) {
let calls = Arc::new(Mutex::new(Vec::new()));
let seen = Arc::clone(&calls);
let door: gunnar_ssh::TunnelAddressDoor = Arc::new(move |address: &str| {
seen.lock().unwrap().push(address.to_string());
match address {
"" | "10.77.0.2/16" => {
Ok("wg_public_key K\naddress 10.77.0.2/16\nchanged true\n".to_string())
}
other => Err(format!("{other} refused: never re-pointed")),
}
});
(door, calls)
}
async fn serve(
plane: Plane,
door: Option<gunnar_ssh::TunnelAddressDoor>,
) -> gunnar_ssh::BoundServer {
let transport = Arc::new(BannerTransport::new());
let mut server = GunnarSsh::new(SshConfig::ephemeral_loopback(), plane.control, transport);
if let Some(door) = door {
server = server.with_tunnel_address_door(door);
}
server.bind(mint_key()).await.expect("bind")
}
#[tokio::test]
async fn only_a_server_admin_reaches_the_tunnel_address_door() {
let admin = mint_key();
let reader = mint_key();
let plane = Plane::empty()
.with_the_repo()
.with_principal("admin")
.with_key("admin", &admin)
.with_principal("reader")
.with_key("reader", &reader)
.with_grant("reader", Access::Admin);
plane
.auth()
.grant(Grant::server("admin", Level::Admin))
.expect("server admin");
let (door, calls) = recording_door();
let server = serve(plane, Some(door)).await;
let addr = server.local_addr();
// The admin: the door's answer on stdout, exit 0.
let s = authenticate(addr, &admin, "git")
.await
.unwrap()
.expect("admin key accepted");
match exec(&s, &format!("{TUNNEL_ADDRESS_VERB} 10.77.0.2/16"))
.await
.unwrap()
{
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(exit_status, Some(0), "{stdout}");
assert!(stdout.contains("address 10.77.0.2/16"), "{stdout}");
}
other => panic!("a server admin must reach the door: {other:?}"),
}
// A read (no argument) is the same door with an empty address.
match exec(&s, TUNNEL_ADDRESS_VERB).await.unwrap() {
ExecOutcome::Accepted { exit_status, .. } => assert_eq!(exit_status, Some(0)),
other => panic!("{other:?}"),
}
// The door's refusal is the admin's answer, non-zero, in words.
match exec(&s, &format!("{TUNNEL_ADDRESS_VERB} 10.77.0.3/16"))
.await
.unwrap()
{
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(exit_status, Some(1), "{stdout}");
assert!(
stdout.starts_with("error: ") && stdout.contains("never re-pointed"),
"{stdout}"
);
}
other => panic!("{other:?}"),
}
// Two arguments is not the verb.
match exec(&s, &format!("{TUNNEL_ADDRESS_VERB} 10.77.0.2/16 extra"))
.await
.unwrap()
{
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(exit_status, Some(1), "{stdout}");
}
other => panic!("{other:?}"),
}
assert_eq!(
*calls.lock().unwrap(),
vec!["10.77.0.2/16", "", "10.77.0.3/16"],
"the malformed line never reached the door"
);
// A principal with ADMIN on a repository — not on the server — is refused
// before the door.
let r = authenticate(addr, &reader, "git")
.await
.unwrap()
.expect("reader key accepted");
match exec(&r, &format!("{TUNNEL_ADDRESS_VERB} 10.77.0.2/16"))
.await
.unwrap()
{
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(
exit_status,
Some(gunnar_ssh::server::EXIT_DENIED),
"{stdout}"
);
assert!(stdout.contains("server-wide Admin"), "{stdout}");
}
ExecOutcome::Refused => {}
}
assert_eq!(
calls.lock().unwrap().len(),
3,
"a repository admin never reaches the door"
);
}
#[tokio::test]
async fn a_box_with_no_tunnel_says_so_by_name() {
let admin = mint_key();
let plane = Plane::empty()
.with_principal("admin")
.with_key("admin", &admin);
plane
.auth()
.grant(Grant::server("admin", Level::Admin))
.expect("server admin");
// No door: a build without the tunnel says so, by name.
let server = serve(plane, None).await;
let s = authenticate(server.local_addr(), &admin, "git")
.await
.unwrap()
.expect("admin");
match exec(&s, &format!("{TUNNEL_ADDRESS_VERB} 10.77.0.2/16"))
.await
.unwrap()
{
ExecOutcome::Accepted {
stdout,
exit_status,
} => {
assert_eq!(exit_status, Some(1), "{stdout}");
assert!(stdout.contains("no WireGuard tunnel"), "{stdout}");
}
other => panic!("{other:?}"),
}
}