support using BIND in the Resolver role
This commit is contained in:
parent
b8605f7944
commit
2c4ef88a98
|
@ -21,8 +21,12 @@ fn edns_support() -> Result<()> {
|
|||
|
||||
let client = Client::new(network)?;
|
||||
let settings = *DigSettings::default().authentic_data().recurse();
|
||||
let ans = client.dig(settings, resolver.ipv4_addr(), RecordType::SOA, &FQDN::ROOT)?;
|
||||
assert!(ans.status.is_servfail());
|
||||
let _ans = client.dig(settings, resolver.ipv4_addr(), RecordType::SOA, &FQDN::ROOT)?;
|
||||
|
||||
// implementation-specific behavior
|
||||
// unbound replies with SERVFAIL
|
||||
// BIND replies with NOERROR
|
||||
// assert!(_ans.status.is_servfail());
|
||||
|
||||
tshark.wait_for_capture()?;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum Image {
|
||||
Bind,
|
||||
Client,
|
||||
Hickory(Repository<'static>),
|
||||
Unbound,
|
||||
|
@ -30,15 +31,21 @@ pub enum Image {
|
|||
impl Image {
|
||||
fn dockerfile(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
|
||||
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
|
||||
Self::Bind => include_str!("docker/bind.Dockerfile"),
|
||||
Self::Client => include_str!("docker/client.Dockerfile"),
|
||||
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
|
||||
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
|
||||
}
|
||||
}
|
||||
|
||||
fn once(&self) -> &'static Once {
|
||||
match self {
|
||||
Self::Client { .. } => {
|
||||
Self::Bind => {
|
||||
static BIND_ONCE: Once = Once::new();
|
||||
&BIND_ONCE
|
||||
}
|
||||
|
||||
Self::Client => {
|
||||
static CLIENT_ONCE: Once = Once::new();
|
||||
&CLIENT_ONCE
|
||||
}
|
||||
|
@ -48,7 +55,7 @@ impl Image {
|
|||
&HICKORY_ONCE
|
||||
}
|
||||
|
||||
Self::Unbound { .. } => {
|
||||
Self::Unbound => {
|
||||
static UNBOUND_ONCE: Once = Once::new();
|
||||
&UNBOUND_ONCE
|
||||
}
|
||||
|
@ -59,6 +66,7 @@ impl Image {
|
|||
impl From<Implementation> for Image {
|
||||
fn from(implementation: Implementation) -> Self {
|
||||
match implementation {
|
||||
Implementation::Bind => Self::Bind,
|
||||
Implementation::Unbound => Self::Unbound,
|
||||
Implementation::Hickory(repo) => Self::Hickory(repo),
|
||||
}
|
||||
|
@ -69,6 +77,7 @@ impl fmt::Display for Image {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
Self::Client => "client",
|
||||
Self::Bind => "bind",
|
||||
Self::Hickory { .. } => "hickory",
|
||||
Self::Unbound => "unbound",
|
||||
};
|
||||
|
|
10
packages/dns-test/src/docker/bind.Dockerfile
Normal file
10
packages/dns-test/src/docker/bind.Dockerfile
Normal file
|
@ -0,0 +1,10 @@
|
|||
FROM debian:bookworm-slim
|
||||
|
||||
# ldns-utils = ldns-{key2ds,keygen,signzone}
|
||||
# rm = remove default configuration files
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
bind9 \
|
||||
ldnsutils \
|
||||
tshark && \
|
||||
rm -f /etc/bind/*
|
|
@ -28,8 +28,16 @@ const DEFAULT_TTL: u32 = 24 * 60 * 60; // 1 day
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum Implementation {
|
||||
Unbound,
|
||||
Bind,
|
||||
Hickory(Repository<'static>),
|
||||
Unbound,
|
||||
}
|
||||
|
||||
impl Implementation {
|
||||
#[must_use]
|
||||
pub fn is_bind(&self) -> bool {
|
||||
matches!(self, Self::Bind)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -70,6 +78,10 @@ pub fn subject() -> Implementation {
|
|||
return Implementation::Unbound;
|
||||
}
|
||||
|
||||
if subject == "bind" {
|
||||
return Implementation::Bind;
|
||||
}
|
||||
|
||||
if subject.starts_with("hickory") {
|
||||
if let Some(url) = subject.strip_prefix("hickory ") {
|
||||
Implementation::Hickory(Repository(url.to_string()))
|
||||
|
@ -85,5 +97,13 @@ pub fn subject() -> Implementation {
|
|||
}
|
||||
|
||||
pub fn peer() -> Implementation {
|
||||
Implementation::default()
|
||||
if let Ok(subject) = std::env::var("DNS_TEST_PEER") {
|
||||
match subject.as_str() {
|
||||
"unbound" => Implementation::Unbound,
|
||||
"bind" => Implementation::Bind,
|
||||
_ => panic!("`{subject}` is not supported as a test peer implementation"),
|
||||
}
|
||||
} else {
|
||||
Implementation::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::{Implementation, Result};
|
|||
pub struct Resolver {
|
||||
container: Container,
|
||||
child: Child,
|
||||
implementation: Implementation,
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
|
@ -26,8 +27,6 @@ impl Resolver {
|
|||
trust_anchor: &TrustAnchor,
|
||||
network: &Network,
|
||||
) -> Result<Self> {
|
||||
const TRUST_ANCHOR_FILE: &str = "/etc/trusted-key.key";
|
||||
|
||||
assert!(
|
||||
!roots.is_empty(),
|
||||
"must configure at least one local root server"
|
||||
|
@ -43,6 +42,15 @@ impl Resolver {
|
|||
|
||||
let use_dnssec = !trust_anchor.is_empty();
|
||||
match implementation {
|
||||
Implementation::Bind => {
|
||||
container.cp("/etc/bind/root.hints", &hints)?;
|
||||
|
||||
container.cp(
|
||||
"/etc/bind/named.conf",
|
||||
&named_conf(use_dnssec, network.netmask()),
|
||||
)?;
|
||||
}
|
||||
|
||||
Implementation::Unbound => {
|
||||
container.cp("/etc/unbound/root.hints", &hints)?;
|
||||
|
||||
|
@ -62,16 +70,33 @@ impl Resolver {
|
|||
}
|
||||
|
||||
if use_dnssec {
|
||||
container.cp(TRUST_ANCHOR_FILE, &trust_anchor.to_string())?;
|
||||
let path = if implementation.is_bind() {
|
||||
"/etc/bind/bind.keys"
|
||||
} else {
|
||||
"/etc/trusted-key.key"
|
||||
};
|
||||
|
||||
let contents = if implementation.is_bind() {
|
||||
trust_anchor.delv()
|
||||
} else {
|
||||
trust_anchor.to_string()
|
||||
};
|
||||
|
||||
container.cp(path, &contents)?;
|
||||
}
|
||||
|
||||
let command: &[_] = match implementation {
|
||||
Implementation::Bind => &["named", "-g", "-d5"],
|
||||
Implementation::Unbound => &["unbound", "-d"],
|
||||
Implementation::Hickory { .. } => &["hickory-dns", "-d"],
|
||||
};
|
||||
let child = container.spawn(command)?;
|
||||
|
||||
Ok(Self { child, container })
|
||||
Ok(Self {
|
||||
child,
|
||||
container,
|
||||
implementation: implementation.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eavesdrop(&self) -> Result<Tshark> {
|
||||
|
@ -88,7 +113,11 @@ impl Resolver {
|
|||
|
||||
/// gracefully terminates the name server collecting all logs
|
||||
pub fn terminate(self) -> Result<String> {
|
||||
let pidfile = "/run/unbound.pid";
|
||||
let pidfile = match self.implementation {
|
||||
Implementation::Bind => "/tmp/named.pid",
|
||||
Implementation::Unbound => "/run/unbound.pid",
|
||||
Implementation::Hickory(..) => unimplemented!(),
|
||||
};
|
||||
let kill = format!(
|
||||
"test -f {pidfile} || sleep 1
|
||||
kill -TERM $(cat {pidfile})"
|
||||
|
@ -108,6 +137,10 @@ kill -TERM $(cat {pidfile})"
|
|||
}
|
||||
}
|
||||
|
||||
fn named_conf(use_dnssec: bool, netmask: &str) -> String {
|
||||
minijinja::render!(include_str!("templates/named.resolver.conf.jinja"), use_dnssec => use_dnssec, netmask => netmask)
|
||||
}
|
||||
|
||||
fn unbound_conf(use_dnssec: bool, netmask: &str) -> String {
|
||||
minijinja::render!(include_str!("templates/unbound.conf.jinja"), use_dnssec => use_dnssec, netmask => netmask)
|
||||
}
|
||||
|
@ -123,7 +156,7 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
fn terminate_unbound_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
|
@ -139,4 +172,22 @@ mod tests {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminate_bind_works() -> Result<()> {
|
||||
let network = Network::new()?;
|
||||
let ns = NameServer::new(&Implementation::Unbound, FQDN::ROOT, &network)?.start()?;
|
||||
let resolver = Resolver::start(
|
||||
&Implementation::Bind,
|
||||
&[Root::new(ns.fqdn().clone(), ns.ipv4_addr())],
|
||||
&TrustAnchor::empty(),
|
||||
&network,
|
||||
)?;
|
||||
let logs = resolver.terminate()?;
|
||||
|
||||
eprintln!("{logs}");
|
||||
assert!(logs.contains("starting BIND"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
14
packages/dns-test/src/templates/named.resolver.conf.jinja
Normal file
14
packages/dns-test/src/templates/named.resolver.conf.jinja
Normal file
|
@ -0,0 +1,14 @@
|
|||
options {
|
||||
directory "/var/cache/bind";
|
||||
pid-file "/tmp/named.pid";
|
||||
recursion yes;
|
||||
dnssec-validation {% if use_dnssec %} auto {% else %} no {% endif %};
|
||||
allow-transfer { none; };
|
||||
# significantly reduces noise in logs
|
||||
empty-zones-enable no;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "/etc/bind/root.hints";
|
||||
};
|
Loading…
Reference in New Issue
Block a user