CRL-Monitor monitors CRLs.
It issues certificates, revokes them, and then looks for them to appear in CRLs. Each component runs as an AWS Lambda.
The churner
runs periodically. On each run it issues a certificate, fetches and lints
the certificate's CRL URL, revokes the certificate, and stores its serial number and metadata
for the checker
to later verify that it shows up as revoked. It also checks previously
seen serials. If they haven't shown up in a CRL after a reasonable amount of time, checker
produces an error.
The checker
runs in response to the upload of each new CRL shard in S3. It diffs the newly
uploaded CRL shard against its previous version and verifies:
- New CRL has a later date and higher CRL number than the previous version.
- New CRL passes lints.
- For any serials removed between the old shard and the new one:
- The certificate is expired (based on fetching it by serial from Let's Encrypt).
- For any serials added (if the certificate was issued by the churner):
- The certificate's CRLDistributionPoint matches the CRL shard's IssuingDistributionPoint.
The checker
also removes from database any certificates it sees, to indicate that their
revocation has been published, so the churner
won't alert about them
It then marks as completed (deletes) any churner
-issued certificates that show up on
the new CRL.
This repository has two binaries named checker
and two binaries named churner
. The
binaries under cmd
are for local use and testing. The binaries under lambda
are for
deployment to AWS Lambda. The key difference is that the lambda/
binaries register a
lambda handler (lambda.StartWithOptions()
), which AWS then calls. That
handler can return errors, and we have separate Cloudwatch monitoring that alerts when
any errors are detected.
The lambda binaries are built by a release workflow on GitHub Actions triggered by uploading
a release tag (starting with v
). Those binaries are uploaded to S3 under a versioned path.
They are then deployed to Lambda using Terraform (in another repository).
Most of the tests are unittests and can be run with:
go test ./...
There is also an integration test for DynamoDB code. To run this, install Java and run:
./db/run_integration_test.sh
sequenceDiagram
participant churn as Certificate Churner<br />Lambda
participant ca as Let's Encrypt<br />Certification Authority
participant ddb as Pending Certificates<br />DynamoDB
participant s3 as CRL Storage<br />S3 Bucket
participant checker as CRL Checker<br />Lambda
loop timer
activate churn
churn->>ca: Issue certificate
churn->>ca: Fetch CRL
churn->>ca: Revoke certificate
churn->>ddb: Store certificate metadata
ddb->>churn: Get previous revoked serials
Note over churn: Alert if any<br />expected serials missed<br />inclusion deadline
deactivate churn
end
loop New CRL
ca->>s3: Publish CRL Shard
activate checker
s3->>checker: S3 Event
checker->>s3: Read current CRL
checker->>s3: Read previous CRL
Note over checker: Alert if CRL<br />fails linting
loop random selection of serials
checker->>ca: Get Certificate
end
Note over checker: Alert if CRL had<br />serials leave early
checker->>ddb: Get revoked serials
checker->>ddb: Delete seen serials
deactivate checker
end