]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add support for more than one TLS certificate
authorRemi Gacogne <remi.gacogne@powerdns.com>
Sat, 21 Apr 2018 15:11:05 +0000 (17:11 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Sat, 21 Apr 2018 16:08:25 +0000 (18:08 +0200)
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/guides/dns-over-tls.rst
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/tcpiohandler.cc
pdns/dnsdistdist/tcpiohandler.hh

index 735e3c846739d4ba72c3a35305d1e573598d2b4f..be71a5f869b999316064409af69137193642c149 100644 (file)
@@ -301,7 +301,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
   { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
   { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a self-answered response rule" },
-  { "addTLSLocal", true, "addr, certFile, keyFile[,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate and key. The last parameter is a table" },
+  { "addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table" },
   { "AllowAction", true, "", "let these packets go through" },
   { "AllowResponseAction", true, "", "let these packets go through" },
   { "AllRule", true, "", "matches all traffic" },
index fb0d78378bead25d76e5c89c7b3b0e28ca1c07d5..c6c97c3a58e06e39931620380958458b503d73ae 100644 (file)
@@ -1431,7 +1431,7 @@ void setupLuaConfig(bool client)
 #endif
     });
 
-    g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, const std::string& certFile, const std::string& keyFile, boost::optional<localbind_t> vars) {
+  g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles, boost::optional<localbind_t> vars) {
         if (client)
           return;
 #ifdef HAVE_DNS_OVER_TLS
@@ -1441,8 +1441,32 @@ void setupLuaConfig(bool client)
           return;
         }
         shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
-        frontend->d_certFile = certFile;
-        frontend->d_keyFile = keyFile;
+
+        if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
+          auto certFile = boost::get<std::string>(certFiles);
+          auto keyFile = boost::get<std::string>(keyFiles);
+          frontend->d_certKeyPairs.push_back({certFile, keyFile});
+        }
+        else if (certFiles.type() == typeid(std::vector<std::pair<int,std::string>>) && keyFiles.type() == typeid(std::vector<std::pair<int,std::string>>))
+        {
+          auto certFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(certFiles);
+          auto keyFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(keyFiles);
+          if (certFilesVect.size() == keyFilesVect.size()) {
+            for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
+              frontend->d_certKeyPairs.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second});
+            }
+          }
+          else {
+            errlog("Error, mismatching number of certificates and keys in call to addTLSLocal()!");
+            g_outputBuffer="Error, mismatching number of certificates and keys in call to addTLSLocal()!";
+            return;
+          }
+        }
+        else {
+          errlog("Error, mismatching number of certificates and keys in call to addTLSLocal()!");
+          g_outputBuffer="Error, mismatching number of certificates and keys in call to addTLSLocal()!";
+          return;
+        }
 
         if (vars) {
           bool doTCP = true;
index 90ab9fab258556c7f700f582bc2784ec69cb4155..7fbfb48d3a606b3727abff780efd2b04983a584f 100644 (file)
@@ -10,3 +10,9 @@ Adding a listen port for DNS-over-TLS can be done with the :func:`addTLSLocal` f
   addTLSLocal('192.0.2.55', '/etc/ssl/certs/example.com.pem', '/etc/ssl/private/example.com.key')
 
 This will make :program:`dnsdist` listen on 192.0.2.55:853 on TCP and UDP and will use the provided certificate and key to provide the TLS connection.
+
+In order to support multiple certificates and keys, for example an ECDSA and an RSA one, the following syntax may be used instead::
+
+  addTLSLocal('192.0.2.55', {'/etc/ssl/certs/example.com.rsa.pem', '/etc/ssl/certs/example.com.ecdsa.pem'}, {'/etc/ssl/private/example.com.rsa.key', '/etc/ssl/private/example.com.ecdsa.key'})
+
+The certificate chain to present will then be selected based on the algorithms advertised by the client.
index e552a4f01642b8af4b6a6a937b0dc10ee73bd5d0..878fca3716b3fce612a3da66e7a0f48515ad07c8 100644 (file)
@@ -86,16 +86,18 @@ Listen Sockets
                                   higher than 0 to enable TCP Fast Open when available.
                                   Default is 0.
 
-.. function:: addTLSLocal(address, certFile, keyFile[, options])
+.. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options])
 
   .. versionadded:: 1.3.0
+  .. versionchanged:: 1.3.1
+    ``certFile(s)`` and ``keyFile(s)`` parameters accept a list of files.
 
   Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
 
   :param str address: The IP Address with an optional port to listen on.
                       The default port is 853.
-  :param str certFile: The path to a X.509 certificate file in PEM format.
-  :param str keyFile: The path to the private key file corresponding to the certificate.
+  :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
+  :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
   :param table options: A table with key: value pairs with listen options.
 
   Options:
index 6316bf16c2b76c5f54728fbc06705399d52acc90..0fbf16c7c632adc2d28ba54251b8ae7752585dce 100644 (file)
@@ -399,11 +399,23 @@ public:
 #if defined(SSL_CTX_set_ecdh_auto)
     SSL_CTX_set_ecdh_auto(d_tlsCtx, 1);
 #endif
-    SSL_CTX_use_certificate_chain_file(d_tlsCtx, fe.d_certFile.c_str());
-    SSL_CTX_use_PrivateKey_file(d_tlsCtx, fe.d_keyFile.c_str(), SSL_FILETYPE_PEM);
+
+    for (const auto& pair : fe.d_certKeyPairs) {
+      if (SSL_CTX_use_certificate_chain_file(d_tlsCtx, pair.first.c_str()) != 1) {
+        ERR_print_errors_fp(stderr);
+        throw std::runtime_error("Error loading certificate from " + pair.first + " for the TLS context on " + fe.d_addr.toStringWithPort());
+      }
+      if (SSL_CTX_use_PrivateKey_file(d_tlsCtx, pair.second.c_str(), SSL_FILETYPE_PEM) != 1) {
+        ERR_print_errors_fp(stderr);
+        throw std::runtime_error("Error loading key from " + pair.second + " for the TLS context on " + fe.d_addr.toStringWithPort());
+      }
+    }
 
     if (!fe.d_ciphers.empty()) {
-      SSL_CTX_set_cipher_list(d_tlsCtx, fe.d_ciphers.c_str());
+      if (SSL_CTX_set_cipher_list(d_tlsCtx, fe.d_ciphers.c_str()) != 1) {
+        ERR_print_errors_fp(stderr);
+        throw std::runtime_error("Error setting the cipher list to '" + fe.d_ciphers + "' for the TLS context on " + fe.d_addr.toStringWithPort());
+      }
     }
 
     try {
@@ -772,10 +784,12 @@ public:
       throw std::runtime_error("Error allocating credentials for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
     }
 
-    rc = gnutls_certificate_set_x509_key_file(d_creds, fe.d_certFile.c_str(), fe.d_keyFile.c_str(), GNUTLS_X509_FMT_PEM);
-    if (rc != GNUTLS_E_SUCCESS) {
-      gnutls_certificate_free_credentials(d_creds);
-      throw std::runtime_error("Error loading certificate and key for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+    for (const auto& pair : fe.d_certKeyPairs) {
+      rc = gnutls_certificate_set_x509_key_file(d_creds, pair.first.c_str(), pair.second.c_str(), GNUTLS_X509_FMT_PEM);
+      if (rc != GNUTLS_E_SUCCESS) {
+        gnutls_certificate_free_credentials(d_creds);
+        throw std::runtime_error("Error loading certificate ('" + pair.first + "') and key ('" + pair.second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+      }
     }
 
 #if GNUTLS_VERSION_NUMBER >= 0x030600
index 4f3780409675eb7919a72fc15dc48820f229431c..2f4a2dfe2536669e7fb8685a858fd34733bea55b 100644 (file)
@@ -128,9 +128,8 @@ public:
   }
 
   std::set<int> d_cpus;
+  std::vector<std::pair<std::string, std::string>> d_certKeyPairs;
   ComboAddress d_addr;
-  std::string d_certFile;
-  std::string d_keyFile;
   std::string d_ciphers;
   std::string d_provider;
   std::string d_interface;