mirror of
https://github.com/hanzoai/vm.git
synced 2026-10-01 00:47:56 +00:00
Allow configuring the DNS resolver
This commit is contained in:
Generated
+1
-1
@@ -1476,7 +1476,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "shuru-cli"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@@ -44,6 +44,9 @@ shuru run --allow-net
|
||||
# Restrict to specific hosts
|
||||
shuru run --allow-net --allow-host api.openai.com --allow-host registry.npmjs.org
|
||||
|
||||
# Use a specific DNS resolver
|
||||
shuru run --allow-net --dns-resolver 1.1.1.1 -- curl https://example.com
|
||||
|
||||
# Custom resources
|
||||
shuru run --cpus 4 --memory 4096 --disk-size 8192 -- make -j4
|
||||
```
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
@@ -30,6 +32,10 @@ pub(crate) struct VmArgs {
|
||||
#[arg(long)]
|
||||
pub allow_net: bool,
|
||||
|
||||
/// IPv4 DNS resolver to use for guest queries (default: 8.8.8.8)
|
||||
#[arg(long, value_name = "IP")]
|
||||
pub dns_resolver: Option<Ipv4Addr>,
|
||||
|
||||
/// Allow mounts to write to host filesystem (required for :rw mounts)
|
||||
#[arg(long)]
|
||||
pub allow_host_writes: bool,
|
||||
|
||||
@@ -70,6 +70,10 @@ pub(crate) fn prepare_vm(
|
||||
let proxy_config = if allow_net {
|
||||
let mut proxy = cfg.to_proxy_config();
|
||||
|
||||
if let Some(resolver) = vm.dns_resolver {
|
||||
proxy.dns_resolver = resolver;
|
||||
}
|
||||
|
||||
// Merge --secret flags: NAME=ENV_VAR@host1,host2
|
||||
for s in &vm.secret {
|
||||
let (name, from, hosts) = parse_secret_flag(s)
|
||||
|
||||
@@ -11,8 +11,10 @@ pub struct ExposeHostMapping {
|
||||
}
|
||||
|
||||
/// Configuration for the proxy engine.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProxyConfig {
|
||||
/// IPv4 DNS server used for upstream queries.
|
||||
pub dns_resolver: Ipv4Addr,
|
||||
/// Secrets to inject. Key is the env var name visible to the guest.
|
||||
/// The guest gets a random placeholder token; the proxy substitutes
|
||||
/// the real value only when the request targets an allowed host.
|
||||
@@ -23,6 +25,17 @@ pub struct ProxyConfig {
|
||||
pub expose_host: Vec<ExposeHostMapping>,
|
||||
}
|
||||
|
||||
impl Default for ProxyConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dns_resolver: Ipv4Addr::new(8, 8, 8, 8),
|
||||
secrets: HashMap::new(),
|
||||
network: NetworkConfig::default(),
|
||||
expose_host: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A secret that the proxy injects into HTTP requests.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SecretConfig {
|
||||
@@ -183,4 +196,12 @@ mod tests {
|
||||
};
|
||||
assert!(with_entries.has_allowlist());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_dns_resolver_is_google_public_dns() {
|
||||
assert_eq!(
|
||||
ProxyConfig::default().dns_resolver,
|
||||
Ipv4Addr::new(8, 8, 8, 8)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,11 @@ async fn resolve_query(
|
||||
return build_refused_response(query_bytes);
|
||||
}
|
||||
|
||||
// Resolve on the host by forwarding to system resolver
|
||||
// Resolve on the host by forwarding to the configured upstream resolver.
|
||||
let response_bytes = tokio::task::spawn_blocking({
|
||||
let query = query_bytes.to_vec();
|
||||
move || forward_to_system_resolver(&query)
|
||||
let resolver = config.dns_resolver;
|
||||
move || forward_to_system_resolver(&query, resolver)
|
||||
})
|
||||
.await??;
|
||||
|
||||
@@ -112,11 +113,15 @@ fn pin_resolved_ips(response: &[u8], allowed_ips: &AllowedIps) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Forward a raw DNS query to the system resolver and return the raw response.
|
||||
fn forward_to_system_resolver(query: &[u8]) -> anyhow::Result<Vec<u8>> {
|
||||
/// Forward a raw DNS query over UDP to the configured IPv4 resolver on port 53
|
||||
/// and return the raw response. This does not consult the host resolver API.
|
||||
///
|
||||
/// The name is intentional: using the host's resolver configuration remains
|
||||
/// the desired long-term behavior for this operation.
|
||||
fn forward_to_system_resolver(query: &[u8], resolver: Ipv4Addr) -> anyhow::Result<Vec<u8>> {
|
||||
let sock = UdpSocket::bind("0.0.0.0:0")?;
|
||||
sock.set_read_timeout(Some(std::time::Duration::from_secs(5)))?;
|
||||
sock.send_to(query, "8.8.8.8:53")?;
|
||||
sock.send_to(query, (resolver, 53))?;
|
||||
let mut buf = [0u8; 4096];
|
||||
let n = sock.recv(&mut buf)?;
|
||||
Ok(buf[..n].to_vec())
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
Networking is **off by default**. Pass `--allow-net` to enable it.
|
||||
|
||||
Use `--dns-resolver IP` to select the IPv4 DNS server used for upstream guest
|
||||
queries. The default is `8.8.8.8`.
|
||||
|
||||
## How It Works
|
||||
|
||||
All guest network traffic goes through a userspace proxy on the host (no NAT, no direct internet access). The proxy:
|
||||
@@ -19,6 +22,9 @@ ICMP (ping) is not supported — only TCP traffic is proxied.
|
||||
shuru run --allow-net -- sh -c 'apt-get install -y curl && curl https://example.com'
|
||||
```
|
||||
|
||||
For environments that require a specific DNS server, select it with
|
||||
`--dns-resolver`, for example `shuru run --allow-net --dns-resolver 10.0.0.53`.
|
||||
|
||||
Or set it in `shuru.json`:
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user