add config option for allow_networks
This commit is contained in:
parent
4f4f3172bf
commit
f141667a0b
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1189,6 +1189,9 @@ name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
|
@ -391,10 +391,11 @@ fn main() {
|
||||
.iter()
|
||||
.flat_map(|x| (*x, listen_port).to_socket_addrs().unwrap())
|
||||
.collect();
|
||||
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::new(catalog);
|
||||
let mut server = ServerFuture::with_access(catalog, allow_networks);
|
||||
|
||||
// load all the listeners
|
||||
for udp_socket in &sockaddrs {
|
||||
|
@ -22,12 +22,8 @@ use hickory_client::tcp::TcpClientStream;
|
||||
use hickory_client::udp::UdpClientStream;
|
||||
use hickory_server::server::Protocol;
|
||||
|
||||
// TODO: Needed for when TLS tests are added back
|
||||
// #[cfg(feature = "dns-over-openssl")]
|
||||
// use hickory_proto::openssl::TlsClientStreamBuilder;
|
||||
|
||||
use hickory_proto::iocompat::AsyncIoTokioAsStd;
|
||||
use server_harness::{named_test_harness, query_a};
|
||||
use server_harness::{named_test_harness, query_a, query_a_refused};
|
||||
|
||||
#[test]
|
||||
fn test_example_toml_startup() {
|
||||
@ -342,3 +338,67 @@ fn test_forward() {
|
||||
assert!(!response.header().authoritative());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_allow_networks_toml_startup() {
|
||||
named_test_harness("example_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::<AsyncIoTokioAsStd<TokioTcpStream>>::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::<AsyncIoTokioAsStd<TokioTcpStream>>::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 succeed
|
||||
query_a(&mut io_loop, &mut client);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deny_networks_toml_startup() {
|
||||
named_test_harness("example_deny_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::<AsyncIoTokioAsStd<TokioTcpStream>>::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 be refused
|
||||
query_a_refused(&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::<AsyncIoTokioAsStd<TokioTcpStream>>::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);
|
||||
})
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ use std::{
|
||||
use hickory_client::{client::*, proto::xfer::DnsResponse};
|
||||
#[cfg(feature = "dnssec")]
|
||||
use hickory_proto::rr::dnssec::*;
|
||||
use hickory_proto::rr::{rdata::A, *};
|
||||
use hickory_proto::{
|
||||
op::ResponseCode,
|
||||
rr::{rdata::A, *},
|
||||
};
|
||||
use hickory_server::server::Protocol;
|
||||
use regex::Regex;
|
||||
use tokio::runtime::Runtime;
|
||||
@ -257,6 +260,15 @@ pub fn query_a<C: ClientHandle>(io_loop: &mut Runtime, client: &mut C) {
|
||||
}
|
||||
}
|
||||
|
||||
// This only validates that a query to the server works, it shouldn't be used for more than this.
|
||||
// i.e. more complex checks live with the clients and authorities to validate deeper functionality
|
||||
#[allow(dead_code)]
|
||||
pub fn query_a_refused<C: ClientHandle>(io_loop: &mut Runtime, client: &mut C) {
|
||||
let name = Name::from_str("www.example.com").unwrap();
|
||||
let response = query_message(io_loop, client, name, RecordType::A);
|
||||
assert_eq!(response.response_code(), ResponseCode::Refused);
|
||||
}
|
||||
|
||||
// This only validates that a query to the server works, it shouldn't be used for more than this.
|
||||
// i.e. more complex checks live with the clients and authorities to validate deeper functionality
|
||||
#[allow(dead_code)]
|
||||
|
@ -122,7 +122,7 @@ h2 = { workspace = true, features = ["stream"], optional = true }
|
||||
h3 = { workspace = true, optional = true }
|
||||
h3-quinn = { workspace = true, optional = true }
|
||||
http = { workspace = true, optional = true }
|
||||
ipnet.workspace = true
|
||||
ipnet = { workspace = true, features = ["serde"] }
|
||||
openssl = { workspace = true, features = ["v102", "v110"], optional = true }
|
||||
prefix-trie.workspace = true
|
||||
rusqlite = { workspace = true, features = ["bundled", "time"], optional = true }
|
||||
|
@ -32,6 +32,12 @@ impl Access {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert_all(&mut self, networks: &[IpNet]) {
|
||||
for net in networks {
|
||||
self.insert(*net);
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate the IP address against the allowed networks
|
||||
///
|
||||
/// # Return
|
||||
@ -67,7 +73,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_none() {
|
||||
let mut access = Access::default();
|
||||
let access = Access::default();
|
||||
assert!(access.allow("192.168.1.1".parse().unwrap()).is_ok());
|
||||
assert!(access.allow("fd00::1".parse().unwrap()).is_ok());
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use ipnet::IpNet;
|
||||
use serde::{self, Deserialize};
|
||||
|
||||
use crate::proto::error::ProtoResult;
|
||||
@ -68,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<dnssec::TlsCertConfig>,
|
||||
/// Networks allowed to access the server
|
||||
#[serde(default)]
|
||||
allow_networks: Vec<IpNet>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -160,6 +164,11 @@ impl Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// get the networks allowed to connect to this server
|
||||
pub fn get_allow_networks(&self) -> &[IpNet] {
|
||||
&self.allow_networks
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a zone
|
||||
|
@ -13,6 +13,7 @@ use std::{
|
||||
|
||||
use futures_util::{FutureExt, StreamExt};
|
||||
use hickory_proto::{op::MessageType, rr::Record};
|
||||
use ipnet::IpNet;
|
||||
#[cfg(feature = "dns-over-rustls")]
|
||||
use rustls::{Certificate, PrivateKey, ServerConfig};
|
||||
use tokio::{net, task::JoinSet};
|
||||
@ -49,11 +50,19 @@ pub struct ServerFuture<T: RequestHandler> {
|
||||
impl<T: RequestHandler> ServerFuture<T> {
|
||||
/// Creates a new ServerFuture with the specified Handler.
|
||||
pub fn new(handler: T) -> Self {
|
||||
Self::with_access(handler, &[])
|
||||
}
|
||||
|
||||
/// Creates a new ServerFuture with the specified Handler and Access
|
||||
pub fn with_access(handler: T, allowed_networks: &[IpNet]) -> Self {
|
||||
let mut access = Access::default();
|
||||
access.insert_all(allowed_networks);
|
||||
|
||||
Self {
|
||||
handler: Arc::new(handler),
|
||||
join_set: JoinSet::new(),
|
||||
shutdown_token: CancellationToken::new(),
|
||||
access: Arc::new(Access::default()),
|
||||
access: Arc::new(access),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,10 @@
|
||||
## directory: path on the host filesystem to where zone files are stored.
|
||||
# directory = "/var/named"
|
||||
|
||||
## Allowed networks, a list of CIDRs in IPv4 or IPv6 formats,
|
||||
## any request that does not originate from the specified networks will be denied
|
||||
# allow_networks = ["127/8", "::1/128"]
|
||||
|
||||
## Default zones, these should be present on all nameservers, except in rare
|
||||
## configuration cases
|
||||
[[zones]]
|
||||
|
10
tests/test-data/test_configs/example_allow_networks.toml
Normal file
10
tests/test-data/test_configs/example_allow_networks.toml
Normal file
@ -0,0 +1,10 @@
|
||||
listen_addrs_ipv4 = ["0.0.0.0"]
|
||||
listen_addrs_ipv6 = ["::0"]
|
||||
|
||||
## Allowed networks, all of the 127 network space is allowed (local), and only the localhost in ipv6 is allowed.
|
||||
allow_networks = ["127.0.0.0/8", "::1/128"]
|
||||
|
||||
[[zones]]
|
||||
zone = "example.com"
|
||||
zone_type = "Primary"
|
||||
file = "example.com.zone"
|
10
tests/test-data/test_configs/example_deny_networks.toml
Normal file
10
tests/test-data/test_configs/example_deny_networks.toml
Normal file
@ -0,0 +1,10 @@
|
||||
listen_addrs_ipv4 = ["0.0.0.0"]
|
||||
listen_addrs_ipv6 = ["::0"]
|
||||
|
||||
## Allowed networks, only the 0 address is allowed, which means everything is denied
|
||||
allow_networks = ["0.0.0.0/8", "::/128"]
|
||||
|
||||
[[zones]]
|
||||
zone = "example.com"
|
||||
zone_type = "Primary"
|
||||
file = "example.com.zone"
|
Loading…
Reference in New Issue
Block a user