From fa974ada509d447ee543f78aa861e07c22818cc8 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Sat, 21 Apr 2018 17:11:05 +0200 Subject: [PATCH] dnsdist: Add support for more than one TLS certificate --- pdns/dnsdist-console.cc | 2 +- pdns/dnsdist-lua.cc | 30 +++++++++++++++++-- pdns/dnsdistdist/docs/guides/dns-over-tls.rst | 6 ++++ pdns/dnsdistdist/docs/reference/config.rst | 8 +++-- pdns/dnsdistdist/tcpiohandler.cc | 28 ++++++++++++----- pdns/dnsdistdist/tcpiohandler.hh | 3 +- 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 735e3c846..be71a5f86 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -301,7 +301,7 @@ const std::vector 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" }, diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index fb0d78378..c6c97c3a5 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -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 vars) { + g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant>> certFiles, boost::variant>> keyFiles, boost::optional vars) { if (client) return; #ifdef HAVE_DNS_OVER_TLS @@ -1441,8 +1441,32 @@ void setupLuaConfig(bool client) return; } shared_ptr frontend = std::make_shared(); - frontend->d_certFile = certFile; - frontend->d_keyFile = keyFile; + + if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) { + auto certFile = boost::get(certFiles); + auto keyFile = boost::get(keyFiles); + frontend->d_certKeyPairs.push_back({certFile, keyFile}); + } + else if (certFiles.type() == typeid(std::vector>) && keyFiles.type() == typeid(std::vector>)) + { + auto certFilesVect = boost::get>>(certFiles); + auto keyFilesVect = boost::get>>(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; diff --git a/pdns/dnsdistdist/docs/guides/dns-over-tls.rst b/pdns/dnsdistdist/docs/guides/dns-over-tls.rst index 90ab9fab2..7fbfb48d3 100644 --- a/pdns/dnsdistdist/docs/guides/dns-over-tls.rst +++ b/pdns/dnsdistdist/docs/guides/dns-over-tls.rst @@ -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. diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index e552a4f01..878fca371 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -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: diff --git a/pdns/dnsdistdist/tcpiohandler.cc b/pdns/dnsdistdist/tcpiohandler.cc index 6316bf16c..0fbf16c7c 100644 --- a/pdns/dnsdistdist/tcpiohandler.cc +++ b/pdns/dnsdistdist/tcpiohandler.cc @@ -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 diff --git a/pdns/dnsdistdist/tcpiohandler.hh b/pdns/dnsdistdist/tcpiohandler.hh index 4f3780409..2f4a2dfe2 100644 --- a/pdns/dnsdistdist/tcpiohandler.hh +++ b/pdns/dnsdistdist/tcpiohandler.hh @@ -128,9 +128,8 @@ public: } std::set d_cpus; + std::vector> 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; -- 2.40.0