]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add support for rotating certificates and keys
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 28 Jun 2018 16:48:07 +0000 (18:48 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 28 Jun 2018 16:48:07 +0000 (18:48 +0200)
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/reference/config.rst

index b94be3ebe041b8f918d6ccb4f2bc1cc8f8379856..ab772ea7885beac6254008bdea3519de6dd17816 100644 (file)
@@ -362,6 +362,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "getServer", true, "n", "returns server with index n" },
   { "getServers", true, "", "returns a table with all defined servers" },
   { "getTLSContext", true, "n", "returns the TLS context with index n" },
+  { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
   { "inClientStartup", true, "", "returns true during console client parsing of configuration" },
   { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
   { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
index efa4f105fccfb002a7d7cedf0c7a943e6e80771a..54bf579f3b51a170fd8464fe71cf077b9bb25da0 100644 (file)
@@ -106,6 +106,41 @@ static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, b
   }
 }
 
+#ifdef HAVE_DNS_OVER_TLS
+static bool loadTLSCertificateAndKeys(shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)
+{
+  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.clear();
+    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()) {
+      frontend->d_certKeyPairs.clear();
+      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 false;
+    }
+  }
+  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 false;
+  }
+
+  return true;
+}
+#endif /* HAVE_DNS_OVER_TLS */
+
 void setupLuaConfig(bool client)
 {
   typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> >, DownstreamState::checkfunc_t > > newserver_t;
@@ -1451,29 +1486,7 @@ void setupLuaConfig(bool client)
         }
         shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
 
-        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()!";
+        if (!loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
           return;
         }
 
@@ -1562,6 +1575,29 @@ void setupLuaConfig(bool client)
         return result;
       });
 
+    g_lua.writeFunction("getTLSFrontend", [](size_t index) {
+        std::shared_ptr<TLSFrontend> result = nullptr;
+#ifdef HAVE_DNS_OVER_TLS
+        setLuaNoSideEffect();
+        try {
+          if (index < g_tlslocals.size()) {
+            result = g_tlslocals.at(index);
+          }
+          else {
+            errlog("Error: trying to get TLS frontend with index %zu but we only have %zu\n", index, g_tlslocals.size());
+            g_outputBuffer="Error: trying to get TLS frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + "\n";
+          }
+        }
+        catch(const std::exception& e) {
+          g_outputBuffer="Error: "+string(e.what())+"\n";
+          errlog("Error: %s\n", string(e.what()));
+        }
+#else
+        g_outputBuffer="DNS over TLS support is not present!\n";
+#endif
+        return result;
+      });
+
     g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
         if (ctx != nullptr) {
           ctx->rotateTicketsKey(time(nullptr));
@@ -1573,6 +1609,12 @@ void setupLuaConfig(bool client)
           ctx->loadTicketsKeys(file);
         }
       });
+
+    g_lua.registerFunction<void(std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles) {
+        if (loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
+          frontend->setupTLS();
+        }
+      });
 }
 
 vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
index ab836f8c618a4b7daf3f4fc410367d252d63b607..e0714efd7008771f52be015659374cfffe11aa82 100644 (file)
@@ -616,6 +616,12 @@ Status, Statistics and More
 
   Return the TLSContext object for the context of index ``idx``.
 
+.. function:: getTLSFrontend(idx)
+
+  .. versionadded:: 1.3.1
+
+  Return the TLSFrontend object for the TLS bind of index ``idx``.
+
 .. function:: grepq(selector[, num])
               grepq(selectors[, num])
 
@@ -890,3 +896,19 @@ TLSContext
      Load new tickets keys from the selected file, replacing the existing ones. These keys should be rotated often and never written to persistent storage to preserve forward secrecy. The default is to generate a random key. The OpenSSL provider supports several tickets keys to be able to decrypt existing sessions after the rotation, while the GnuTLS provider only supports one key.
 
     :param str ticketsKeysFile: The path to a file from where TLS tickets keys should be loaded.
+
+TLSFrontend
+~~~~~~~~~~
+
+.. class:: TLSFrontend
+
+  .. versionadded:: 1.3.1
+
+  This object represents the configuration of a listening frontend for DNS over TLS queries. To each frontend is associated a TLSContext.
+
+  .. method:: TLSContext:loadNewCertificatesAndKeys(certFile(s), keyFile(s))
+
+     Create and switch to a new TLS context using the same options than were passed to the corresponding `addTLSLocal()` directive, but loading new certificates and keys from the selected files, replacing the existing ones.
+
+  :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.