Skip to content

Commit 9922d44

Browse files
committed
Support tls1.3 as default for tls server
1 parent 016fce6 commit 9922d44

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

aiomisc/service/tls.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ class SSLOptionsBase:
2525
verify: bool
2626
require_client_cert: bool
2727
purpose: ssl.Purpose
28+
minimum_version: ssl.TLSVersion = ssl.TLSVersion.TLSv1_3
29+
maximum_version: ssl.TLSVersion = ssl.TLSVersion.TLSv1_3
30+
ciphers: Tuple[str, ...] = (
31+
"ECDHE-RSA-AES256-GCM-SHA384",
32+
"ECDHE-ECDSA-AES256-GCM-SHA384",
33+
"ECDHE-RSA-CHACHA20-POLY1305",
34+
"ECDHE-ECDSA-CHACHA20-POLY1305",
35+
"ECDHE-RSA-AES128-GCM-SHA256",
36+
"ECDHE-ECDSA-AES128-GCM-SHA256",
37+
)
2838

2939

3040
class SSLOptions(SSLOptionsBase):
@@ -44,21 +54,41 @@ def __init__(
4454

4555
def create_context(self) -> ssl.SSLContext:
4656
context = ssl.create_default_context(
47-
purpose=self.purpose, cafile=self.ca,
57+
purpose=self.purpose,
4858
)
4959

50-
if self.ca and not self.ca.exists():
51-
raise FileNotFoundError("CA file doesn't exists")
60+
# Disable compression to prevent CRIME attacks
61+
context.options |= ssl.OP_NO_COMPRESSION
62+
63+
context.maximum_version = self.maximum_version
64+
context.minimum_version = self.minimum_version
65+
context.set_ciphers(":".join(self.ciphers))
66+
67+
if not self.verify:
68+
context.check_hostname = False
69+
70+
if self.ca:
71+
log.debug("Loading CA from %s", self.ca)
72+
if not self.ca.exists():
73+
raise FileNotFoundError(
74+
"CA file doesn't exists", str(self.ca.resolve()),
75+
)
76+
context.load_verify_locations(cafile=str(self.ca))
5277

5378
if self.require_client_cert:
79+
log.debug("Set server-side cert verification")
5480
context.verify_mode = ssl.VerifyMode.CERT_REQUIRED
81+
# Post-handshake authentication is required
82+
context.post_handshake_auth = True
83+
else:
84+
# Disable server-side cert verification
85+
context.check_hostname = False
86+
context.verify_mode = ssl.VerifyMode.CERT_NONE
5587

88+
# Load server-side cert and key
5689
if self.key and self.cert:
5790
context.load_cert_chain(self.cert, self.key)
5891

59-
if not self.verify:
60-
context.check_hostname = False
61-
6292
return context
6393

6494

poetry.toml

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/conftest.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,16 @@ def ssl_client_context(certs):
6565
key = str(certs / "client.key")
6666
cert = str(certs / "client.pem")
6767

68-
context = ssl.create_default_context(
69-
ssl.Purpose.SERVER_AUTH, capath=ca,
70-
)
68+
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, capath=ca)
7169

7270
if key:
7371
context.load_cert_chain(
7472
cert,
7573
key,
7674
)
7775

76+
context.load_verify_locations(cafile=ca)
7877
context.check_hostname = False
79-
context.verify_mode = ssl.CERT_NONE
78+
context.verify_mode = ssl.VerifyMode.CERT_NONE
8079

8180
return context

tests/test_entrypoint.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import pytest
1818

1919
import aiomisc
20+
from aiomisc import timeout
2021
from aiomisc.entrypoint import Entrypoint, entrypoint
2122
from aiomisc.service import TCPServer, TLSServer, UDPServer
2223
from aiomisc.service.aiohttp import AIOHTTPService
@@ -301,18 +302,22 @@ async def go():
301302
def test_tls_server(
302303
client_cert_required, certs, ssl_client_context, localhost,
303304
):
304-
event = asyncio.Event()
305-
306305
class TestService(TLSServer):
307306
DATA = []
307+
event: asyncio.Event
308+
309+
async def start(self):
310+
await super().start()
311+
self.event = asyncio.Event()
308312

309313
async def handle_client(
310314
self, reader: asyncio.StreamReader,
311315
writer: asyncio.StreamWriter,
312316
):
317+
print("handle_client")
313318
self.DATA.append(await reader.readline())
314319
writer.close()
315-
event.set()
320+
self.event.set()
316321

317322
service = TestService(
318323
address="127.0.0.1", port=0,
@@ -336,14 +341,17 @@ def writer(port):
336341
)
337342

338343
ssock.connect(("127.0.0.1", port))
339-
ssock.send(b"hello server\n")
344+
count_bytes = ssock.send(b"hello server\n")
345+
assert count_bytes == len(b"hello server\n")
346+
347+
@timeout(999)
348+
async def go():
349+
await asyncio.wait_for(writer(service.port), timeout=10)
350+
await service.event.wait()
340351

341352
with aiomisc.entrypoint(service) as loop:
342353
assert service.address == localhost
343-
loop.run_until_complete(
344-
asyncio.wait_for(writer(service.port), timeout=10),
345-
)
346-
loop.run_until_complete(event.wait())
354+
loop.run_until_complete(go())
347355

348356
assert TestService.DATA
349357
assert TestService.DATA == [b"hello server\n"]

0 commit comments

Comments
 (0)