From: Remi Gacogne Date: Wed, 17 Apr 2019 09:11:42 +0000 (+0200) Subject: dnsdist: Add support for more than one TLS certificate for DoH X-Git-Tag: dnsdist-1.4.0-alpha2~6^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf8cd40da2324019e68b584cfeafba563506ccbf;p=thirdparty%2Fpdns.git dnsdist: Add support for more than one TLS certificate for DoH So we can present an ECDSA one to clients supporting it and a RSA one to those who don't. --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 0897b03f74..723d9e2a16 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -112,40 +112,38 @@ static void parseLocalBindVars(boost::optional vars, bool& doTCP, b } } -#ifdef HAVE_DNS_OVER_TLS -static bool loadTLSCertificateAndKeys(shared_ptr& frontend, boost::variant>> certFiles, boost::variant>> keyFiles) +static bool loadTLSCertificateAndKeys(const std::string& context, std::vector>& pairs, boost::variant>> certFiles, boost::variant>> keyFiles) { if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) { auto certFile = boost::get(certFiles); auto keyFile = boost::get(keyFiles); - frontend->d_certKeyPairs.clear(); - frontend->d_certKeyPairs.push_back({certFile, keyFile}); + pairs.clear(); + pairs.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()) { - frontend->d_certKeyPairs.clear(); + pairs.clear(); for (size_t idx = 0; idx < certFilesVect.size(); idx++) { - frontend->d_certKeyPairs.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second}); + pairs.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()!"; + errlog("Error, mismatching number of certificates and keys in call to %s()!", context); + g_outputBuffer="Error, mismatching number of certificates and keys in call to " + context + "()!"; 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()!"; + errlog("Error, mismatching number of certificates and keys in call to %s()!", context); + g_outputBuffer="Error, mismatching number of certificates and keys in call to " + context + "()!"; return false; } return true; } -#endif /* HAVE_DNS_OVER_TLS */ void setupLuaConfig(bool client) { @@ -1650,7 +1648,7 @@ void setupLuaConfig(bool client) setSyslogFacility(facility); }); - g_lua.writeFunction("addDOHLocal", [client](const std::string& addr, const std::string& certFile, const std::string& keyFile, boost::optional > > urls, boost::optional vars) { + g_lua.writeFunction("addDOHLocal", [client](const std::string& addr, boost::variant>> certFiles, boost::variant>> keyFiles, boost::optional > > urls, boost::optional vars) { if (client) { return; } @@ -1661,8 +1659,11 @@ void setupLuaConfig(bool client) return; } auto frontend = std::make_shared(); - frontend->d_certFile = certFile; - frontend->d_keyFile = keyFile; + + if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_certKeyPairs, certFiles, keyFiles)) { + return; + } + frontend->d_local = ComboAddress(addr, 443); if(urls && !urls->empty()) { for(const auto& p : *urls) { @@ -1765,7 +1766,7 @@ void setupLuaConfig(bool client) } shared_ptr frontend = std::make_shared(); - if (!loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) { + if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_certKeyPairs, certFiles, keyFiles)) { return; } @@ -1915,7 +1916,7 @@ void setupLuaConfig(bool client) g_lua.registerFunction::*)(boost::variant>> certFiles, boost::variant>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr& frontend, boost::variant>> certFiles, boost::variant>> keyFiles) { #ifdef HAVE_DNS_OVER_TLS - if (loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) { + if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend->d_certKeyPairs, certFiles, keyFiles)) { frontend->setupTLS(); } #endif diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 075268b541..0295821ef1 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -108,8 +108,8 @@ Listen Sockets :param str address: The IP Address with an optional port to listen on. The default port is 443. - :param str certFile(s): The path to a X.509 certificate file in PEM format. - :param str keyFile(s): 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 list url: A list of URLs to accept queries on. The default is /. :param table options: A table with key: value pairs with listen options. diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index 0f1247d603..d149dffde5 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -9,6 +9,9 @@ //#include #include +#include +#include + #include "base64.hh" #include "dnsname.hh" #undef CERT @@ -569,7 +572,7 @@ static int create_listener(const ComboAddress& addr, std::shared_ptr getTLSContext(const std::string& cert_file, const std::string& key_file, const std::string& ciphers, const std::string& ciphers13) +static std::unique_ptr getTLSContext(const std::vector>& pairs, const std::string& ciphers, const std::string& ciphers13) { auto ctx = std::unique_ptr(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); @@ -580,11 +583,15 @@ static std::unique_ptr getTLSContext(const std::stri #endif /* load certificate and private key */ - if (SSL_CTX_use_certificate_chain_file(ctx.get(), cert_file.c_str()) != 1) { - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server certificate file: " + cert_file); - } - if (SSL_CTX_use_PrivateKey_file(ctx.get(), key_file.c_str(), SSL_FILETYPE_PEM) != 1) { - throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server private key file: " + key_file); + for (const auto& pair : pairs) { + if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.first.c_str()) != 1) { + ERR_print_errors_fp(stderr); + throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server certificate file: " + pair.first); + } + if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) { + ERR_print_errors_fp(stderr); + throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server private key file: " + pair.second); + } } if (SSL_CTX_set_cipher_list(ctx.get(), ciphers.empty() == false ? ciphers.c_str() : DOH_DEFAULT_CIPHERS) != 1) { @@ -608,7 +615,7 @@ static void setupAcceptContext(DOHAcceptContext& ctx, DOHServerConfig& dsc, bool nativeCtx->ctx = &dsc.h2o_ctx; nativeCtx->hosts = dsc.h2o_config.hosts; if (setupTLS) { - auto tlsCtx = getTLSContext(dsc.df->d_certFile, dsc.df->d_keyFile, + auto tlsCtx = getTLSContext(dsc.df->d_certKeyPairs, dsc.df->d_ciphers, dsc.df->d_ciphers13); @@ -633,7 +640,7 @@ void DOHFrontend::setup() d_dsc = std::make_shared(d_idleTimeout); - auto tlsCtx = getTLSContext(d_certFile, d_keyFile, + auto tlsCtx = getTLSContext(d_certKeyPairs, d_ciphers, d_ciphers13); diff --git a/pdns/doh.hh b/pdns/doh.hh index d075c3933d..519fa20446 100644 --- a/pdns/doh.hh +++ b/pdns/doh.hh @@ -6,8 +6,7 @@ struct DOHServerConfig; struct DOHFrontend { std::shared_ptr d_dsc{nullptr}; - std::string d_certFile; - std::string d_keyFile; + std::vector> d_certKeyPairs; std::string d_ciphers; std::string d_ciphers13; ComboAddress d_local;