]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Check identical frontends get the same STEK
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 2 Apr 2025 13:31:26 +0000 (15:31 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 2 Apr 2025 13:31:26 +0000 (15:31 +0200)
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_TLSSessionResumption.py

index cc9bd25b9fb71b545a7191872be6ce896e0026cc..53c97b04b476a20634588f6dfe7cdb952584dac2 100644 (file)
@@ -663,7 +663,7 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
         return sock
 
     @classmethod
-    def openTLSConnection(cls, port, serverName, caCert=None, timeout=2.0, alpn=[]):
+    def openTLSConnection(cls, port, serverName, caCert=None, timeout=2.0, alpn=[], sslctx=None, session=None):
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
         if timeout:
@@ -671,10 +671,11 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
 
         # 2.7.9+
         if hasattr(ssl, 'create_default_context'):
-            sslctx = ssl.create_default_context(cafile=caCert)
-            if len(alpn)> 0 and hasattr(sslctx, 'set_alpn_protocols'):
-              sslctx.set_alpn_protocols(alpn)
-            sslsock = sslctx.wrap_socket(sock, server_hostname=serverName)
+            if not sslctx:
+                sslctx = ssl.create_default_context(cafile=caCert)
+                if len(alpn)> 0 and hasattr(sslctx, 'set_alpn_protocols'):
+                    sslctx.set_alpn_protocols(alpn)
+            sslsock = sslctx.wrap_socket(sock, server_hostname=serverName, session=session)
         else:
             sslsock = ssl.wrap_socket(sock, ca_certs=caCert, cert_reqs=ssl.CERT_REQUIRED)
 
index 7f41c79491f91a87d36364f09fc69f37705c1032..b46eded2cb899e642af0898d7c8f60705d795e02 100644 (file)
@@ -2,7 +2,9 @@
 import base64
 import dns
 import os
+import requests
 import shutil
+import ssl
 import subprocess
 import tempfile
 import time
@@ -297,3 +299,92 @@ class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
         # reload from file 2, the latest session should resume
         self.sendConsoleCommand("getTLSFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
         self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot.2', '/tmp/session.dot.2', allowNoTicket=True))
+
+class TestTLSSessionResumptionDOTIdenticalFrontends(DNSDistTest):
+    """
+    Check that identical frontends share the same TLS session ticket encryption key
+    """
+    _webTimeout = 2.0
+    _webServerPort = pickAvailablePort()
+    _webServerBasicAuthPassword = 'secret'
+    _webServerAPIKey = 'apisecret'
+    _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
+    _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
+    _serverKey = 'server.key'
+    _serverCert = 'server.chain'
+    _serverName = 'tls.tests.dnsdist.org'
+    _caCert = 'ca.pem'
+    _tlsServerPort = pickAvailablePort()
+    _numberOfIdenticalFrontends = 10
+    _config_template = ""
+    _config_params = []
+    _yaml_config_template = """---
+webserver:
+  listen_address: "127.0.0.1:%d"
+  password: "%s"
+  api_key: "%s"
+  acl:
+    - 127.0.0.0/8
+backends:
+  - address: "127.0.0.1:%d"
+    protocol: "Do53"
+binds:
+  - listen_address: "127.0.0.1:%d"
+    reuseport: true
+    protocol: "DoT"
+    threads: %d
+    tls:
+      certificates:
+        - certificate: "%s"
+          key: "%s"
+      provider: "openssl"
+    """
+    _yaml_config_params = ['_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed', '_testServerPort', '_tlsServerPort', '_numberOfIdenticalFrontends', '_serverCert', '_serverKey']
+
+    def testTLSSessionResumptionAcrossIdenticalFrontends(self):
+        """
+        TCP Session Resumption: Check that identical frontends share the same STEK
+        """
+        name = 'identical-frontends.tls-session-resumption.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+
+        session = None
+        sslctx = ssl.create_default_context(cafile=self._caCert)
+        nb_connections = self._numberOfIdenticalFrontends * 20
+        for idx in range(nb_connections):
+            conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert, timeout=1, sslctx=sslctx, session=session)
+            self.sendTCPQueryOverConnection(conn, query, response=response, timeout=1)
+            (receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, useQueue=True, timeout=1)
+            receivedQuery.id = query.id
+            self.assertEqual(receivedQuery, query)
+            self.assertEqual(receivedResponse, response)
+            if idx == 0:
+                self.assertFalse(conn.session_reused)
+                session = conn.session
+            else:
+                self.assertTrue(conn.session_reused)
+
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEqual(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+        self.assertTrue(len(content['frontends']), self._numberOfIdenticalFrontends + 2)
+
+        new_sessions = 0
+        resumed_sessions = 0
+        tls_frontends_seen = {}
+        for frontend in content['frontends']:
+            if frontend['type'] != 'TCP (DNS over TLS)':
+                continue
+            new_sessions = new_sessions + int(frontend['tlsNewSessions'])
+            resumed_sessions = resumed_sessions + int(frontend['tlsResumptions'])
+            if int(frontend['tlsNewSessions']) > 0 or int(frontend['tlsResumptions']) > 0:
+                tls_frontends_seen[frontend['id']] = True
+
+        self.assertEqual(new_sessions, 1)
+        self.assertEqual(resumed_sessions, nb_connections - new_sessions)
+        self.assertGreater(len(tls_frontends_seen), 1)