]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Handle DNSCrypt in the YAML configuration
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 27 Dec 2024 11:58:58 +0000 (12:58 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 Jan 2025 08:50:33 +0000 (09:50 +0100)
pdns/dnsdistdist/dnsdist-configuration-yaml.cc
pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs
pdns/dnsdistdist/dnsdist-settings-definitions.yml
pdns/dnsdistdist/docs/reference/yaml-settings.rst
regression-tests.dnsdist/test_DNSCrypt.py

index 4f91015545a41c606db0ec9342d627278e58a08e..1ed9835171e2a1a0b65c55f8d98adb93de87e44e 100644 (file)
@@ -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> dnsCryptContext;
+#endif /* defined(HAVE_DNSCRYPT) */
+
           auto state = std::make_shared<ClientState>(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<DNSCryptContext::CertKeyPaths> certKeys;
+            for (const auto& pair : bind.dnscrypt.certificates) {
+              certKeys.push_back({std::string(pair.certificate), std::string(pair.key)});
+            }
+            dnsCryptContext = std::make_shared<DNSCryptContext>(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<ClientState>(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<ClientState>(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));
           }
         }
index c48b935ab4244fe40e56a5038854aabc9333736c..ac9a1845e4c3a62502dcb454dbc179d70b16f9b4 100644 (file)
@@ -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<IncomingDnscryptCertificateKeyPairConfiguration>,
+    }
+
     #[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<String>,
     }
@@ -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(())
     }
 }
index c321435a686e7f0f0328498eee64eac09e3a5652..1e42374d39acf0b692a8e118b01be70950b3cc89 100644 (file)
@@ -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<IncomingDnscryptCertificateKeyPairConfiguration>"
+      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<String>"
       default: true
index 3878dc14120555595f5326536193f27ad799cef6..b887c5e8a3b858675f81a3a17a2b665b555aa86a 100644 (file)
@@ -106,6 +106,7 @@ BindConfiguration
 - **doh**: :ref:`IncomingDohConfiguration <yaml-settings-IncomingDohConfiguration>`
 - **doq**: :ref:`IncomingDoqConfiguration <yaml-settings-IncomingDoqConfiguration>`
 - **quic**: :ref:`IncomingQuicConfiguration <yaml-settings-IncomingQuicConfiguration>`
+- **dnscrypt**: :ref:`IncomingDnscryptConfiguration <yaml-settings-IncomingDnscryptConfiguration>`
 - **additional-addresses**: Sequence of String
 
 
@@ -305,6 +306,24 @@ HttpResponsesMapConfiguration
 - **headers**: Sequence of :ref:`HttpCustomResponseHeaderConfiguration <yaml-settings-HttpCustomResponseHeaderConfiguration>`
 
 
+.. _yaml-settings-IncomingDnscryptCertificateKeyPairConfiguration:
+
+IncomingDnscryptCertificateKeyPairConfiguration
+-----------------------------------------------
+
+- **certificate**: String
+- **key**: String
+
+
+.. _yaml-settings-IncomingDnscryptConfiguration:
+
+IncomingDnscryptConfiguration
+-----------------------------
+
+- **provider-name**: String ``("")``
+- **certificates**: Sequence of :ref:`IncomingDnscryptCertificateKeyPairConfiguration <yaml-settings-IncomingDnscryptCertificateKeyPairConfiguration>`
+
+
 .. _yaml-settings-IncomingDohConfiguration:
 
 IncomingDohConfiguration
index c5e150e8d9b25dcc5b57e2d027c3e69647da6fd9..0d8daaff0c36b1d5595644ac80b6e2fbb3d32aa1 100644 (file)
@@ -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']