From: Remi Gacogne Date: Wed, 2 Feb 2022 14:41:00 +0000 (+0100) Subject: dnsdist: Enable experimental kTLS support with OpenSSL on Linux X-Git-Tag: dnsdist-1.8.0-rc1~23^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5afc196c67a7ff2539fadc18aa22322761e25d6a;p=thirdparty%2Fpdns.git dnsdist: Enable experimental kTLS support with OpenSSL on Linux --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index e77170d94f..3f69ca1d3a 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -223,6 +223,7 @@ static void parseTLSConfig(TLSConfig& config, const std::string& context, boost: getOptionalValue(vars, "releaseBuffers", config.d_releaseBuffers); getOptionalValue(vars, "enableRenegotiation", config.d_enableRenegotiation); getOptionalValue(vars, "tlsAsyncMode", config.d_asyncMode); + getOptionalValue(vars, "ktls", config.d_ktls); } #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) @@ -522,6 +523,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "validateCertificates", config.d_tlsParams.d_validateCertificates); getOptionalValue(vars, "releaseBuffers", config.d_tlsParams.d_releaseBuffers); getOptionalValue(vars, "enableRenegotiation", config.d_tlsParams.d_enableRenegotiation); + getOptionalValue(vars, "ktls", config.d_tlsParams.d_ktls); getOptionalValue(vars, "subjectName", config.d_tlsSubjectName); if (getOptionalValue(vars, "subjectAddr", valueStr) > 0) { diff --git a/pdns/dnsdistdist/docs/advanced/tuning.rst b/pdns/dnsdistdist/docs/advanced/tuning.rst index ed940657fe..0a122dee35 100644 --- a/pdns/dnsdistdist/docs/advanced/tuning.rst +++ b/pdns/dnsdistdist/docs/advanced/tuning.rst @@ -111,6 +111,12 @@ For incoming and outgoing DNS over TLS support, the choice of the TLS provider ( Since 1.8.0, incoming DNS over TLS might also benefit from experimental support for TLS acceleration engines, like Intel QAT. See :func:`loadTLSEngine`, and the `tlsAsyncMode` parameter of :func:`addTLSLocal` for more information. +Incoming and outgoing DNS over TLS, as well as outgoing DNS over HTTPS, might benefit from experimental support kernel-accelerated TLS on Linux, when supported by the kernel and OpenSSL. See the `ktls` options on :func:`addTLSLocal` and :func:`newServer` for more information. Kernel support for kTLS might be verified by looking at the counters in ``/proc/net/tls_stat``. Note that: + + * supported ciphers depend on the exact kernel version used. ``TLS_AES_128_GCM_SHA256`` might be a good option for testing purpose since it was supported pretty early + * as of OpenSSL 3.0.7, kTLS can only be used for sending TLS 1.3 packets, not receiving them. Both sending and receiving packets should be working for TLS 1.2. + + Rules and Lua ------------- diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index a9d2c27071..b3d6b6a6ae 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -177,7 +177,7 @@ Listen Sockets ``tlsAsyncMode`` option added. .. versionchanged:: 1.8.0 ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`). - ``additionalAddresses`` and ``ignoreTLSConfigurationErrors`` options added. + ``additionalAddresses``, ``ignoreTLSConfigurationErrors`` and ``ktls`` options added. Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate. @@ -214,6 +214,7 @@ Listen Sockets * ``tlsAsyncMode=false``: bool - Whether to enable experimental asynchronous TLS I/O operations if OpenSSL is used as the TLS implementation and an asynchronous capable SSL engine (or provider) is loaded. See also :func:`loadTLSEngine` or :func:`loadTLSProvider` to load the engine (or provider). * ``additionalAddresses``: list - List of additional addresses (with port) to listen on. Using this option instead of creating a new frontend for each address avoids the creation of new thread and Frontend objects, reducing the memory usage. The drawback is that there will be a single set of metrics for all addresses. * ``ignoreTLSConfigurationErrors=false``: bool - Ignore TLS configuration errors (such as invalid certificate path) and just issue a warning instead of aborting the whole process + * ``ktls=false``: bool - Whether to enable the experimental kernel TLS support on Linux, if both the kernel and the OpenSSL library support it. Default is false. .. function:: setLocal(address[, options]) @@ -562,7 +563,7 @@ Servers Added ``addXForwardedHeaders``, ``caStore``, ``checkTCP``, ``ciphers``, ``ciphers13``, ``dohPath``, ``enableRenegotiation``, ``releaseBuffers``, ``subjectName``, ``tcpOnly``, ``tls`` and ``validateCertificates`` to server_table. .. versionchanged:: 1.8.0 - Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool``, ``maxConcurrentTCPConnections``, ``subjectAddr``, ``lazyHealthCheckSampleSize``, ``lazyHealthCheckMinSampleCount``, ``lazyHealthCheckThreshold``, ``lazyHealthCheckFailedInterval``, ``lazyHealthCheckMode``, ``lazyHealthCheckUseExponentialBackOff``, ``lazyHealthCheckMaxBackOff``, ``lazyHealthCheckWhenUpgraded`` and ``healthCheckMode`` to server_table. + Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool``, ``maxConcurrentTCPConnections``, ``subjectAddr``, ``lazyHealthCheckSampleSize``, ``lazyHealthCheckMinSampleCount``, ``lazyHealthCheckThreshold``, ``lazyHealthCheckFailedInterval``, ``lazyHealthCheckMode``, ``lazyHealthCheckUseExponentialBackOff``, ``lazyHealthCheckMaxBackOff``, ``lazyHealthCheckWhenUpgraded``, ``healthCheckMode`` and ``ktls`` to server_table. Add a new backend server. Call this function with either a string:: @@ -637,7 +638,8 @@ Servers lazyHealthCheckThreshold=NUM, -- The threshold, as a percentage, of queries that should fail for the 'lazy' health-check to be triggered when ``healthCheckMode`` is set to ``lazy``. The default is 20 which means 20% of the last ``lazyHealthCheckSampleSize`` queries should fail for a health-check to be triggered. lazyHealthCheckUseExponentialBackOff=BOOL, -- Whether the 'lazy' health-check should use an exponential back-off instead of a fixed value, between health-check probes. The default is false which means that after a backend has been moved to the 'down' state health-check probes are sent every ``lazyHealthCheckFailedInterval`` seconds. When set to true, the delay between each probe starts at ``lazyHealthCheckFailedInterval`` seconds and double between every probe, capped at ``lazyHealthCheckMaxBackOff`` seconds. lazyHealthCheckMaxBackOff=NUM, -- This value, in seconds, caps the time between two health-check queries when ``lazyHealthCheckUseExponentialBackOff`` is set to true. The default is 3600 which means that at most one hour will pass between two health-check queries. - lazyHealthCheckWhenUpgraded=BOOL -- Whether the auto-upgraded version of this backend (see ``autoUpgrade``) should use the lazy health-checking mode. Default is false, which means it will use the regular health-checking mode. + lazyHealthCheckWhenUpgraded=BOOL, -- Whether the auto-upgraded version of this backend (see ``autoUpgrade``) should use the lazy health-checking mode. Default is false, which means it will use the regular health-checking mode. + ktls=BOOL, -- Whether to enable the experimental kernel TLS support on Linux, if both the kernel and the OpenSSL library support it. Default is false. Currently both DoT and DoH backend support this option. }) :param str server_string: A simple IP:PORT string. diff --git a/pdns/libssl.cc b/pdns/libssl.cc index 930273047f..a85c5f2da6 100644 --- a/pdns/libssl.cc +++ b/pdns/libssl.cc @@ -864,6 +864,12 @@ std::pair, std::vector 0) { SSL_CTX_set_timeout(ctx.get(), config.d_sessionTimeout); } diff --git a/pdns/libssl.hh b/pdns/libssl.hh index 977f3f6bd3..fd5d90c032 100644 --- a/pdns/libssl.hh +++ b/pdns/libssl.hh @@ -51,6 +51,8 @@ public: bool d_enableRenegotiation{false}; /* enable TLS async mode, if supported by any engine */ bool d_asyncMode{false}; + /* enable kTLS mode, if supported */ + bool d_ktls{false}; }; struct TLSErrorCounters diff --git a/pdns/tcpiohandler.cc b/pdns/tcpiohandler.cc index f2817bd902..3cb2aad8c7 100644 --- a/pdns/tcpiohandler.cc +++ b/pdns/tcpiohandler.cc @@ -351,6 +351,13 @@ public: IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override { + if (!d_feContext && !d_connected) { + if (d_ktls) { + /* work-around to get kTLS to be started, as we cannot do that until after the socket has been connected */ + SSL_set_fd(d_conn.get(), SSL_get_fd(d_conn.get())); + } + } + do { int res = SSL_write(d_conn.get(), reinterpret_cast(&buffer.at(pos)), static_cast(toWrite - pos)); if (res <= 0) { @@ -361,6 +368,11 @@ public: } } while (pos < toWrite); + + if (!d_connected) { + d_connected = true; + } + return IOState::Done; } @@ -562,6 +574,11 @@ public: d_tlsSessions.push_back(std::make_unique(std::unique_ptr(session, SSL_SESSION_free))); } + void enableKTLS() + { + d_ktls = true; + } + static int s_tlsConnIndex; private: @@ -575,6 +592,8 @@ private: std::unique_ptr d_conn; std::string d_hostname; struct timeval d_timeout; + bool d_connected{false}; + bool d_ktls{false}; }; std::atomic_flag OpenSSLTLSConnection::s_initTLSConnIndex = ATOMIC_FLAG_INIT; @@ -646,6 +665,13 @@ public: #endif } + if (params.d_ktls) { +#ifdef SSL_OP_ENABLE_KTLS + sslOptions |= SSL_OP_ENABLE_KTLS; + d_ktls = true; +#endif /* SSL_OP_ENABLE_KTLS */ + } + registerOpenSSLUser(); #ifdef HAVE_TLS_CLIENT_METHOD @@ -773,7 +799,11 @@ public: std::unique_ptr getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override { - return std::make_unique(host, hostIsAddr, socket, timeout, d_tlsCtx); + auto conn = std::make_unique(host, hostIsAddr, socket, timeout, d_tlsCtx); + if (d_ktls) { + conn->enableKTLS(); + } + return conn; } void rotateTicketsKey(time_t now) override @@ -876,6 +906,7 @@ private: std::shared_ptr d_feContext{nullptr}; std::shared_ptr d_tlsCtx{nullptr}; // client context, on a server-side the context is stored in d_feContext->d_tlsCtx bool (*d_nextProtocolSelectCallback)(unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen){nullptr}; + bool d_ktls{false}; }; #endif /* HAVE_LIBSSL */ diff --git a/pdns/tcpiohandler.hh b/pdns/tcpiohandler.hh index 92faa6909a..68b1c02c63 100644 --- a/pdns/tcpiohandler.hh +++ b/pdns/tcpiohandler.hh @@ -577,6 +577,7 @@ struct TLSContextParameters bool d_validateCertificates{true}; bool d_releaseBuffers{true}; bool d_enableRenegotiation{false}; + bool d_ktls{false}; }; std::shared_ptr getTLSContext(const TLSContextParameters& params);