The previous solution (ad-hoc stream-parsing the CRLs to obtain their issuers and nextUpdate) was too cute. It began breaking on CRLs that had an addition hex 0x30 byte somewhere in their header. I thought that 0x30 was a reserved character only to be used for tags in ASN1 encoded with DER; turns out that's not true. Rather than write a full-fledged ASN1 stream-parser, the simplest solution is to just maintain the list of issuers as a constant in the codebase. This is fine because the issuer for a specific CRL URI should not change. If it does, we've probably got bigger problems. This also removes the Flask app's functionality for updating the local CRL cache. This is being handled out-of-band by a Kubernetes CronJob and is not a concern of the app's. This means that instances of the CRLCache do not have to explicitly track expirations for CRLs. Previously, the in-memory dictionary or CRL issuers and locations included expirations; now it is flattened to not include that information. The CRLCache class has been updated to accept a crl_list kwargs so that unit tests can provide their own alternative CRL lists, since we now hard-code the expected CRLs and issuers. The nightly CRL check job has been updated to check that the hard-coded list of issuers matches what we get when we actually sync the CRLs.
62 lines
1.6 KiB
Python
62 lines
1.6 KiB
Python
from contextlib import contextmanager
|
|
import os
|
|
from unittest.mock import Mock
|
|
|
|
from OpenSSL import crypto
|
|
from cryptography.hazmat.backends import default_backend
|
|
from flask import template_rendered
|
|
|
|
from atst.utils.notification_sender import NotificationSender
|
|
|
|
|
|
@contextmanager
|
|
def captured_templates(app):
|
|
recorded = []
|
|
|
|
def record(sender, template, context, **extra):
|
|
recorded.append((template, context))
|
|
|
|
template_rendered.connect(record, app)
|
|
try:
|
|
yield recorded
|
|
finally:
|
|
template_rendered.disconnect(record, app)
|
|
|
|
|
|
class FakeLogger:
|
|
def __init__(self):
|
|
self.messages = []
|
|
self.extras = []
|
|
|
|
def log(self, _lvl, msg, *args, **kwargs):
|
|
self._log(_lvl, msg, *args, **kwargs)
|
|
|
|
def info(self, msg, *args, **kwargs):
|
|
self._log("info", msg, *args, **kwargs)
|
|
|
|
def warning(self, msg, *args, **kwargs):
|
|
self._log("warning", msg, *args, **kwargs)
|
|
|
|
def error(self, msg, *args, **kwargs):
|
|
self._log("error", msg, *args, **kwargs)
|
|
|
|
def _log(self, _lvl, msg, *args, **kwargs):
|
|
self.messages.append(msg)
|
|
if "extra" in kwargs:
|
|
self.extras.append(kwargs["extra"])
|
|
|
|
|
|
FakeNotificationSender = lambda: Mock(spec=NotificationSender)
|
|
|
|
|
|
def parse_for_issuer_and_next_update(crl):
|
|
with open(crl, "rb") as crl_file:
|
|
parsed = crypto.load_crl(crypto.FILETYPE_ASN1, crl_file.read())
|
|
return parsed.get_issuer().der()
|
|
|
|
|
|
def make_crl_list(x509_obj, x509_path):
|
|
issuer = x509_obj.issuer.public_bytes(default_backend())
|
|
filename = os.path.basename(x509_path)
|
|
return [(filename, issuer.hex())]
|