From 820f1c3447a798dcf3086a6fa942a8952a289ec2 Mon Sep 17 00:00:00 2001 From: Sebastian Ziebell Date: Tue, 13 Feb 2024 11:25:52 +0100 Subject: [PATCH] Pass in Network to containers --- .../src/resolver/dns/scenarios.rs | 24 +++++++------- .../src/resolver/dnssec/scenarios.rs | 20 ++++++----- packages/dns-test/src/client.rs | 6 ++-- packages/dns-test/src/container.rs | 13 +++++--- packages/dns-test/src/container/network.rs | 33 ++++++++++++------- packages/dns-test/src/lib.rs | 1 + packages/dns-test/src/name_server.rs | 24 ++++++++------ packages/dns-test/src/resolver.rs | 10 +++--- 8 files changed, 79 insertions(+), 52 deletions(-) diff --git a/packages/conformance-tests/src/resolver/dns/scenarios.rs b/packages/conformance-tests/src/resolver/dns/scenarios.rs index a25d3bce..9dcdcbd2 100644 --- a/packages/conformance-tests/src/resolver/dns/scenarios.rs +++ b/packages/conformance-tests/src/resolver/dns/scenarios.rs @@ -4,17 +4,18 @@ 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::{Resolver, Result, TrustAnchor, FQDN}; +use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN}; #[test] fn can_resolve() -> Result<()> { let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4); let needle_fqdn = FQDN("example.nameservers.com.")?; - let mut root_ns = NameServer::new(FQDN::ROOT)?; - let mut com_ns = NameServer::new(FQDN::COM)?; + let network = Network::new()?; + let mut root_ns = NameServer::new(FQDN::ROOT, &network)?; + let mut com_ns = NameServer::new(FQDN::COM, &network)?; - let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?)?; + let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?; nameservers_ns .a(root_ns.fqdn().clone(), root_ns.ipv4_addr()) .a(com_ns.fqdn().clone(), com_ns.ipv4_addr()) @@ -38,10 +39,10 @@ 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 = Resolver::start(dns_test::subject(), roots, &TrustAnchor::empty())?; + let resolver = Resolver::start(dns_test::subject(), roots, &TrustAnchor::empty(), &network)?; let resolver_ip_addr = resolver.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::Yes, Dnssec::No, @@ -66,10 +67,11 @@ fn can_resolve() -> Result<()> { fn nxdomain() -> Result<()> { let needle_fqdn = FQDN("unicorn.nameservers.com.")?; - let mut root_ns = NameServer::new(FQDN::ROOT)?; - let mut com_ns = NameServer::new(FQDN::COM)?; + let network = Network::new()?; + let mut root_ns = NameServer::new(FQDN::ROOT, &network)?; + let mut com_ns = NameServer::new(FQDN::COM, &network)?; - let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?)?; + let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?; nameservers_ns .a(root_ns.fqdn().clone(), root_ns.ipv4_addr()) .a(com_ns.fqdn().clone(), com_ns.ipv4_addr()); @@ -86,10 +88,10 @@ fn nxdomain() -> Result<()> { let root_ns = root_ns.start()?; let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())]; - let resolver = Resolver::start(dns_test::subject(), roots, &TrustAnchor::empty())?; + let resolver = Resolver::start(dns_test::subject(), roots, &TrustAnchor::empty(), &network)?; let resolver_ip_addr = resolver.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::Yes, Dnssec::No, diff --git a/packages/conformance-tests/src/resolver/dnssec/scenarios.rs b/packages/conformance-tests/src/resolver/dnssec/scenarios.rs index 31c5df7b..1300d9b4 100644 --- a/packages/conformance-tests/src/resolver/dnssec/scenarios.rs +++ b/packages/conformance-tests/src/resolver/dnssec/scenarios.rs @@ -4,13 +4,14 @@ 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::{Resolver, Result, TrustAnchor, FQDN}; +use dns_test::{Network, Resolver, Result, TrustAnchor, FQDN}; // no DS records are involved; this is a single-link chain of trust #[ignore] #[test] fn can_validate_without_delegation() -> Result<()> { - let mut ns = NameServer::new(FQDN::ROOT)?; + let network = Network::new()?; + let mut ns = NameServer::new(FQDN::ROOT, &network)?; ns.a(ns.fqdn().clone(), ns.ipv4_addr()); let ns = ns.sign()?; @@ -26,10 +27,10 @@ fn can_validate_without_delegation() -> Result<()> { let roots = &[Root::new(ns.fqdn().clone(), ns.ipv4_addr())]; let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]); - let resolver = Resolver::start(dns_test::subject(), roots, &trust_anchor)?; + let resolver = Resolver::start(dns_test::subject(), roots, &trust_anchor, &network)?; let resolver_addr = resolver.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::Yes, Dnssec::Yes, @@ -53,10 +54,11 @@ fn can_validate_with_delegation() -> Result<()> { let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4); let needle_fqdn = FQDN("example.nameservers.com.")?; - let mut root_ns = NameServer::new(FQDN::ROOT)?; - let mut com_ns = NameServer::new(FQDN::COM)?; + let network = Network::new()?; + let mut root_ns = NameServer::new(FQDN::ROOT, &network)?; + let mut com_ns = NameServer::new(FQDN::COM, &network)?; - let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?)?; + let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, &network)?; nameservers_ns .a(root_ns.fqdn().clone(), root_ns.ipv4_addr()) .a(com_ns.fqdn().clone(), com_ns.ipv4_addr()) @@ -96,10 +98,10 @@ fn can_validate_with_delegation() -> Result<()> { let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())]; let trust_anchor = TrustAnchor::from_iter([root_ksk.clone(), root_zsk.clone()]); - let resolver = Resolver::start(dns_test::subject(), roots, &trust_anchor)?; + let resolver = Resolver::start(dns_test::subject(), roots, &trust_anchor, &network)?; let resolver_addr = resolver.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::Yes, Dnssec::Yes, diff --git a/packages/dns-test/src/client.rs b/packages/dns-test/src/client.rs index 5cce1e4e..bfc57b7b 100644 --- a/packages/dns-test/src/client.rs +++ b/packages/dns-test/src/client.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use std::net::Ipv4Addr; -use crate::container::Container; +use crate::container::{Container, Network}; use crate::record::{Record, RecordType}; use crate::trust_anchor::TrustAnchor; use crate::{Error, Implementation, Result, FQDN}; @@ -11,9 +11,9 @@ pub struct Client { } impl Client { - pub fn new() -> Result { + pub fn new(network: &Network) -> Result { Ok(Self { - inner: Container::run(Implementation::Unbound)?, + inner: Container::run(Implementation::Unbound, network)?, }) } diff --git a/packages/dns-test/src/container.rs b/packages/dns-test/src/container.rs index 61bf0182..21d621ee 100644 --- a/packages/dns-test/src/container.rs +++ b/packages/dns-test/src/container.rs @@ -12,6 +12,8 @@ use tempfile::{NamedTempFile, TempDir}; use crate::{Error, Implementation, Result}; +pub use crate::container::network::Network; + pub struct Container { inner: Arc, } @@ -20,7 +22,7 @@ const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME"); impl Container { /// Starts the container in a "parked" state - pub fn run(implementation: Implementation) -> Result { + pub fn run(implementation: Implementation, network: &Network) -> Result { // TODO make this configurable and support hickory & bind let dockerfile = implementation.dockerfile(); let docker_build_dir = TempDir::new()?; @@ -269,7 +271,8 @@ mod tests { #[test] fn run_works() -> Result<()> { - let container = Container::run(Implementation::Unbound)?; + let network = Network::new()?; + let container = Container::run(Implementation::Unbound, &network)?; let output = container.output(&["true"])?; assert!(output.status.success()); @@ -279,7 +282,8 @@ mod tests { #[test] fn ipv4_addr_works() -> Result<()> { - let container = Container::run(Implementation::Unbound)?; + let network = Network::new()?; + let container = Container::run(Implementation::Unbound, &network)?; let ipv4_addr = container.ipv4_addr(); let output = container.output(&["ping", "-c1", &format!("{ipv4_addr}")])?; @@ -290,7 +294,8 @@ mod tests { #[test] fn cp_works() -> Result<()> { - let container = Container::run(Implementation::Unbound)?; + let network = Network::new()?; + let container = Container::run(Implementation::Unbound, &network)?; let path = "/tmp/somefile"; let contents = "hello"; diff --git a/packages/dns-test/src/container/network.rs b/packages/dns-test/src/container/network.rs index fdbe5ee7..7d7036ab 100644 --- a/packages/dns-test/src/container/network.rs +++ b/packages/dns-test/src/container/network.rs @@ -1,5 +1,5 @@ use std::{ - process::{Command, Stdio}, + process::{Command, ExitStatus, Stdio}, sync::atomic::{self, AtomicUsize}, }; @@ -10,6 +10,7 @@ const NETWORK_NAME: &str = "dnssec-network"; /// Represents a network in which to put containers into. pub struct Network { name: String, + config: NetworkConfig, } impl Network { @@ -17,6 +18,9 @@ impl Network { let id = network_count(); let network_name = format!("{NETWORK_NAME}-{id}"); + // A network can exist, for example when a test panics + let _ = remove_network(network_name.as_str())?; + let mut command = Command::new("docker"); command .args(["network", "create"]) @@ -33,8 +37,12 @@ impl Network { ); // inspect & parse network details + let config = get_network_config(&network_name)?; - Ok(Self { name: network_name }) + Ok(Self { + name: network_name, + config, + }) } /// Returns the name of the network. @@ -49,7 +57,7 @@ pub struct NetworkConfig { subnet: String, } -/// +/// Return network config fn get_network_config(network_name: &str) -> Result { let mut command = Command::new("docker"); command @@ -70,19 +78,22 @@ fn get_network_config(network_name: &str) -> Result { Ok(NetworkConfig { subnet }) } -/// This ensure the Docket network is deleted after the test runner process ends. +/// This ensure the Docker network is deleted after the test runner process ends. impl Drop for Network { fn drop(&mut self) { - // Remove the network - // TODO check if all containers need to disconnect first - let _ = Command::new("docker") - .args(["network", "rm", "--force", self.name.as_str()]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status(); + let _ = remove_network(&self.name); } } +fn remove_network(network_name: &str) -> Result { + let mut command = Command::new("docker"); + command + .args(["network", "rm", "--force", network_name]) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + Ok(command.status()?) +} + fn network_count() -> usize { static COUNT: AtomicUsize = AtomicUsize::new(1); diff --git a/packages/dns-test/src/lib.rs b/packages/dns-test/src/lib.rs index c88e1209..18a625d7 100644 --- a/packages/dns-test/src/lib.rs +++ b/packages/dns-test/src/lib.rs @@ -3,6 +3,7 @@ use core::fmt; use std::sync::Once; +pub use crate::container::Network; pub use crate::fqdn::FQDN; pub use crate::resolver::Resolver; pub use crate::trust_anchor::TrustAnchor; diff --git a/packages/dns-test/src/name_server.rs b/packages/dns-test/src/name_server.rs index 5de5a2e9..42d0919c 100644 --- a/packages/dns-test/src/name_server.rs +++ b/packages/dns-test/src/name_server.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{self, AtomicUsize}; use std::net::Ipv4Addr; -use crate::container::{Child, Container}; +use crate::container::{Child, Container, Network}; use crate::zone_file::{self, SoaSettings, ZoneFile, DNSKEY, DS}; use crate::{Implementation, Result, FQDN}; @@ -24,7 +24,7 @@ impl<'a> NameServer<'a, Stopped> { /// - one SOA record, with the primary name server field set to this name server's FQDN /// - one NS record, with this name server's FQDN set as the only available name server for /// the zone - pub fn new(zone: FQDN<'a>) -> Result { + pub fn new(zone: FQDN<'a>, network: &Network) -> Result { let ns_count = ns_count(); let nameserver = primary_ns(ns_count); @@ -42,7 +42,7 @@ impl<'a> NameServer<'a, Stopped> { }); Ok(Self { - container: Container::run(Implementation::Unbound)?, + container: Container::run(Implementation::Unbound, network)?, zone_file, state: Stopped, }) @@ -290,10 +290,11 @@ mod tests { #[test] fn simplest() -> Result<()> { - let tld_ns = NameServer::new(FQDN::COM)?.start()?; + let network = Network::new()?; + let tld_ns = NameServer::new(FQDN::COM, &network)?.start()?; let ip_addr = tld_ns.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::No, Dnssec::No, @@ -309,8 +310,9 @@ mod tests { #[test] fn with_referral() -> Result<()> { + let network = Network::new()?; let expected_ip_addr = Ipv4Addr::new(172, 17, 200, 1); - let mut root_ns = NameServer::new(FQDN::ROOT)?; + let mut root_ns = NameServer::new(FQDN::ROOT, &network)?; root_ns.referral( FQDN::COM, FQDN("primary.tld-server.com.")?, @@ -322,7 +324,7 @@ mod tests { let ipv4_addr = root_ns.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::No, Dnssec::No, @@ -338,7 +340,8 @@ mod tests { #[test] fn signed() -> Result<()> { - let ns = NameServer::new(FQDN::ROOT)?.sign()?; + let network = Network::new()?; + let ns = NameServer::new(FQDN::ROOT, &network)?.sign()?; eprintln!("KSK:\n{}", ns.key_signing_key()); eprintln!("ZSK:\n{}", ns.zone_signing_key()); @@ -348,7 +351,7 @@ mod tests { let ns_addr = tld_ns.ipv4_addr(); - let client = Client::new()?; + let client = Client::new(&network)?; let output = client.dig( Recurse::No, Dnssec::Yes, @@ -373,7 +376,8 @@ mod tests { #[test] fn terminate_works() -> Result<()> { - let ns = NameServer::new(FQDN::ROOT)?.start()?; + let network = Network::new()?; + let ns = NameServer::new(FQDN::ROOT, &network)?.start()?; let logs = ns.terminate()?; assert!(logs.contains("nsd starting")); diff --git a/packages/dns-test/src/resolver.rs b/packages/dns-test/src/resolver.rs index 3a084f7a..1e15adeb 100644 --- a/packages/dns-test/src/resolver.rs +++ b/packages/dns-test/src/resolver.rs @@ -1,7 +1,7 @@ use core::fmt::Write; use std::net::Ipv4Addr; -use crate::container::{Child, Container}; +use crate::container::{Child, Container, Network}; use crate::trust_anchor::TrustAnchor; use crate::zone_file::Root; use crate::{Implementation, Result}; @@ -23,6 +23,7 @@ impl Resolver { implementation: Implementation, roots: &[Root], trust_anchor: &TrustAnchor, + network: &Network, ) -> Result { const TRUST_ANCHOR_FILE: &str = "/etc/trusted-key.key"; @@ -31,7 +32,7 @@ impl Resolver { "must configure at least one local root server" ); - let container = Container::run(implementation)?; + let container = Container::run(implementation, network)?; let mut hints = String::new(); for root in roots { @@ -110,12 +111,13 @@ mod tests { #[test] fn terminate_works() -> Result<()> { - let ns = NameServer::new(FQDN::ROOT)?.start()?; - + let network = Network::new()?; + let ns = NameServer::new(FQDN::ROOT, &network)?.start()?; let resolver = Resolver::start( Implementation::Unbound, &[Root::new(ns.fqdn().clone(), ns.ipv4_addr())], &TrustAnchor::empty(), + &network, )?; let logs = resolver.terminate()?;