]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_TLSSessionResumption.py
Merge pull request #8344 from franklouwers/rec-signalhandling
[thirdparty/pdns.git] / regression-tests.dnsdist / test_TLSSessionResumption.py
1 #!/usr/bin/env python
2 import base64
3 import dns
4 import os
5 import subprocess
6 import time
7 from dnsdisttests import DNSDistTest
8 try:
9 range = xrange
10 except NameError:
11 pass
12
13 class DNSDistTLSSessionResumptionTest(DNSDistTest):
14
15 _consoleKey = DNSDistTest.generateConsoleKey()
16 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
17
18 @classmethod
19 def checkSessionResumed(cls, addr, port, serverName, caFile, ticketFileOut, ticketFileIn):
20 # 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
21 # whereas in TLS 1.2 the existing ticket is written instead..
22 testcmd = ['openssl', 's_client', '-tls1_3', '-CAfile', caFile, '-connect', '%s:%d' % (addr, port), '-servername', serverName, '-sess_out', ticketFileOut]
23 if ticketFileIn and os.path.exists(ticketFileIn):
24 testcmd = testcmd + ['-sess_in', ticketFileIn]
25
26 output = None
27 try:
28 process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
29 # we need to wait just a bit so that the Post-Handshake New Session Ticket has the time to arrive..
30 time.sleep(0.1)
31 output = process.communicate(input=b'')
32 except subprocess.CalledProcessError as exc:
33 raise AssertionError('dnsdist --check-config failed (%d): %s' % (exc.returncode, exc.output))
34
35 for line in output[0].decode().splitlines():
36 if line.startswith('Reused, TLSv1.'):
37 return True
38
39 return False
40
41 @staticmethod
42 def generateTicketKeysFile(numberOfTickets, outputFile):
43 with open(outputFile, 'wb') as fp:
44 fp.write(os.urandom(numberOfTickets * 80))
45
46 class TestNoTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
47
48 _serverKey = 'server.key'
49 _serverCert = 'server.chain'
50 _serverName = 'tls.tests.dnsdist.org'
51 _caCert = 'ca.pem'
52 _dohServerPort = 8443
53 _numberOfKeys = 0
54 _config_template = """
55 newServer{address="127.0.0.1:%s"}
56
57 addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" }, { numberOfTicketsKeys=%d, numberOfStoredSessions=0, sessionTickets=false })
58 """
59 _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_numberOfKeys']
60
61 def testNoSessionResumption(self):
62 """
63 Session Resumption: DoH (disabled)
64 """
65 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.out', None))
66 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.out', '/tmp/session.out'))
67
68 class TestTLSSessionResumptionDOH(DNSDistTLSSessionResumptionTest):
69
70 _serverKey = 'server.key'
71 _serverCert = 'server.chain'
72 _serverName = 'tls.tests.dnsdist.org'
73 _caCert = 'ca.pem'
74 _dohServerPort = 8443
75 _numberOfKeys = 5
76 _config_template = """
77 setKey("%s")
78 controlSocket("127.0.0.1:%s")
79 newServer{address="127.0.0.1:%s"}
80
81 addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" }, { numberOfTicketsKeys=%d })
82 """
83 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_dohServerPort', '_serverCert', '_serverKey', '_numberOfKeys']
84
85 def testSessionResumption(self):
86 """
87 Session Resumption: DoH
88 """
89 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', None))
90 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
91
92 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
93 for _ in range(self._numberOfKeys - 1):
94 self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
95
96 # the session should be resumed and a new ticket, encrypted with the newly active key, should be stored
97 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
98
99 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
100 for _ in range(self._numberOfKeys - 1):
101 self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
102
103 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
104
105 # rotate the TLS session ticket keys several times, not keeping any key around this time!
106 for _ in range(self._numberOfKeys):
107 self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
108
109 # we should not be able to resume
110 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
111
112 # generate a file containing _numberOfKeys ticket keys
113 self.generateTicketKeysFile(self._numberOfKeys, '/tmp/ticketKeys.1')
114 self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
115 # load all ticket keys from the file
116 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
117
118 # create a new session, resume it
119 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', None))
120 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
121
122 # reload the same keys
123 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
124
125 # should still be able to resume
126 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
127
128 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
129 for _ in range(self._numberOfKeys - 1):
130 self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
131 # should still be able to resume
132 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
133
134 # reload the same keys
135 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
136 # since the last key was only present in memory, we should not be able to resume
137 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
138
139 # but now we can
140 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
141
142 # generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
143 self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
144 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
145 # 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!!)
146 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session'))
147 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
148
149 # rotate all keys, we should not be able to resume
150 for _ in range(self._numberOfKeys):
151 self.sendConsoleCommand("getDOHFrontend(0):rotateTicketsKey()")
152 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.3', '/tmp/session.2'))
153
154 # reload from file 1, the old session should resume
155 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.1')")
156 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
157
158 # reload from file 2, the latest session should resume
159 self.sendConsoleCommand("getDOHFrontend(0):loadTicketsKeys('/tmp/ticketKeys.2')")
160 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._dohServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
161
162 class TestNoTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
163
164 _serverKey = 'server.key'
165 _serverCert = 'server.chain'
166 _serverName = 'tls.tests.dnsdist.org'
167 _caCert = 'ca.pem'
168 _tlsServerPort = 8443
169 _numberOfKeys = 0
170 _config_template = """
171 newServer{address="127.0.0.1:%s"}
172
173 addTLSLocal("127.0.0.1:%s", "%s", "%s", { numberOfTicketsKeys=%d, numberOfStoredSessions=0, sessionTickets=false })
174 """
175 _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_numberOfKeys']
176
177 def testNoSessionResumption(self):
178 """
179 Session Resumption: DoT (disabled)
180 """
181 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.out', None))
182 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.out', '/tmp/session.out'))
183
184 class TestTLSSessionResumptionDOT(DNSDistTLSSessionResumptionTest):
185
186 _serverKey = 'server.key'
187 _serverCert = 'server.chain'
188 _serverName = 'tls.tests.dnsdist.org'
189 _caCert = 'ca.pem'
190 _tlsServerPort = 8443
191 _numberOfKeys = 5
192 _config_template = """
193 setKey("%s")
194 controlSocket("127.0.0.1:%s")
195 newServer{address="127.0.0.1:%s"}
196
197 addTLSLocal("127.0.0.1:%s", "%s", "%s", { provider="openssl", numberOfTicketsKeys=%d })
198 """
199 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_numberOfKeys']
200
201 def testSessionResumption(self):
202 """
203 Session Resumption: DoT
204 """
205 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', None))
206 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
207
208 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
209 for _ in range(self._numberOfKeys - 1):
210 self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
211
212 # the session should be resumed and a new ticket, encrypted with the newly active key, should be stored
213 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
214
215 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
216 for _ in range(self._numberOfKeys - 1):
217 self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
218
219 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
220
221 # rotate the TLS session ticket keys several times, not keeping any key around this time!
222 for _ in range(self._numberOfKeys):
223 self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
224
225 # we should not be able to resume
226 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
227
228 # generate a file containing _numberOfKeys ticket keys
229 self.generateTicketKeysFile(self._numberOfKeys, '/tmp/ticketKeys.1')
230 self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
231 # load all ticket keys from the file
232 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
233
234 # create a new session, resume it
235 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', None))
236 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
237
238 # reload the same keys
239 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
240
241 # should still be able to resume
242 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
243
244 # rotate the TLS session ticket keys several times, but keep the previously active one around so we can resume
245 for _ in range(self._numberOfKeys - 1):
246 self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
247 # should still be able to resume
248 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
249
250 # reload the same keys
251 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
252 # since the last key was only present in memory, we should not be able to resume
253 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
254
255 # but now we can
256 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
257
258 # generate a file with only _numberOfKeys - 1 keys, so the last active one should still be around after loading that one
259 self.generateTicketKeysFile(self._numberOfKeys - 1, '/tmp/ticketKeys.2')
260 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.2')")
261 # 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!!)
262 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session'))
263 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))
264
265 # rotate all keys, we should not be able to resume
266 for _ in range(self._numberOfKeys):
267 self.sendConsoleCommand("getTLSContext(0):rotateTicketsKey()")
268 self.assertFalse(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.3', '/tmp/session.2'))
269
270 # reload from file 1, the old session should resume
271 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.1')")
272 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session', '/tmp/session'))
273
274 # reload from file 2, the latest session should resume
275 self.sendConsoleCommand("getTLSContext(0):loadTicketsKeys('/tmp/ticketKeys.2')")
276 self.assertTrue(self.checkSessionResumed('127.0.0.1', self._tlsServerPort, self._serverName, self._caCert, '/tmp/session.2', '/tmp/session.2'))