126 lines
4.1 KiB
Rust
126 lines
4.1 KiB
Rust
//! Real components can accept TCP connections. Host port registration and host-only config deny access.
|
|
mod hook_guest;
|
|
use sausage::{
|
|
exec::{Command, Exec, Options},
|
|
process::{Runtime, net::NetPolicy},
|
|
world::World,
|
|
};
|
|
use std::{sync::Arc, time::Duration};
|
|
#[test]
|
|
fn host_policy() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
std::fs::write(
|
|
temp.path().join("network.toml"),
|
|
"deny = ['127.0.0.1:9001', '[::1]:9002']",
|
|
)
|
|
.unwrap();
|
|
let policy = NetPolicy::open(temp.path()).unwrap();
|
|
policy.deny_host_port(9000);
|
|
for address in [
|
|
"127.0.0.1:9000",
|
|
"0.0.0.0:9000",
|
|
"[::1]:9000",
|
|
"[::ffff:127.0.0.1]:9000",
|
|
"127.0.0.1:9001",
|
|
"[::1]:9002",
|
|
] {
|
|
assert!(!policy.allows(address.parse().unwrap()));
|
|
}
|
|
assert!(policy.allows("0.0.0.0:8080".parse().unwrap()));
|
|
for interface in if_addrs::get_if_addrs().unwrap() {
|
|
assert!(!policy.allows(std::net::SocketAddr::new(interface.ip(), 9000)));
|
|
}
|
|
assert!(!policy.allows("[::ffff:127.0.0.1]:9001".parse().unwrap()));
|
|
}
|
|
async fn run(runtime: &Arc<Runtime>, mode: &str, address: String) -> String {
|
|
let mut output = Vec::new();
|
|
let code = runtime
|
|
.exec(
|
|
Command::Argv(vec!["/bin/guest.wasm".into(), mode.into(), address]),
|
|
Options {
|
|
cwd: "/".into(),
|
|
timeout: Some(Duration::from_secs(30)),
|
|
..Default::default()
|
|
},
|
|
&mut |_, bytes| output.extend_from_slice(bytes),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(code, 0);
|
|
String::from_utf8(output).unwrap()
|
|
}
|
|
#[tokio::test]
|
|
async fn guest_tcp_and_host_denial() {
|
|
let guest = hook_guest::component();
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let world = World::open(temp.path()).unwrap();
|
|
std::fs::create_dir_all(temp.path().join("world/bin")).unwrap();
|
|
std::fs::copy(guest, temp.path().join("world/bin/guest.wasm")).unwrap();
|
|
let configured = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
std::fs::write(
|
|
world.host_dir.join("network.toml"),
|
|
format!("deny = ['{}']", configured.local_addr().unwrap()),
|
|
)
|
|
.unwrap();
|
|
let runtime = Runtime::new(world).unwrap();
|
|
assert!(
|
|
run(
|
|
&runtime,
|
|
"connect",
|
|
configured.local_addr().unwrap().to_string()
|
|
)
|
|
.await
|
|
.contains("denied")
|
|
);
|
|
let native = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let denied = native.local_addr().unwrap();
|
|
runtime.network.deny_host_port(denied.port());
|
|
assert!(
|
|
run(&runtime, "connect", denied.to_string())
|
|
.await
|
|
.contains("denied")
|
|
);
|
|
let ephemeral = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
|
let address = ephemeral.local_addr().unwrap();
|
|
drop(ephemeral);
|
|
let server_runtime = runtime.clone();
|
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
|
let server = tokio::spawn(async move {
|
|
let mut tx = Some(tx);
|
|
let mut bytes = Vec::new();
|
|
server_runtime
|
|
.exec(
|
|
Command::Argv(vec![
|
|
"/bin/guest.wasm".into(),
|
|
"listen".into(),
|
|
format!("0.0.0.0:{}", address.port()),
|
|
]),
|
|
Options {
|
|
cwd: "/".into(),
|
|
timeout: Some(Duration::from_secs(30)),
|
|
..Default::default()
|
|
},
|
|
&mut |_, chunk| {
|
|
bytes.extend_from_slice(chunk);
|
|
if bytes.windows(5).any(|w| w == b"ready")
|
|
&& let Some(tx) = tx.take()
|
|
{
|
|
let _ = tx.send(());
|
|
}
|
|
},
|
|
)
|
|
.await
|
|
.unwrap()
|
|
});
|
|
tokio::time::timeout(Duration::from_secs(30), rx)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert!(
|
|
run(&runtime, "connect", format!("localhost:{}", address.port()))
|
|
.await
|
|
.contains("ping")
|
|
);
|
|
assert_eq!(server.await.unwrap(), 0);
|
|
}
|