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:
# 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)
import base64
import dns
import os
+import requests
import shutil
+import ssl
import subprocess
import tempfile
import time
# 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)