From: Remi Gacogne Date: Fri, 27 Dec 2024 11:58:58 +0000 (+0100) Subject: dnsdist: Handle DNSCrypt in the YAML configuration X-Git-Tag: dnsdist-2.0.0-alpha1~160^2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aabcee7a1901e3e872c2b709860d3e5eb44800c3;p=thirdparty%2Fpdns.git dnsdist: Handle DNSCrypt in the YAML configuration --- diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index 4f91015545..1ed9835171 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -26,6 +26,7 @@ #if defined(HAVE_YAML_CONFIGURATION) #include "base64.hh" #include "dolog.hh" +#include "dnscrypt.hh" #include "dnsdist-actions-factory.hh" #include "dnsdist-backend.hh" #include "dnsdist-cache.hh" @@ -490,12 +491,24 @@ bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool } for (const auto& bind : globalConfig.binds) { - ComboAddress listeningAddress(std::string(bind.listen_address), 53); - updateImmutableConfiguration([&bind, listeningAddress](ImmutableConfiguration& config) { + updateImmutableConfiguration([&bind](ImmutableConfiguration& config) { auto protocol = boost::to_lower_copy(std::string(bind.protocol)); + uint16_t defaultPort = 53; + if (protocol == "dot" || protocol == "doq") { + defaultPort = 853; + } + else if (protocol == "doh" || protocol == "dnscrypt" || protocol == "doh3") { + defaultPort = 443; + } + ComboAddress listeningAddress(std::string(bind.listen_address), defaultPort); auto cpus = getCPUPiningFromStr("binds", std::string(bind.cpus)); for (size_t idx = 0; idx < bind.threads; idx++) { +#if defined(HAVE_DNSCRYPT) + std::shared_ptr dnsCryptContext; +#endif /* defined(HAVE_DNSCRYPT) */ + auto state = std::make_shared(listeningAddress, protocol != "doq" && protocol != "doh3", bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, false); + if (bind.tcp.listen_queue_size > 0) { state->tcpListenQueueSize = bind.tcp.listen_queue_size; } @@ -516,16 +529,29 @@ bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool } } - if (protocol != "do53") { + if (protocol == "dnscrypt") { +#if defined(HAVE_DNSCRYPT) + std::vector certKeys; + for (const auto& pair : bind.dnscrypt.certificates) { + certKeys.push_back({std::string(pair.certificate), std::string(pair.key)}); + } + dnsCryptContext = std::make_shared(std::string(bind.dnscrypt.provider_name), certKeys); + state->dnscryptCtx = dnsCryptContext; +#endif /* defined(HAVE_DNSCRYPT) */ + } + else if (protocol != "do53") { if (!handleTLSConfiguration(bind, *state)) { continue; } } config.d_frontends.emplace_back(std::move(state)); - if (protocol == "do53") { + if (protocol == "do53" || protocol == "dnscrypt") { /* also create the UDP listener */ - state = std::make_shared(ComboAddress(std::string(bind.listen_address), 53), false, bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, false); + state = std::make_shared(ComboAddress(std::string(bind.listen_address), defaultPort), false, bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, false); +#if defined(HAVE_DNSCRYPT) + state->dnscryptCtx = dnsCryptContext; +#endif /* defined(HAVE_DNSCRYPT) */ config.d_frontends.emplace_back(std::move(state)); } } diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs index c48b935ab4..ac9a1845e4 100644 --- a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs +++ b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs @@ -1575,6 +1575,22 @@ mod dnsdistsettings { internal_pipe_buffer_size: u32, } + #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[serde(deny_unknown_fields)] + struct IncomingDnscryptCertificateKeyPairConfiguration { + certificate: String, + key: String, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[serde(deny_unknown_fields)] + struct IncomingDnscryptConfiguration { + #[serde(rename = "provider-name", default, skip_serializing_if = "crate::is_default")] + provider_name: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + certificates: Vec, + } + #[derive(Deserialize, Serialize, Debug, PartialEq)] #[serde(deny_unknown_fields)] struct OutgoingDohConfiguration { @@ -1624,6 +1640,8 @@ mod dnsdistsettings { doq: IncomingDoqConfiguration, #[serde(default, skip_serializing_if = "crate::is_default")] quic: IncomingQuicConfiguration, + #[serde(default, skip_serializing_if = "crate::is_default")] + dnscrypt: IncomingDnscryptConfiguration, #[serde(rename = "additional-addresses", default, skip_serializing_if = "crate::is_default")] additional_addresses: Vec, } @@ -2768,6 +2786,22 @@ impl Default for dnsdistsettings::IncomingQuicConfiguration { } +impl Default for dnsdistsettings::IncomingDnscryptCertificateKeyPairConfiguration { + fn default() -> Self { + let deserialized: dnsdistsettings::IncomingDnscryptCertificateKeyPairConfiguration = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + +impl Default for dnsdistsettings::IncomingDnscryptConfiguration { + fn default() -> Self { + let deserialized: dnsdistsettings::IncomingDnscryptConfiguration = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + // DEFAULT HANDLING for outgoing_doh_path fn default_value_outgoing_doh_path() -> String { String::from("/dns-query") @@ -3288,6 +3322,19 @@ impl dnsdistsettings::IncomingQuicConfiguration { Ok(()) } } +impl dnsdistsettings::IncomingDnscryptCertificateKeyPairConfiguration { + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } +} +impl dnsdistsettings::IncomingDnscryptConfiguration { + fn validate(&self) -> Result<(), ValidationError> { + for sub_type in &self.certificates { + sub_type.validate()?; + } + Ok(()) + } +} impl dnsdistsettings::OutgoingDohConfiguration { fn validate(&self) -> Result<(), ValidationError> { Ok(()) @@ -3305,6 +3352,7 @@ impl dnsdistsettings::BindConfiguration { self.doh.validate()?; self.doq.validate()?; self.quic.validate()?; + self.dnscrypt.validate()?; Ok(()) } } diff --git a/pdns/dnsdistdist/dnsdist-settings-definitions.yml b/pdns/dnsdistdist/dnsdist-settings-definitions.yml index c321435a68..1e42374d39 100644 --- a/pdns/dnsdistdist/dnsdist-settings-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-settings-definitions.yml @@ -731,6 +731,22 @@ incoming-quic: type: "u32" default: 1048576 +incoming-dnscrypt-certificate-key-pair: + parameters: + - name: "certificate" + type: "String" + - name: "key" + type: "String" + +incoming-dnscrypt: + parameters: + - name: "provider-name" + type: "String" + default: "" + - name: "certificates" + type: "Vec" + default: true + outgoing-doh: parameters: - name: "path" @@ -793,6 +809,9 @@ bind: - name: "quic" type: "IncomingQuicConfiguration" default: true + - name: "dnscrypt" + type: "IncomingDnscryptConfiguration" + default: true - name: "additional-addresses" type: "Vec" default: true diff --git a/pdns/dnsdistdist/docs/reference/yaml-settings.rst b/pdns/dnsdistdist/docs/reference/yaml-settings.rst index 3878dc1412..b887c5e8a3 100644 --- a/pdns/dnsdistdist/docs/reference/yaml-settings.rst +++ b/pdns/dnsdistdist/docs/reference/yaml-settings.rst @@ -106,6 +106,7 @@ BindConfiguration - **doh**: :ref:`IncomingDohConfiguration ` - **doq**: :ref:`IncomingDoqConfiguration ` - **quic**: :ref:`IncomingQuicConfiguration ` +- **dnscrypt**: :ref:`IncomingDnscryptConfiguration ` - **additional-addresses**: Sequence of String @@ -305,6 +306,24 @@ HttpResponsesMapConfiguration - **headers**: Sequence of :ref:`HttpCustomResponseHeaderConfiguration ` +.. _yaml-settings-IncomingDnscryptCertificateKeyPairConfiguration: + +IncomingDnscryptCertificateKeyPairConfiguration +----------------------------------------------- + +- **certificate**: String +- **key**: String + + +.. _yaml-settings-IncomingDnscryptConfiguration: + +IncomingDnscryptConfiguration +----------------------------- + +- **provider-name**: String ``("")`` +- **certificates**: Sequence of :ref:`IncomingDnscryptCertificateKeyPairConfiguration ` + + .. _yaml-settings-IncomingDohConfiguration: IncomingDohConfiguration diff --git a/regression-tests.dnsdist/test_DNSCrypt.py b/regression-tests.dnsdist/test_DNSCrypt.py index c5e150e8d9..0d8daaff0c 100644 --- a/regression-tests.dnsdist/test_DNSCrypt.py +++ b/regression-tests.dnsdist/test_DNSCrypt.py @@ -265,6 +265,56 @@ class TestDNSCrypt(DNSCryptTest): self.doDNSCryptQuery(client, query, response, True) +class TestDNSCryptYaml(TestDNSCrypt): + _config_template = """ + function checkDNSCryptUDP(dq) + if dq:getProtocol() ~= "DNSCrypt UDP" then + return DNSAction.Spoof, '1.2.3.4' + end + return DNSAction.None + end + + function checkDNSCryptTCP(dq) + if dq:getProtocol() ~= "DNSCrypt TCP" then + return DNSAction.Spoof, '1.2.3.4' + end + return DNSAction.None + end +""" + _yaml_config_template = """ +console: + key: "%s" + listen-address: "127.0.0.1:%d" + acl: + - 127.0.0.0/8 +binds: + - listen-address: "127.0.0.1:%d" + protocol: "DNSCrypt" + dnscrypt: + provider-name: "%s" + certificates: + - certificate: "DNSCryptResolver.cert" + key: "DNSCryptResolver.key" +backends: + - address: "127.0.0.1:%d" + protocol: Do53 +query-rules: + - selector: + type: "QName" + qname: "udp.protocols.dnscrypt.tests.powerdns.com." + action: + type: "Lua" + function: "checkDNSCryptUDP" + - selector: + type: "QName" + qname: "tcp.protocols.dnscrypt.tests.powerdns.com." + action: + type: "Lua" + function: "checkDNSCryptTCP" +""" + _config_params = [] + _yaml_config_params = ['_consoleKeyB64', '_consolePort', '_dnsDistPortDNSCrypt', '_providerName', '_testServerPort'] + class TestDNSCryptWithCache(DNSCryptTest): _config_params = ['_resolverCertificateSerial', '_resolverCertificateValidFrom', '_resolverCertificateValidUntil', '_dnsDistPortDNSCrypt', '_providerName', '_testServerPort']