From: Remi Gacogne Date: Fri, 17 Nov 2023 11:14:19 +0000 (+0100) Subject: dnsdist: Add an option to set the SSL proxy protocol TLV X-Git-Tag: dnsdist-1.9.0-alpha4~25^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ad3e2e308ab29620bcc31caf5554c957909addd;p=thirdparty%2Fpdns.git dnsdist: Add an option to set the SSL proxy protocol TLV When the new `proxyProtocolAdvertiseTLS` to `newServer` is set, and the query has been received from the client over an encrypted channel, the SSL proxy protocol TLV is set in the proxy protocol payload sent to the backend. --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 09d9c5a344..1869ab0cf3 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -496,6 +496,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "useClientSubnet", config.useECS); getOptionalValue(vars, "useProxyProtocol", config.useProxyProtocol); + getOptionalValue(vars, "proxyProtocolAdvertiseTLS", config.d_proxyProtocolAdvertiseTLS); getOptionalValue(vars, "disableZeroScope", config.disableZeroScope); getOptionalValue(vars, "ipBindAddrNoPort", config.ipBindAddrNoPort); diff --git a/pdns/dnsdist-protocols.cc b/pdns/dnsdist-protocols.cc index e113925e06..2347822153 100644 --- a/pdns/dnsdist-protocols.cc +++ b/pdns/dnsdist-protocols.cc @@ -81,6 +81,11 @@ bool Protocol::isUDP() const return d_protocol == DoUDP || d_protocol == DNSCryptUDP; } +bool Protocol::isEncrypted() const +{ + return d_protocol != DoUDP && d_protocol != DoTCP; +} + uint8_t Protocol::toNumber() const { return static_cast(d_protocol); diff --git a/pdns/dnsdist-protocols.hh b/pdns/dnsdist-protocols.hh index bece300955..00070a0d21 100644 --- a/pdns/dnsdist-protocols.hh +++ b/pdns/dnsdist-protocols.hh @@ -57,6 +57,7 @@ public: const std::string& toString() const; const std::string& toPrettyString() const; bool isUDP() const; + bool isEncrypted() const; uint8_t toNumber() const; private: diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index b077138850..2d34cd1fbe 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -1506,6 +1506,13 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders addXPF(dq, selectedBackend->d_config.xpfRRCode); } + if (selectedBackend->d_config.useProxyProtocol && dq.getProtocol().isEncrypted() && selectedBackend->d_config.d_proxyProtocolAdvertiseTLS) { + if (!dq.proxyProtocolValues) { + dq.proxyProtocolValues = std::make_unique>(); + } + dq.proxyProtocolValues->push_back(ProxyProtocolValue{"", static_cast(ProxyProtocolValue::Types::PP_TLV_SSL)}); + } + selectedBackend->incQueriesCount(); return ProcessQueryResult::PassToBackend; } diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index acbc45102e..2a8b22d9b1 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -712,6 +712,7 @@ struct DownstreamState: public std::enable_shared_from_this bool mustResolve{false}; bool useECS{false}; bool useProxyProtocol{false}; + bool d_proxyProtocolAdvertiseTLS{false}; bool setCD{false}; bool disableZeroScope{false}; bool tcpFastOpen{false}; diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 61888708ca..06264ee285 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -598,6 +598,9 @@ Servers .. versionchanged:: 1.8.0 Added ``autoUpgrade``, ``autoUpgradeDoHKey``, ``autoUpgradeInterval``, ``autoUpgradeKeep``, ``autoUpgradePool``, ``maxConcurrentTCPConnections``, ``subjectAddr``, ``lazyHealthCheckSampleSize``, ``lazyHealthCheckMinSampleCount``, ``lazyHealthCheckThreshold``, ``lazyHealthCheckFailedInterval``, ``lazyHealthCheckMode``, ``lazyHealthCheckUseExponentialBackOff``, ``lazyHealthCheckMaxBackOff``, ``lazyHealthCheckWhenUpgraded``, ``healthCheckMode`` and ``ktls`` to server_table. + .. versionchanged:: 1.9.0 + Added ``proxyProtocolAdvertiseTLS`` to server_table. + :param str server_string: A simple IP:PORT string. :param table server_table: A table with at least an ``address`` key @@ -684,6 +687,7 @@ Servers ``lazyHealthCheckMaxBackOff`` ``number`` "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." ``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." + ``proxyProtocolAdvertiseTLS`` ``bool`` "Whether to set the SSL Proxy Protocol TLV in the proxy protocol payload sent to the backend if the query was received over an encrypted channel (DNSCrypt, DoQ, DoH or DoT). Requires ``useProxyProtocol=true``. Default is false." .. function:: getServer(index) -> Server diff --git a/pdns/proxy-protocol.hh b/pdns/proxy-protocol.hh index 373a750e94..a44c72110f 100644 --- a/pdns/proxy-protocol.hh +++ b/pdns/proxy-protocol.hh @@ -33,6 +33,8 @@ struct ProxyProtocolValue { return type == rhs.type && content == rhs.content; } + + enum class Types : uint8_t { PP_TLV_ALPN = 0x01, PP_TLV_SSL = 0x20 }; }; static const size_t s_proxyProtocolMinimumHeaderSize = 16; diff --git a/regression-tests.dnsdist/test_ProxyProtocol.py b/regression-tests.dnsdist/test_ProxyProtocol.py index dd4ca4fbef..8e9eab2c41 100644 --- a/regression-tests.dnsdist/test_ProxyProtocol.py +++ b/regression-tests.dnsdist/test_ProxyProtocol.py @@ -492,7 +492,7 @@ class TestProxyProtocolIncoming(ProxyProtocolTest): addDOHLocal("127.0.0.1:%d", "%s", "%s", {"/"}, {library='nghttp2', proxyProtocolOutsideTLS=true}) addDOHLocal("127.0.0.1:%d", "%s", "%s", {"/"}, {library='nghttp2', proxyProtocolOutsideTLS=false}) setProxyProtocolACL( { "127.0.0.1/32" } ) - newServer{address="127.0.0.1:%d", useProxyProtocol=true} + newServer{address="127.0.0.1:%d", useProxyProtocol=true, proxyProtocolAdvertiseTLS=true} function addValues(dq) dq:addProxyProtocolValue(0, 'foo') @@ -789,7 +789,7 @@ class TestProxyProtocolIncoming(ProxyProtocolTest): receivedResponse.id = response.id self.assertEqual(receivedQuery, query) self.assertEqual(receivedResponse, response) - self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) + self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) for idx in range(5): receivedResponse = None @@ -805,7 +805,7 @@ class TestProxyProtocolIncoming(ProxyProtocolTest): receivedResponse.id = response.id self.assertEqual(receivedQuery, query) self.assertEqual(receivedResponse, response) - self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) + self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) def testProxyDoHSeveralQueriesOverConnectionPPInside(self): """ @@ -842,7 +842,7 @@ class TestProxyProtocolIncoming(ProxyProtocolTest): receivedResponse.id = response.id self.assertEqual(receivedQuery, query) self.assertEqual(receivedResponse, response) - self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) + self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) for idx in range(5): receivedResponse = None @@ -858,7 +858,7 @@ class TestProxyProtocolIncoming(ProxyProtocolTest): receivedResponse.id = response.id self.assertEqual(receivedQuery, query) self.assertEqual(receivedResponse, response) - self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) + self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'], [1, b'dnsdist'], [ 2, b'foo'], [3, b'proxy'], [32, ''], [ 42, b'bar'], [255, b'proxy-protocol'] ], v6=False, sourcePort=None, destinationPort=reverseProxyPort) @classmethod def tearDownClass(cls):