Skip to content

Commit e032eca

Browse files
committed
host: add --net and --net-allow CLI flags to hyperlight-unikraft and pyhl
Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent dfa9fb8 commit e032eca

3 files changed

Lines changed: 70 additions & 11 deletions

File tree

examples/networking-py/Justfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ image := "networking-py-hyperlight"
1616

1717
# Run HTTP GET test (raw sockets)
1818
run-get:
19-
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} -- /http_get.py
19+
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} --net -- /http_get.py
2020

2121
# Run HTTP GET test (urllib — high-level stdlib)
2222
run-urllib:
23-
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} -- /urllib_get.py
23+
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} --net -- /urllib_get.py
2424

2525
# Run HTTPS (TLS) test
2626
run-https:
27-
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} -- /https_test.py
27+
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} --net -- /https_test.py
2828

2929
# Run echo server
3030
run-echo:
31-
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} -- /echo_server.py
31+
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} --net -- /echo_server.py
3232

3333
# Build rootfs via Docker (cross-platform)
3434
rootfs:

host/src/bin/pyhl.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use hyperlight_unikraft::pyhl::{
2626
copy_replace, discover_source_artifacts, extract_from_ghcr, GHCR_INITRD_IMAGE,
2727
GHCR_KERNEL_IMAGE,
2828
};
29-
use hyperlight_unikraft::{Preopen, Sandbox};
29+
use hyperlight_unikraft::{AllowList, NetworkPolicy, Preopen, Sandbox};
3030
use std::fs;
3131
use std::path::{Path, PathBuf};
3232
use std::time::Instant;
@@ -37,6 +37,16 @@ fn parse_mount(spec: &str) -> Result<Preopen> {
3737
Preopen::parse_cli(spec).map_err(|e| anyhow!("invalid --mount {:?}: {}", spec, e))
3838
}
3939

40+
fn build_network_policy(net: bool, net_allow: &[String]) -> Result<Option<NetworkPolicy>> {
41+
if !net_allow.is_empty() {
42+
Ok(Some(NetworkPolicy::AllowList(AllowList::from_hosts(net_allow)?)))
43+
} else if net {
44+
Ok(Some(NetworkPolicy::AllowAll))
45+
} else {
46+
Ok(None)
47+
}
48+
}
49+
4050
/// Keep in sync with `py_initialize_once` in examples/python-agent-driver/
4151
/// hl_pydriver.c. These modules are imported during `pyhl setup`'s warmup
4252
/// so they're already in `sys.modules` in every `pyhl run` invocation —
@@ -145,6 +155,15 @@ struct SetupArgs {
145155
/// given to `setup`.
146156
#[arg(long = "mount", value_name = "HOST[:GUEST]")]
147157
mounts: Vec<String>,
158+
159+
/// Enable guest networking.
160+
#[arg(long)]
161+
net: bool,
162+
163+
/// Restrict guest networking to the listed hosts/IPs.
164+
/// Implies --net. Repeatable.
165+
#[arg(long = "net-allow", value_name = "HOST_OR_IP")]
166+
net_allow: Vec<String>,
148167
}
149168

150169
#[derive(Args)]
@@ -172,6 +191,15 @@ struct RunArgs {
172191
#[arg(long = "mount", value_name = "HOST[:GUEST]")]
173192
mounts: Vec<String>,
174193

194+
/// Enable guest networking.
195+
#[arg(long)]
196+
net: bool,
197+
198+
/// Restrict guest networking to the listed hosts/IPs.
199+
/// Implies --net. Repeatable.
200+
#[arg(long = "net-allow", value_name = "HOST_OR_IP")]
201+
net_allow: Vec<String>,
202+
175203
/// Print evolve/warmup/per-run timing to stderr. Off by default so the
176204
/// user's script output is clean.
177205
#[arg(short = 'v', long = "verbose")]
@@ -329,6 +357,7 @@ fn cmd_setup(args: SetupArgs) -> Result<()> {
329357
.iter()
330358
.map(|m| parse_mount(m))
331359
.collect::<Result<_>>()?;
360+
let network = build_network_policy(args.net, &args.net_allow)?;
332361

333362
eprintln!("pyhl: warming up Python and persisting snapshot…");
334363
let t_warm = Instant::now();
@@ -339,6 +368,9 @@ fn cmd_setup(args: SetupArgs) -> Result<()> {
339368
for p in &setup_preopens {
340369
builder = builder.preopen(p.clone());
341370
}
371+
if let Some(ref policy) = network {
372+
builder = builder.network(policy.clone());
373+
}
342374
let mut sbox = builder.build()?;
343375
sbox.restore()?;
344376
let _: () = sbox.call_named("run", "pass".to_string())?;
@@ -463,17 +495,22 @@ fn cmd_run(args: RunArgs) -> Result<()> {
463495
.iter()
464496
.map(|m| parse_mount(m))
465497
.collect::<Result<_>>()?;
498+
let network = build_network_policy(args.net, &args.net_allow)?;
466499

467500
let initrd = home.join(INITRD_FILE);
468501

469502
let t_load = Instant::now();
470-
let mut sandbox = if initrd.is_file() {
471-
Sandbox::from_snapshot_file_with_initrd(&snapshot, &run_preopens, &initrd)?
472-
} else if run_preopens.is_empty() {
473-
Sandbox::from_snapshot_file(&snapshot)?
503+
let initrd_ref = if initrd.is_file() {
504+
Some(initrd.as_path())
474505
} else {
475-
Sandbox::from_snapshot_file_with(&snapshot, &run_preopens)?
506+
None
476507
};
508+
let mut sandbox = Sandbox::from_snapshot_file_configured(
509+
&snapshot,
510+
&run_preopens,
511+
initrd_ref,
512+
network.as_ref(),
513+
)?;
477514
if args.verbose {
478515
eprintln!(
479516
"[pyhl] load_snapshot={:.1}ms",

host/src/main.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
use anyhow::Result;
1010
use clap::Parser;
11-
use hyperlight_unikraft::{parse_memory, Preopen, Sandbox};
11+
use hyperlight_unikraft::{parse_memory, AllowList, NetworkPolicy, Preopen, Sandbox};
1212
use std::path::PathBuf;
1313

1414
#[derive(Parser, Debug)]
@@ -57,6 +57,17 @@ struct Args {
5757
#[arg(long, value_name = "HOST[:GUEST]")]
5858
mount: Vec<String>,
5959

60+
/// Enable guest networking. Without this flag, the guest has no
61+
/// network access.
62+
#[arg(long)]
63+
net: bool,
64+
65+
/// Restrict guest networking to the listed hosts/IPs.
66+
/// Implies --net. Hostnames are resolved at sandbox creation time.
67+
/// Repeatable: `--net-allow api.github.com --net-allow 10.0.0.1`.
68+
#[arg(long = "net-allow", value_name = "HOST_OR_IP")]
69+
net_allow: Vec<String>,
70+
6071
/// Run the application N additional times via snapshot/restore + call.
6172
/// The first run always happens. --repeat=2 means 3 total runs.
6273
#[arg(long, default_value = "0")]
@@ -152,6 +163,14 @@ fn main() -> Result<()> {
152163
None => args.app_args.clone(),
153164
};
154165

166+
let network = if !args.net_allow.is_empty() {
167+
Some(NetworkPolicy::AllowList(AllowList::from_hosts(&args.net_allow)?))
168+
} else if args.net {
169+
Some(NetworkPolicy::AllowAll)
170+
} else {
171+
None
172+
};
173+
155174
let mut builder = Sandbox::builder(&args.kernel)
156175
.args(app_args)
157176
.heap_size(heap_size)
@@ -162,6 +181,9 @@ fn main() -> Result<()> {
162181
for p in preopens {
163182
builder = builder.preopen(p);
164183
}
184+
if let Some(policy) = network {
185+
builder = builder.network(policy);
186+
}
165187
if args.enable_tools {
166188
builder = builder.tool("echo", Ok);
167189
}

0 commit comments

Comments
 (0)