Automating SSL Certificate Monitoring with Python and SSLyze
Certificate expiry rarely causes a subtle incident. A checkout page, customer portal or API endpoint can start failing within minutes, often while the team is focused on something else. Automating the checks removes the dependence on a calendar reminder and provides a reliable signal before an outage begins.
SSLyze is well suited to this work because it speaks TLS directly and reports certificate details alongside protocol and cipher configuration. Python can wrap each scan, normalise the results, compare them with previous runs and send an alert when a deadline or policy threshold is crossed. This automation should complement, rather than replace, a dependable certificate renewal process.
Australian teams often operate a mixture of .com.au domains, newer .au namespaces, cloud services and on-premises systems across Sydney, Melbourne, Brisbane and regional offices. An inventory must distinguish origin servers from CDN edges because a public hostname can present several certificates. That distinction matters when an apparent expiry is actually an isolated edge-node fault.
Monitoring must also respect local obligations. Where the Privacy Act 1988 applies, keep customer records out of scan results; IP addresses and diagnostic logs can still contain personal information, so apply access controls and sensible retention. The Australian Signals Directorate’s Essential Eight also provides a useful security baseline for the monitoring host, including patching, backups and administrative controls.
What SSLyze can inspect
A certificate scan can expose the leaf certificate’s expiry window, subject, issuer, serial number, public-key algorithm and deployment details. SSLyze can also collect the presented chain and hostname information, giving your Python layer enough context to decide whether an endpoint meets policy rather than merely reporting a date.
Broader TLS scans add protocol support, cipher suites, renegotiation behaviour, compression, session resumption and OCSP stapling. These findings matter because a valid certificate can still sit behind obsolete TLS settings. Keep certificate lifecycle alerts separate from broader security findings so routine upgrades do not obscure an imminent renewal failure.
SSLyze is a scanner, not an authoritative certificate inventory. It sees what a particular connection receives, which may differ by resolver, region or CDN edge. Store the hostname, port, resolved address, scan timestamp and scanner version with every result, then repeat unexpected findings from another network before escalating them.
Designing the monitoring workflow
Begin with a declarative inventory containing each public hostname, port, owning team, environment and renewal method. Record whether DNS-01, a managed load balancer or manual deployment provides the certificate. That metadata turns raw expiry data into an actionable alert and prevents the monitoring job from paging the wrong person.
Run a broad inventory scan once, then schedule routine checks daily. Increase frequency when a certificate enters a defined renewal window, but avoid scanning every endpoint every hour without reason. Most public sites sit behind CDNs and WAFs, so randomise start times slightly to reduce burst traffic and make the job less conspicuous from monitored networks.
A command-line job should behave predictably in every environment. The same preference for repeatable routines appears in specialist software such as the Nikon Coolscan utility, although certificate monitoring belongs in its own isolated service account and dependency environment.
Parsing scanner results with Python
Invoke SSLyze through Python’s subprocess module with an argument list, never a shell string. Give every run a timeout, capture standard error and write JSON to a unique file. A failed TCP connection, TLS handshake or scanner process must produce a monitoring error rather than being mistaken for an expired certificate.
import json
import subprocess
from pathlib import Path
result_file = Path("/var/lib/sslyze/example.com.au.json")
process = subprocess.run(
[
"sslyze",
"--cert_info=example.com.au:443",
f"--json_out={result_file}",
],
capture_output=True,
text=True,
timeout=90,
check=False,
)
if process.returncode != 0:
raise RuntimeError(process.stderr or "SSLyze failed")
payload = json.loads(result_file.read_text())
The exact JSON nesting can change between SSLyze releases, so isolate extraction in one adapter function and maintain a fixture-based test for it. Walk the server scan results to find the certificate command result, then emit a stable internal record containing the hostname, expiry date, issuer and hostname status. Store that normalised schema separately from the scanner-specific payload.
Essential monitoring checks
The certificate lifecycle list should cover more than the final expiry date:
- Expiry thresholds at 30, 14, 7 and 1 day
- Activation dates, hostname matches and wildcard scope
- Issuer, chain and public-key algorithm changes
- Certificate serial changes after renewal
- OCSP stapling and revocation signals where supported
The transport and job-health list should make configuration drift and monitoring failures visible:
- TLS 1.2 minimum and TLS 1.3 support
- Exclusion of obsolete cipher suites and compression
- Stable hostname, port, resolver and CDN routing
- Scan completion, runtime and scanner-version changes
- Separation of network failure from certificate failure
Each alert should state the affected hostname, certificate expiry, days remaining and responsible owner. Include the previous successful scan time so recipients can distinguish an expiring certificate from an endpoint that SSLyze simply could not reach. Preserve enough diagnostic context to investigate the change without attaching an entire verbose scan to every message.
Running the service reliably
Use systemd timers on Linux hosts or cron in a container, and run the scanner as an unprivileged account with a fixed working directory. Store output atomically, apply retention to old JSON files and schedule retries with exponential backoff. A failed job should generate its own alert because silence after a deployment can otherwise look like good news.
Australian sites often depend on NBN connections, with staff also moving between fixed office networks and mobile hotspots during the day. A timeout from one location can be a local broadband problem rather than a certificate fault. Record the source address and compare a retry from a second network before declaring an endpoint down.
Existing operations software can carry the alert. Karl’s Nagios and SNMP monitoring demonstrates how Nagios and SNMP fit together in a home network, and the same approach works for a small business. Expose certificate age and scan freshness as checks, then map them onto the normal notification path.
Put the monitoring service into production
Package the wrapper as a version-controlled command with a small configuration file, an inventory file and a local test endpoint. Add unit tests for expiry calculations and a live canary host with a known renewal date. Keep credentials out of the repository, and use a dedicated API token with permission to send only the required alerts.
Choose the host for reliability rather than raw computing power. A low-power mini PC, cloud instance or small rack server can all work, but power, storage and replacement-part availability differ across the Australian market. Karl’s hardware vendor directory provides a practical reference when the monitoring appliance needs to be replaced or expanded.
Roll out the service in stages: first scan a handful of critical domains, verify the alert wording, then add the remaining services. Publish a runbook that names the certificate owner, renewal lead time and rollback action. Automate collection and comparison while leaving deployment approval with the team controlling each certificate.
Create the inventory today and run the first scheduled scan against your highest-impact .com.au or .au hostname. Confirm that a deliberate test alert reaches the right person, then extend the same tested path to every production endpoint.
Karl Katzke