]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - regression-tests.dnsdist/test_TLSSessionResumption.py
Merge pull request #8713 from rgacogne/auth-strict-caches-size
[thirdparty/pdns.git] / regression-tests.dnsdist / test_TLSSessionResumption.py
index 2c5b615fb024dc7cf78795489d09f99ca1b5575d..4f9dfd6194c3e1cf6a11f5892d1bda4ad59fb9c4 100644 (file)
@@ -2,8 +2,11 @@
 import base64
 import dns
 import os
+import shutil
 import subprocess
+import tempfile
 import time
+import unittest
 from dnsdisttests import DNSDistTest
 try:
   range = xrange
@@ -16,10 +19,12 @@ class DNSDistTLSSessionResumptionTest(DNSDistTest):
     _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
 
     @classmethod
-    def checkSessionResumed(cls, addr, port, serverName, caFile, ticketFileOut, ticketFileIn):
+    def checkSessionResumed(cls, addr, port, serverName, caFile, ticketFileOut, ticketFileIn, allowNoTicket=False):
+        outFile = tempfile.NamedTemporaryFile()
+
         # we force TLS 1.3 because the session file gets updated when an existing ticket encrypted with an older key gets re-encrypted with the active key
         # whereas in TLS 1.2 the existing ticket is written instead..
-        testcmd = ['openssl', 's_client', '-tls1_3', '-CAfile', caFile, '-connect', '%s:%d' % (addr, port), '-servername', serverName, '-sess_out', ticketFileOut]
+        testcmd = ['openssl', 's_client', '-tls1_3', '-CAfile', caFile, '-connect', '%s:%d' % (addr, port), '-servername', serverName, '-sess_out', outFile.name]
         if ticketFileIn and os.path.exists(ticketFileIn):
             testcmd = testcmd + ['-sess_in', ticketFileIn]
 
@@ -27,10 +32,20 @@ class DNSDistTLSSessionResumptionTest(DNSDistTest):
         try:
             process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
             # we need to wait just a bit so that the Post-Handshake New Session Ticket has the time to arrive..
-            time.sleep(0.1)
+            time.sleep(0.5)
             output = process.communicate(input=b'')
         except subprocess.CalledProcessError as exc:
-            raise AssertionError('dnsdist --check-config failed (%d): %s' % (exc.returncode, exc.output))
+            raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output))
+
+        if process.returncode != 0:
+          raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output))
+
+        if os.stat(outFile.name).st_size == 0:
+          # if tickets have been disabled, or if the session ticket encryption key is exactly the same, we might not get a new ticket
+          if not allowNoTicket:
+            raise AssertionError('%s failed (%d) to write a session to the output file: %s' % (testcmd, process.returncode, output))
+        else:
+          shutil.copyfile(outFile.name, ticketFileOut)
 
         for line in output[0].decode().splitlines():
             if line.startswith('Reused, TLSv1.'):
@@ -43,6 +58,7 @@ class DNSDistTLSSessionResumptionTest(DNSDistTest):
         with open(outputFile, 'wb') as fp:
             fp.write(os.urandom(numberOfTickets * 80))
 
+@unittest.skipIf('SKIP_DOH_TESTS' in os.environ, 'DNS over HTTPS tests are disabled')
 class TestNoTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
 
     _serverKey = 'server.key'
@@ -62,9 +78,10 @@ class TestNoTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
         """
         Session Resumption: DoH (disabled)
         """
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.out', None))
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.out', '/tmp/session.out'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/no-session.out.doh', None, allowNoTicket=True))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/no-session.out.doh', '/tmp/no-session.out.doh', allowNoTicket=True))
 
+@unittest.skipIf('SKIP_DOH_TESTS' in os.environ, 'DNS over HTTPS tests are disabled')
 class TestTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
 
     _serverKey = 'server.key'
@@ -86,28 +103,28 @@ class TestTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
         """
         Session Resumption: DoH
         """
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', None))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', None))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh', allowNoTicket=True))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
 
         # the session should be resumed and a new ticket, encrypted with the newly active key, should be stored
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh'))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
 
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh'))
 
         # rotate the TLS session ticket keys several times, not keeping any key around this time!
         for _ in range(self._numberOfKeys):
             self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
 
         # we should not be able to resume
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh'))
 
         # generate a file containing _numberOfKeys ticket keys
         self.generateTicketKeysFile(self._numberOfKeys, '/tmp/ticketKeys.1')
@@ -116,48 +133,48 @@ class TestTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
 
         # create a new session, resume it
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', None))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', None))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh', allowNoTicket=True))
 
         # reload the same keys
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
 
         # should still be able to resume
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh', allowNoTicket=True))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
         # should still be able to resume
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh'))
 
         # reload the same keys
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
         # since the last key was only present in memory, we should not be able to resume
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh'))
 
         # but now we can
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh', allowNoTicket=True))
 
         # generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
         self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
         # we should be able to resume, and the ticket should be re-encrypted with the new key (NOTE THAT we store into a new file!!)
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session'))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh.2', '/tmp/session.doh'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh.2', '/tmp/session.doh.2', allowNoTicket=True))
 
         # rotate all keys, we should not be able to resume
         for _ in range(self._numberOfKeys):
             self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.3', '/tmp/session.2'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh.3', '/tmp/session.doh.2'))
 
         # reload from file 1, the old session should resume
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh', '/tmp/session.doh', allowNoTicket=True))
 
         # reload from file 2, the latest session should resume
         self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.doh.2', '/tmp/session.doh.2', allowNoTicket=True))
 
 class TestNoTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
 
@@ -178,8 +195,8 @@ class TestNoTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
         """
         Session Resumption: DoT (disabled)
         """
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.out', None))
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.out', '/tmp/session.out'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/no-session.out.dot', None, allowNoTicket=True))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/no-session.out.dot', '/tmp/no-session.out.dot', allowNoTicket=True))
 
 class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
 
@@ -202,28 +219,28 @@ class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
         """
         Session Resumption: DoT
         """
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', None))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', None))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot', allowNoTicket=True))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
 
         # the session should be resumed and a new ticket, encrypted with the newly active key, should be stored
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot'))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
 
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot'))
 
         # rotate the TLS session ticket keys several times, not keeping any key around this time!
         for _ in range(self._numberOfKeys):
             self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
 
         # we should not be able to resume
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot'))
 
         # generate a file containing _numberOfKeys ticket keys
         self.generateTicketKeysFile(self._numberOfKeys, '/tmp/ticketKeys.1')
@@ -232,45 +249,45 @@ class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
 
         # create a new session, resume it
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', None))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', None))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot', allowNoTicket=True))
 
         # reload the same keys
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
 
         # should still be able to resume
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot', allowNoTicket=True))
 
         # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
         for _ in range(self._numberOfKeys - 1):
             self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
         # should still be able to resume
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot'))
 
         # reload the same keys
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
         # since the last key was only present in memory, we should not be able to resume
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot'))
 
         # but now we can
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot', allowNoTicket=True))
 
         # generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
         self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.2')")
         # we should be able to resume, and the ticket should be re-encrypted with the new key (NOTE THAT we store into a new file!!)
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session'))
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot.2', '/tmp/session.dot'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot.2', '/tmp/session.dot.2', allowNoTicket=True))
 
         # rotate all keys, we should not be able to resume
         for _ in range(self._numberOfKeys):
             self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
-        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.3', '/tmp/session.2'))
+        self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot.3', '/tmp/session.dot.2'))
 
         # reload from file 1, the old session should resume
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
+        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.dot', '/tmp/session.dot', allowNoTicket=True))
 
         # reload from file 2, the latest session should resume
         self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.2')")
-        self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.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))