From d6a927aba82fdbb5391371255cff8fb15f686580 Mon Sep 17 00:00:00 2001 From: Benjamin Fry Date: Sun, 7 Jan 2024 11:47:20 -0800 Subject: [PATCH] add deny networks config option and examples --- bin/src/hickory-dns.rs | 3 +- bin/tests/named_tests.rs | 32 +++++++++++++++++++ crates/server/src/config/mod.rs | 8 +++++ crates/server/src/server/server_future.rs | 5 +-- tests/test-data/test_configs/example.toml | 9 +++++- .../example_deny_allow_networks.toml | 12 +++++++ 6 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 tests/test-data/test_configs/example_deny_allow_networks.toml diff --git a/bin/src/hickory-dns.rs b/bin/src/hickory-dns.rs index 8af21823..8ea1784b 100644 --- a/bin/src/hickory-dns.rs +++ b/bin/src/hickory-dns.rs @@ -391,11 +391,12 @@ fn main() { .iter() .flat_map(|x| (*x, listen_port).to_socket_addrs().unwrap()) .collect(); + let deny_networks = config.get_deny_networks(); let allow_networks = config.get_allow_networks(); // now, run the server, based on the config #[cfg_attr(not(feature = "dns-over-tls"), allow(unused_mut))] - let mut server = ServerFuture::with_access(catalog, allow_networks); + let mut server = ServerFuture::with_access(catalog, deny_networks, allow_networks); // load all the listeners for udp_socket in &sockaddrs { diff --git a/bin/tests/named_tests.rs b/bin/tests/named_tests.rs index a24ae8d3..ea9838ac 100644 --- a/bin/tests/named_tests.rs +++ b/bin/tests/named_tests.rs @@ -402,3 +402,35 @@ fn test_deny_networks_toml_startup() { query_a_refused(&mut io_loop, &mut client); }) } + +#[test] +fn test_deny_allow_networks_toml_startup() { + named_test_harness("example_deny_allow_networks.toml", |socket_ports| { + let mut io_loop = Runtime::new().unwrap(); + let tcp_port = socket_ports.get_v4(Protocol::Tcp); + let addr: SocketAddr = SocketAddr::new( + Ipv4Addr::new(127, 0, 0, 1).into(), + tcp_port.expect("no tcp_port"), + ); + let (stream, sender) = TcpClientStream::>::new(addr); + let client = AsyncClient::new(Box::new(stream), sender, None); + + let (mut client, bg) = io_loop.block_on(client).expect("client failed to connect"); + hickory_proto::spawn_bg(&io_loop, bg); + // ipv4 should succeed + query_a(&mut io_loop, &mut client); + + let tcp_port = socket_ports.get_v6(Protocol::Tcp); + let addr: SocketAddr = SocketAddr::new( + Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).into(), + tcp_port.expect("no tcp_port"), + ); + let (stream, sender) = TcpClientStream::>::new(addr); + let client = AsyncClient::new(Box::new(stream), sender, None); + let (mut client, bg) = io_loop.block_on(client).expect("client failed to connect"); + hickory_proto::spawn_bg(&io_loop, bg); + + // ipv6 should be refused + query_a_refused(&mut io_loop, &mut client); + }) +} diff --git a/crates/server/src/config/mod.rs b/crates/server/src/config/mod.rs index 96f5b058..c13bf428 100644 --- a/crates/server/src/config/mod.rs +++ b/crates/server/src/config/mod.rs @@ -69,6 +69,9 @@ pub struct Config { /// Certificate to associate to TLS connections (currently the same is used for HTTPS and TLS) #[cfg(feature = "dnssec")] tls_cert: Option, + /// Networks denied to access the server + #[serde(default)] + deny_networks: Vec, /// Networks allowed to access the server #[serde(default)] allow_networks: Vec, @@ -165,6 +168,11 @@ impl Config { } } + /// get the networks denied access to this server + pub fn get_deny_networks(&self) -> &[IpNet] { + &self.deny_networks + } + /// get the networks allowed to connect to this server pub fn get_allow_networks(&self) -> &[IpNet] { &self.allow_networks diff --git a/crates/server/src/server/server_future.rs b/crates/server/src/server/server_future.rs index c10f61e6..7f78b970 100644 --- a/crates/server/src/server/server_future.rs +++ b/crates/server/src/server/server_future.rs @@ -50,12 +50,13 @@ pub struct ServerFuture { impl ServerFuture { /// Creates a new ServerFuture with the specified Handler. pub fn new(handler: T) -> Self { - Self::with_access(handler, &[]) + Self::with_access(handler, &[], &[]) } /// Creates a new ServerFuture with the specified Handler and Access - pub fn with_access(handler: T, allowed_networks: &[IpNet]) -> Self { + pub fn with_access(handler: T, denied_networks: &[IpNet], allowed_networks: &[IpNet]) -> Self { let mut access = Access::default(); + access.insert_deny_all(denied_networks); access.insert_allow_all(allowed_networks); Self { diff --git a/tests/test-data/test_configs/example.toml b/tests/test-data/test_configs/example.toml index dc0add7c..e0f525a1 100644 --- a/tests/test-data/test_configs/example.toml +++ b/tests/test-data/test_configs/example.toml @@ -40,8 +40,15 @@ ## directory: path on the host filesystem to where zone files are stored. # directory = "/var/named" +## Denied networks, a list of CIDRs in IPv4 or IPv6 formats, +## any request that does not originate from the specified networks will be allowed +# deny_networks = ["127/8", "::1/128"] + ## Allowed networks, a list of CIDRs in IPv4 or IPv6 formats, -## any request that does not originate from the specified networks will be denied +## any request that does not originate from the specified networks will be denied, unless +## there are deny_networks specified, in that case, the allow list will be processed as +## an override to the deny_networks. That is, if there is a deny_list and the network does +## not appear there, even if does not appear in the allow list the request will be allowd. # allow_networks = ["127/8", "::1/128"] ## Default zones, these should be present on all nameservers, except in rare diff --git a/tests/test-data/test_configs/example_deny_allow_networks.toml b/tests/test-data/test_configs/example_deny_allow_networks.toml new file mode 100644 index 00000000..452a4933 --- /dev/null +++ b/tests/test-data/test_configs/example_deny_allow_networks.toml @@ -0,0 +1,12 @@ +listen_addrs_ipv4 = ["0.0.0.0"] +listen_addrs_ipv6 = ["::0"] + +## Half o the 127 ipv4 space is denied, but there are no denied ipv6 addresses +deny_networks = ["127.0.0.0/8"] +## The 127.0.0.1 address is allowed, overriding the deny, but the ipv6 allow is effectively none, so all are denied +allow_networks = ["127.0.0.1/32", "::/128"] + +[[zones]] +zone = "example.com" +zone_type = "Primary" +file = "example.com.zone"