make Client::delv
work & use it in dnssec tests
This commit is contained in:
parent
edd6eebe1a
commit
5c53ba0899
|
@ -4,7 +4,7 @@ use dns_test::client::{Client, Dnssec, Recurse};
|
|||
use dns_test::name_server::NameServer;
|
||||
use dns_test::record::RecordType;
|
||||
use dns_test::zone_file::Root;
|
||||
use dns_test::{RecursiveResolver, Result, FQDN};
|
||||
use dns_test::{RecursiveResolver, Result, TrustAnchor, FQDN};
|
||||
|
||||
#[test]
|
||||
fn can_resolve() -> Result<()> {
|
||||
|
@ -38,7 +38,7 @@ fn can_resolve() -> Result<()> {
|
|||
eprintln!("root.zone:\n{}", root_ns.zone_file());
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
let resolver = RecursiveResolver::start(roots, &[])?;
|
||||
let resolver = RecursiveResolver::start(roots, &TrustAnchor::empty())?;
|
||||
let resolver_ip_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new()?;
|
||||
|
|
|
@ -4,7 +4,7 @@ use dns_test::client::{Client, Dnssec, Recurse};
|
|||
use dns_test::name_server::NameServer;
|
||||
use dns_test::record::RecordType;
|
||||
use dns_test::zone_file::Root;
|
||||
use dns_test::{RecursiveResolver, Result, FQDN};
|
||||
use dns_test::{RecursiveResolver, Result, TrustAnchor, FQDN};
|
||||
|
||||
// no DS records are involved; this is a single-link chain of trust
|
||||
#[test]
|
||||
|
@ -24,7 +24,7 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||
|
||||
let roots = &[Root::new(ns.fqdn().clone(), ns.ipv4_addr())];
|
||||
|
||||
let trust_anchor = [root_ksk.clone(), root_zsk.clone()];
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = RecursiveResolver::start(roots, &trust_anchor)?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
|
@ -40,13 +40,16 @@ fn can_validate_without_delegation() -> Result<()> {
|
|||
assert!(output.status.is_noerror());
|
||||
assert!(output.flags.authenticated_data);
|
||||
|
||||
let output = client.delv(resolver_addr, RecordType::SOA, &FQDN::ROOT, &trust_anchor)?;
|
||||
assert!(output.starts_with("; fully validated"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_validate_with_delegation() -> Result<()> {
|
||||
let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4);
|
||||
let needle = FQDN("example.nameservers.com.")?;
|
||||
let needle_fqdn = FQDN("example.nameservers.com.")?;
|
||||
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM)?;
|
||||
|
@ -55,7 +58,7 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr())
|
||||
.a(needle.clone(), expected_ipv4_addr);
|
||||
.a(needle_fqdn.clone(), expected_ipv4_addr);
|
||||
let nameservers_ns = nameservers_ns.sign()?;
|
||||
let nameservers_ds = nameservers_ns.ds().clone();
|
||||
let nameservers_ns = nameservers_ns.start()?;
|
||||
|
@ -90,20 +93,19 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
|
||||
let resolver = RecursiveResolver::start(roots, &[root_ksk.clone(), root_zsk.clone()])?;
|
||||
let resolver_ip_addr = resolver.ipv4_addr();
|
||||
let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]);
|
||||
let resolver = RecursiveResolver::start(roots, &trust_anchor)?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new()?;
|
||||
let output = client.dig(
|
||||
Recurse::Yes,
|
||||
Dnssec::Yes,
|
||||
resolver_ip_addr,
|
||||
resolver_addr,
|
||||
RecordType::A,
|
||||
&needle,
|
||||
&needle_fqdn,
|
||||
)?;
|
||||
|
||||
drop(resolver);
|
||||
|
||||
assert!(output.status.is_noerror());
|
||||
|
||||
assert!(output.flags.authenticated_data);
|
||||
|
@ -111,8 +113,11 @@ fn can_validate_with_delegation() -> Result<()> {
|
|||
let [a, _rrsig] = output.answer.try_into().unwrap();
|
||||
let a = a.try_into_a().unwrap();
|
||||
|
||||
assert_eq!(needle, a.fqdn);
|
||||
assert_eq!(needle_fqdn, a.fqdn);
|
||||
assert_eq!(expected_ipv4_addr, a.ipv4_addr);
|
||||
|
||||
let output = client.delv(resolver_addr, RecordType::A, &needle_fqdn, &trust_anchor)?;
|
||||
assert!(output.starts_with("; fully validated"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::net::Ipv4Addr;
|
|||
|
||||
use crate::container::Container;
|
||||
use crate::record::{Record, RecordType};
|
||||
use crate::trust_anchor::TrustAnchor;
|
||||
use crate::{Error, Result, FQDN};
|
||||
|
||||
pub struct Client {
|
||||
|
@ -16,19 +17,29 @@ impl Client {
|
|||
})
|
||||
}
|
||||
|
||||
// FIXME this needs to use the same trust anchor as `RecursiveResolver` or validation will fail
|
||||
pub fn delv(
|
||||
&self,
|
||||
server: Ipv4Addr,
|
||||
record_type: RecordType,
|
||||
fqdn: &FQDN<'_>,
|
||||
trust_anchor: &TrustAnchor,
|
||||
) -> Result<String> {
|
||||
const TRUST_ANCHOR_PATH: &str = "/etc/bind.keys";
|
||||
|
||||
assert!(
|
||||
!trust_anchor.is_empty(),
|
||||
"`delv` cannot be used with an empty trust anchor"
|
||||
);
|
||||
|
||||
self.inner.cp(TRUST_ANCHOR_PATH, &trust_anchor.delv())?;
|
||||
|
||||
self.inner.stdout(&[
|
||||
"delv",
|
||||
"+mtrace",
|
||||
&format!("@{server}"),
|
||||
record_type.as_str(),
|
||||
"-a",
|
||||
TRUST_ANCHOR_PATH,
|
||||
fqdn.as_str(),
|
||||
record_type.as_str(),
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,17 @@ impl Container {
|
|||
/// Similar to `Self::output` but checks `command_and_args` ran successfully and only
|
||||
/// returns the stdout
|
||||
pub fn stdout(&self, command_and_args: &[&str]) -> Result<String> {
|
||||
let output = self.output(command_and_args)?;
|
||||
let Output {
|
||||
status,
|
||||
stderr,
|
||||
stdout,
|
||||
} = self.output(command_and_args)?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(output.stdout)
|
||||
if status.success() {
|
||||
Ok(stdout)
|
||||
} else {
|
||||
eprintln!("STDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
||||
|
||||
Err(format!("[{}] `{command_and_args:?}` failed", self.inner.name).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub use crate::fqdn::FQDN;
|
||||
pub use crate::recursive_resolver::RecursiveResolver;
|
||||
pub use crate::trust_anchor::TrustAnchor;
|
||||
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
@ -12,4 +13,5 @@ mod fqdn;
|
|||
pub mod name_server;
|
||||
pub mod record;
|
||||
mod recursive_resolver;
|
||||
mod trust_anchor;
|
||||
pub mod zone_file;
|
||||
|
|
|
@ -2,7 +2,8 @@ use core::fmt::Write;
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::container::{Child, Container};
|
||||
use crate::zone_file::{Root, DNSKEY};
|
||||
use crate::trust_anchor::TrustAnchor;
|
||||
use crate::zone_file::Root;
|
||||
use crate::Result;
|
||||
|
||||
pub struct RecursiveResolver {
|
||||
|
@ -11,7 +12,7 @@ pub struct RecursiveResolver {
|
|||
}
|
||||
|
||||
impl RecursiveResolver {
|
||||
pub fn start(roots: &[Root], trust_anchors: &[DNSKEY]) -> Result<Self> {
|
||||
pub fn start(roots: &[Root], trust_anchor: &TrustAnchor) -> Result<Self> {
|
||||
const TRUST_ANCHOR_FILE: &str = "/etc/trusted-key.key";
|
||||
|
||||
let container = Container::run()?;
|
||||
|
@ -23,16 +24,11 @@ impl RecursiveResolver {
|
|||
|
||||
container.cp("/etc/unbound/root.hints", &hints)?;
|
||||
|
||||
let use_dnssec = !trust_anchors.is_empty();
|
||||
let use_dnssec = !trust_anchor.is_empty();
|
||||
container.cp("/etc/unbound/unbound.conf", &unbound_conf(use_dnssec))?;
|
||||
|
||||
if use_dnssec {
|
||||
let trust_anchor = trust_anchors.iter().fold(String::new(), |mut buf, ds| {
|
||||
writeln!(buf, "{ds}").expect("infallible");
|
||||
buf
|
||||
});
|
||||
|
||||
container.cp(TRUST_ANCHOR_FILE, &trust_anchor)?;
|
||||
container.cp(TRUST_ANCHOR_FILE, &trust_anchor.to_string())?;
|
||||
}
|
||||
|
||||
let child = container.spawn(&["unbound", "-d"])?;
|
||||
|
@ -76,7 +72,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn terminate_works() -> Result<()> {
|
||||
let resolver = RecursiveResolver::start(&[], &[])?;
|
||||
let resolver = RecursiveResolver::start(&[], &TrustAnchor::empty())?;
|
||||
let logs = resolver.terminate()?;
|
||||
|
||||
eprintln!("{logs}");
|
||||
|
|
51
packages/dns-test/src/trust_anchor.rs
Normal file
51
packages/dns-test/src/trust_anchor.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use core::fmt;
|
||||
|
||||
use crate::zone_file::DNSKEY;
|
||||
|
||||
pub struct TrustAnchor {
|
||||
keys: Vec<DNSKEY>,
|
||||
}
|
||||
|
||||
impl TrustAnchor {
|
||||
pub fn empty() -> Self {
|
||||
Self { keys: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.keys.is_empty()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, key: DNSKEY) -> &mut Self {
|
||||
self.keys.push(key);
|
||||
self
|
||||
}
|
||||
|
||||
/// formats the `TrustAnchor` in the format `delv` expects
|
||||
pub(super) fn delv(&self) -> String {
|
||||
let mut buf = "trust-anchors {".to_string();
|
||||
|
||||
for key in &self.keys {
|
||||
buf.push_str(&key.delv());
|
||||
}
|
||||
|
||||
buf.push_str("};");
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TrustAnchor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for key in &self.keys {
|
||||
writeln!(f, "{key}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<DNSKEY> for TrustAnchor {
|
||||
fn from_iter<T: IntoIterator<Item = DNSKEY>>(iter: T) -> Self {
|
||||
Self {
|
||||
keys: iter.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -165,6 +165,20 @@ impl DNSKEY {
|
|||
pub fn key_tag(&self) -> u16 {
|
||||
self.key_tag
|
||||
}
|
||||
|
||||
/// formats the `DNSKEY` in the format `delv` expects
|
||||
pub(super) fn delv(&self) -> String {
|
||||
let Self {
|
||||
zone,
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key,
|
||||
..
|
||||
} = self;
|
||||
|
||||
format!("{zone} static-key {flags} {protocol} {algorithm} \"{public_key}\";\n")
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DNSKEY {
|
||||
|
|
Loading…
Reference in New Issue
Block a user