]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Enable experimental kTLS support with OpenSSL on Linux
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 2 Feb 2022 14:41:00 +0000 (15:41 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 14 Feb 2023 11:43:48 +0000 (12:43 +0100)
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/advanced/tuning.rst
pdns/dnsdistdist/docs/reference/config.rst
pdns/libssl.cc
pdns/libssl.hh
pdns/tcpiohandler.cc
pdns/tcpiohandler.hh

index e77170d94fe514ea973d44e105123127396bd2c8..3f69ca1d3aa68d945544dcc19d04e9cdc2f723f5 100644 (file)
@@ -223,6 +223,7 @@ static void parseTLSConfig(TLSConfig& config, const std::string& context, boost:
   getOptionalValue<bool>(vars, "releaseBuffers", config.d_releaseBuffers);
   getOptionalValue<bool>(vars, "enableRenegotiation", config.d_enableRenegotiation);
   getOptionalValue<bool>(vars, "tlsAsyncMode", config.d_asyncMode);
+  getOptionalValue<bool>(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<bool>(vars, "validateCertificates", config.d_tlsParams.d_validateCertificates);
                          getOptionalValue<bool>(vars, "releaseBuffers", config.d_tlsParams.d_releaseBuffers);
                          getOptionalValue<bool>(vars, "enableRenegotiation", config.d_tlsParams.d_enableRenegotiation);
+                         getOptionalValue<bool>(vars, "ktls", config.d_tlsParams.d_ktls);
                          getOptionalValue<std::string>(vars, "subjectName", config.d_tlsSubjectName);
 
                          if (getOptionalValue<std::string>(vars, "subjectAddr", valueStr) > 0) {
index ed940657fe66f14418a58aa6f9d48d12d5dc12e5..0a122dee35d8cfc11603394a8c1eafbf77ae0acb 100644 (file)
@@ -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
 -------------
 
index a9d2c270712054685f188311a2d44c5b2a7d73ec..b3d6b6a6ae83990668b5dfb21f8dffd132206cc1 100644 (file)
@@ -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.
index 930273047fd39e2dacd0e39b6ba6c81779dea116..a85c5f2da6b505c1676ab6e675771157b8dfa03a 100644 (file)
@@ -864,6 +864,12 @@ std::pair<std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)>, std::vector<std::st
 #endif /* HAVE_SSL_CTX_SET_NUM_TICKETS */
   }
 
+  if (config.d_ktls) {
+#ifdef SSL_OP_ENABLE_KTLS
+    sslOptions |= SSL_OP_ENABLE_KTLS;
+#endif /* SSL_OP_ENABLE_KTLS */
+  }
+
   if (config.d_sessionTimeout > 0) {
     SSL_CTX_set_timeout(ctx.get(), config.d_sessionTimeout);
   }
index 977f3f6bd3b526fc10660ea5919fca42714c5353..fd5d90c0320dc5fdd6bee6158751fdc10794a344 100644 (file)
@@ -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
index f2817bd90279fabb985d42fe6ae1e35838701694..3cb2aad8c7d382da706cb17fee482ca6d0618ce0 100644 (file)
@@ -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<const char *>(&buffer.at(pos)), static_cast<int>(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<OpenSSLSession>(std::unique_ptr<SSL_SESSION, void (*)(SSL_SESSION*)>(session, SSL_SESSION_free)));
   }
 
+  void enableKTLS()
+  {
+    d_ktls = true;
+  }
+
   static int s_tlsConnIndex;
 
 private:
@@ -575,6 +592,8 @@ private:
   std::unique_ptr<SSL, void(*)(SSL*)> 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<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override
   {
-    return std::make_unique<OpenSSLTLSConnection>(host, hostIsAddr, socket, timeout, d_tlsCtx);
+    auto conn = std::make_unique<OpenSSLTLSConnection>(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<OpenSSLFrontendContext> d_feContext{nullptr};
   std::shared_ptr<SSL_CTX> 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 */
index 92faa6909a55423fba4e74f601ecadc0874d92e6..68b1c02c631161904d0a9c4d6ac0bf70c83f77c7 100644 (file)
@@ -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<TLSCtx> getTLSContext(const TLSContextParameters& params);