If you manage your DNS using Cloudflare, you can benefit from their Certificate Transparency Monitoring service, currently in beta, which alerts you via email whenever a new certificate appears in CT logs for your domain. This however requires your domain to be hosted on Cloudflare, which is unfortunate as it is a great service. Perhaps once the beta is over they would extend the service to non-Cloudflare domains.

Other alternatives are the Facebook Certificate Transparency Monitoring tool and SSLMate Cert Spotter. Facebook’s offering is free, but requires a Facebook account and email notifications enabled (which may or may not enable other email notifications). This leaves Cert Spotter, which isn’t free and $15/month can be a lot if you only need to monitor a couple of domains.

Luckily, Cert Spotter has a free API offering that is enough for light use. I have created a small utility using the Cert Spotter API: ct-monitor. What it does is pretty simple: for a given domain, get the ID of the last discovered issuance from the positions file if it exists, then query the Cert Spotter API for any new issuances, send a notification, and save the last discovered issuance to the positions file. Queries for all issuances are only done during the first run where there are no positions file.

The tool can be run standalone or wrapped into a systemd file, or it can be run as a CronJob in Kubernetes, which is what I intended to use it for.

Using it is as simple as copying over the sample manifests and editing it as necessary, especially for the secrets.yaml and config.toml files.

In order to stay within the Cert Spotter API limits, it is recommended to run the CronJob once per hour.

cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: ct-monitor
  labels:
    app.kubernetes.io/name: ct-monitor
spec:
  schedule: "0 * * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 60
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: ct-monitor
              image: quay.io/hsn723/ct-monitor:0.1
              volumeMounts:
                - name: config-volume
                  mountPath: /etc/ct-monitor/config.toml
                  subPath: config.toml
                - name: positions-volume
                  mountPath: /var/log/ct-monitor
              envFrom:
                - secretRef:
                    name: ct-monitor-secrets
          volumes:
            - name: positions-volume
              persistentVolumeClaim:
                claimName: ct-monitor-position
            - name: config-volume
              configMap:
                name: ct-monitor-config
          restartPolicy: OnFailure

As the Pod created for the Job gets deleted every CronJob run, we want to keep state (the positions file) using a small PersistentVolumeClaim.

pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ct-monitor-position
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Mi

ct-monitor expects a token for the Cert Spotter API provided in the config.toml, as a command-line argument, or using the environment variable CERTSPOTTER_TOKEN (recommended). Is is recommended to place such Secrets in another repository, or make use of SealedSecrets.

Other environment variables that may be required depending on the chosen way to send report emails. For instance, Sendgrid expects a SENDGRID_TOKEN environment variable and Amazon SES expects an access key ID and secret access key as environment variables:

  • Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY
  • Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY

Once everything is set up and applied, we will start receiving emails like the following:

ct-monitor has observed the issuance of the following certificate(s) for the festivaljapon.com domain:

Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
DNS Names: [monitor-2.festivaljapon.com]
Validity: 2020-07-28T18:07:30-00:00 - 2020-10-26T18:07:30-00:00
SHA256: 946881b2cd9e7fbc55c0172070a7dcb872ade10a374a5b5120aaf74f6081d0c6
Issuance type: cert

Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
DNS Names: [status.festivaljapon.com]
Validity: 2020-07-29T14:36:27-00:00 - 2020-10-27T14:36:27-00:00
SHA256: f0bc7da52ce02856bfadf79bae599a87fe37672f939a5d6fd29d05867d03d294
Issuance type: cert

Issuer: C=US, O="Cloudflare, Inc.", CN=Cloudflare Inc ECC CA-3
DNS Names: [*.festivaljapon.com festivaljapon.com sni.cloudflaressl.com]
Validity: 2020-08-10T00:00:00-00:00 - 2021-08-10T12:00:00-00:00
SHA256: 3b62cdaaf584d39e7844cdccac9c0bd5fe46eb9fe65b4df72c38165d149b2f58
Issuance type: cert