Skip to content

Commit 02dd490

Browse files
committed
fix: scope DNS port-53 exemption to configured resolver IPs
Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent f5eb506 commit 02dd490

1 file changed

Lines changed: 87 additions & 5 deletions

File tree

host/src/lib.rs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,20 +304,50 @@ impl BlockList {
304304
}
305305
}
306306

307+
/// Parse `/etc/resolv.conf` and return the set of nameserver IPs.
308+
///
309+
/// On non-Unix platforms this returns an empty set (DNS goes through the
310+
/// system resolver and never needs a raw port-53 exemption).
311+
fn parse_resolv_conf() -> HashSet<IpAddr> {
312+
#[cfg(unix)]
313+
{
314+
let mut resolvers = HashSet::new();
315+
if let Ok(contents) = std::fs::read_to_string("/etc/resolv.conf") {
316+
for line in contents.lines() {
317+
let line = line.trim();
318+
if let Some(rest) = line.strip_prefix("nameserver") {
319+
if let Some(ip_str) = rest.split_whitespace().next() {
320+
if let Ok(ip) = ip_str.parse::<IpAddr>() {
321+
resolvers.insert(ip);
322+
}
323+
}
324+
}
325+
}
326+
}
327+
resolvers
328+
}
329+
#[cfg(not(unix))]
330+
{
331+
HashSet::new()
332+
}
333+
}
334+
307335
impl NetworkPolicy {
308336
fn check(&self, addr: &std::net::SocketAddr) -> Result<()> {
309337
match self {
310338
NetworkPolicy::AllowAll => Ok(()),
311339
NetworkPolicy::AllowList(al) => {
312-
if addr.port() == 53 || al.is_allowed(&addr.ip()) {
340+
if (addr.port() == 53 && parse_resolv_conf().contains(&addr.ip()))
341+
|| al.is_allowed(&addr.ip())
342+
{
313343
Ok(())
314344
} else {
315345
Err(anyhow!("network policy denies connection to {}", addr))
316346
}
317347
}
318348
NetworkPolicy::BlockList(bl) => {
319-
// DNS (port 53) is always allowed so the guest can resolve names.
320-
if addr.port() == 53 {
349+
// Allow DNS (port 53) only to configured resolver IPs.
350+
if addr.port() == 53 && parse_resolv_conf().contains(&addr.ip()) {
321351
return Ok(());
322352
}
323353
if bl.is_blocked(&addr.ip()) {
@@ -2624,6 +2654,54 @@ mod tests {
26242654
assert!(err.to_string().contains("network policy denies"), "{err}");
26252655
}
26262656

2657+
#[test]
2658+
fn test_port53_arbitrary_ip_blocked() {
2659+
// Only 1.1.1.1 is in the allowlist — 8.8.8.8:53 must be denied
2660+
// because 8.8.8.8 is (almost certainly) not in /etc/resolv.conf.
2661+
let al = AllowList::from_hosts(&["1.1.1.1"]).unwrap();
2662+
let policy = NetworkPolicy::AllowList(al);
2663+
let addr: std::net::SocketAddr = "8.8.8.8:53".parse().unwrap();
2664+
assert!(
2665+
policy.check(&addr).is_err(),
2666+
"port 53 to a non-resolver IP must be denied"
2667+
);
2668+
}
2669+
2670+
#[cfg(unix)]
2671+
#[test]
2672+
fn test_port53_real_resolver_allowed() {
2673+
// Grab the first resolver from /etc/resolv.conf and verify port
2674+
// 53 to that IP is allowed even when the IP is not in the
2675+
// AllowList itself.
2676+
let resolvers = parse_resolv_conf();
2677+
if resolvers.is_empty() {
2678+
eprintln!("skipping: no resolvers found in /etc/resolv.conf");
2679+
return;
2680+
}
2681+
let resolver_ip = *resolvers.iter().next().unwrap();
2682+
let al = AllowList::from_hosts(&["1.1.1.1"]).unwrap();
2683+
let policy = NetworkPolicy::AllowList(al);
2684+
let addr = std::net::SocketAddr::new(resolver_ip, 53);
2685+
assert!(
2686+
policy.check(&addr).is_ok(),
2687+
"port 53 to a configured resolver ({}) must be allowed",
2688+
resolver_ip
2689+
);
2690+
}
2691+
2692+
#[test]
2693+
fn test_port53_blocklist_enforced() {
2694+
// Even on port 53, a blocklisted IP must be denied — unless it
2695+
// happens to be a real resolver. Use a bogus IP to be safe.
2696+
let bl = BlockList::from_hosts(&["192.0.2.1"]).unwrap();
2697+
let policy = NetworkPolicy::BlockList(bl);
2698+
let addr: std::net::SocketAddr = "192.0.2.1:53".parse().unwrap();
2699+
assert!(
2700+
policy.check(&addr).is_err(),
2701+
"blocklisted IP must be denied even on port 53"
2702+
);
2703+
}
2704+
26272705
#[test]
26282706
fn allowlist_resolves_hostnames() {
26292707
let al = AllowList::from_hosts(&["localhost"]).unwrap();
@@ -2656,11 +2734,15 @@ mod tests {
26562734
}
26572735

26582736
#[test]
2659-
fn network_policy_blocklist_allows_dns() {
2737+
fn network_policy_blocklist_denies_blocked_ip_on_port53() {
2738+
// A blocked IP must not be exempted just because the port is 53.
26602739
let bl = BlockList::from_hosts(&["1.2.3.4"]).unwrap();
26612740
let policy = NetworkPolicy::BlockList(bl);
26622741
let addr: std::net::SocketAddr = "1.2.3.4:53".parse().unwrap();
2663-
assert!(policy.check(&addr).is_ok());
2742+
assert!(
2743+
policy.check(&addr).is_err(),
2744+
"blocked IP 1.2.3.4 must be denied even on port 53"
2745+
);
26642746
}
26652747

26662748
#[test]

0 commit comments

Comments
 (0)