Advertisement
Open Source Projects by Phil Schwartz

Automating SSL Certificate Expiry Checks Across Multiple Hosts with Python

Expired TLS certificates remain one of the most common causes of preventable outages, and Australian businesses running e-commerce platforms or government services on .au domains cannot afford the downtime. A single forgotten renewal can lock customers out of payment gateways, break API integrations with partners in Sydney and Melbourne, or trigger browser security warnings that erode user trust overnight. Writing a small Python utility to sweep across an inventory of hosts and flag certificates approaching their expiry date gives operations teams breathing room before a crisis hits.

This kind of automation fits naturally alongside other defensive tools. Phil Schwartz has documented similar maintenance scripts, including the script I wrote to import legacy DenyHosts data from CSV into a new database, which reflects the same philosophy of handling tedious infrastructure hygiene through small, focused programs.

Parsing X.509 Certificates with the Standard Library

Python ships with the ssl and socket modules, which together can establish a handshake, retrieve the peer certificate, and decode its structure without pulling in heavyweight dependencies. The ssl.get_server_certificate function returns a PEM-formatted string, while ssl.SSLContext lets the script enforce minimum protocol versions. For more granular field access, including the subject alternative names and the exact expiry timestamp, the cryptography package offers a cleaner interface than the deprecated OpenSSL bindings.

A robust checker should distinguish between certificates that have already expired, those expiring within thirty days, and those with more than thirty days remaining. Categorising results this way helps prioritise remediation in shared Slack channels used by teams distributed across Brisbane, Perth, and Adelaide time zones, where a single alert can easily get buried under day-to-day chatter.

Connecting to Remote Hosts Safely

Establishing a TCP connection to port 443 on each hostname is straightforward, but the script needs to handle several edge cases gracefully. DNS resolution failures, connection timeouts, and hosts that present a certificate for a different hostname than the one requested all occur frequently in the wild. Wrapping the socket creation in a try/except block with sensible defaults, such as a five-second timeout, prevents the whole sweep from stalling on a single problematic endpoint.

When targeting internal hosts behind a corporate firewall in a data centre in the ACT or a cloud region in Sydney, the script may need to route through a proxy or authenticate against an internal CA bundle. Configuring ssl.CERT_NONE with manual chain validation is rarely the right choice; instead, ship a copy of the organisation's root certificates and let Python verify the chain properly.

Extracting and Comparing Expiry Dates

Once the certificate is in hand, converting the expiry field to a datetime object makes the math trivial. The cryptography library returns a cryptography.x509.NotValidAfter value that responds to standard datetime arithmetic. Subtracting the current time from the expiry yields a timedelta, which can then be compared against a configurable warning window, such as fourteen days for staging environments and forty-five days for production systems serving Australian customers.

Storing the results in a simple dictionary keyed by hostname keeps the output readable, while writing the same data to a CSV file opens up further analysis in spreadsheet tools familiar to finance and compliance teams. For larger environments, dumping to SQLite and generating a weekly summary report has proven more maintainable than fighting with complex logging frameworks.

Sending Notifications That People Actually Read

An alert system is only useful if the right person sees it before customers do. Email remains the lowest common denominator, but pairing the expiry checker with a webhook into Microsoft Teams or Mattermost works well for many Australian IT shops. For critical infrastructure, integrating with PagerDuty or Opsgenie ensures someone is paged at 3 AM if a certificate slips through the cracks.

Local context matters here. The Australian Cyber Security Centre publishes advisories that occasionally affect certificate authorities, so subscribing to their feed helps the script's maintainer respond to upstream changes. For smaller businesses, a daily Slack message posted at 9 AM AEST gives the operations team the morning to act before customer traffic peaks.

Running the Script on a Schedule

A certificate checker that runs once and then sits on a shelf provides limited value. Dropping the script into a cron job on a small Linux instance, or wiring it into systemd timers on a modern Ubuntu box, keeps the checks running without manual intervention. Running the sweep twice daily catches expiries earlier and reduces the chance of missing something due to a transient DNS issue or a misconfigured load balancer.

Logging each run to a structured file or shipping metrics to a Prometheus endpoint turns the script from a one-off utility into a component of a broader observability stack. Over time, the accumulated data reveals which teams or domains consistently renew certificates late, highlighting processes that need tightening rather than just patching the immediate symptom.

Practical Recommendations for Production Use