Skip to content

Commit 8277a34

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

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
@@ -299,20 +299,50 @@ impl BlockList {
299299
}
300300
}
301301

302+
/// Parse `/etc/resolv.conf` and return the set of nameserver IPs.
303+
///
304+
/// On non-Unix platforms this returns an empty set (DNS goes through the
305+
/// system resolver and never needs a raw port-53 exemption).
306+
fn parse_resolv_conf() -> HashSet<IpAddr> {
307+
#[cfg(unix)]
308+
{
309+
let mut resolvers = HashSet::new();
310+
if let Ok(contents) = std::fs::read_to_string("/etc/resolv.conf") {
311+
for line in contents.lines() {
312+
let line = line.trim();
313+
if let Some(rest) = line.strip_prefix("nameserver") {
314+
if let Some(ip_str) = rest.split_whitespace().next() {
315+
if let Ok(ip) = ip_str.parse::<IpAddr>() {
316+
resolvers.insert(ip);
317+
}
318+
}
319+
}
320+
}
321+
}
322+
resolvers
323+
}
324+
#[cfg(not(unix))]
325+
{
326+
HashSet::new()
327+
}
328+
}
329+
302330
impl NetworkPolicy {
303331
fn check(&self, addr: &std::net::SocketAddr) -> Result<()> {
304332
match self {
305333
NetworkPolicy::AllowAll => Ok(()),
306334
NetworkPolicy::AllowList(al) => {
307-
if addr.port() == 53 || al.is_allowed(&addr.ip()) {
335+
if (addr.port() == 53 && parse_resolv_conf().contains(&addr.ip()))
336+
|| al.is_allowed(&addr.ip())
337+
{
308338
Ok(())
309339
} else {
310340
Err(anyhow!("network policy denies connection to {}", addr))
311341
}
312342
}
313343
NetworkPolicy::BlockList(bl) => {
314-
// DNS (port 53) is always allowed so the guest can resolve names.
315-
if addr.port() == 53 {
344+
// Allow DNS (port 53) only to configured resolver IPs.
345+
if addr.port() == 53 && parse_resolv_conf().contains(&addr.ip()) {
316346
return Ok(());
317347
}
318348
if bl.is_blocked(&addr.ip()) {
@@ -2619,6 +2649,54 @@ mod tests {
26192649
assert!(err.to_string().contains("network policy denies"), "{err}");
26202650
}
26212651

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

26532731
#[test]
2654-
fn network_policy_blocklist_allows_dns() {
2732+
fn network_policy_blocklist_denies_blocked_ip_on_port53() {
2733+
// A blocked IP must not be exempted just because the port is 53.
26552734
let bl = BlockList::from_hosts(&["1.2.3.4"]).unwrap();
26562735
let policy = NetworkPolicy::BlockList(bl);
26572736
let addr: std::net::SocketAddr = "1.2.3.4:53".parse().unwrap();
2658-
assert!(policy.check(&addr).is_ok());
2737+
assert!(
2738+
policy.check(&addr).is_err(),
2739+
"blocked IP 1.2.3.4 must be denied even on port 53"
2740+
);
26592741
}
26602742

26612743
#[test]

0 commit comments

Comments
 (0)