diff --git a/authentik/enterprise/stages/mtls/stage.py b/authentik/enterprise/stages/mtls/stage.py index 421db4e2d6..3807fbf2d2 100644 --- a/authentik/enterprise/stages/mtls/stage.py +++ b/authentik/enterprise/stages/mtls/stage.py @@ -14,6 +14,7 @@ from cryptography.x509 import ( load_pem_x509_certificate, ) from cryptography.x509.verification import PolicyBuilder, Store, VerificationError +from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from authentik.brands.models import Brand @@ -138,9 +139,9 @@ class MTLSStageView(ChallengeStageView): authorities_cert = [x.certificate for x in authorities] for _cert in certs: try: - PolicyBuilder().store(Store(authorities_cert)).build_client_verifier().verify( - _cert, [] - ) + PolicyBuilder().store(Store(authorities_cert)).time( + now() + ).build_client_verifier().verify(_cert, []) return _cert except ( InvalidSignature, diff --git a/authentik/root/test_plugin.py b/authentik/root/test_plugin.py index 64057f6aac..42288df74c 100644 --- a/authentik/root/test_plugin.py +++ b/authentik/root/test_plugin.py @@ -16,6 +16,18 @@ def pytest_sessionstart(*_, **__): """Clear the console ahead of the pytest output starting""" if not IS_CI: print("\x1b[2J\x1b[H") + # Pre-warm cryptography's PyO3 PyDateTime type cache with the real + # datetime class. If the first extraction happens under @freeze_time + # (e.g. in MTLSStageTests), PyO3 caches freezegun's FakeDatetime, + # which breaks every later test that passes a real datetime into + # cryptography ("TypeError: 'datetime' object is not an instance + # of 'FakeDatetime'"). The discard is intentional — only side + # effect needed is the type-cache initialization. + from datetime import UTC, datetime + + from cryptography.x509.verification import PolicyBuilder + + PolicyBuilder().time(datetime.now(tz=UTC)) yield