Tools · Python and Bash for security automation · 08 / 20
Monitoring TLS certificate expiry with a Python script
Connect to your hosts with Python’s ssl module, read certificate expiry and SANs, warn before expiry, and run the check on a schedule.
Why it matters
Expired certificates cause outages and train users to click through warnings. With shorter maximum certificate lifetimes now in place for public certificates and automation through ACME, monitoring is the safety net that catches renewals that silently failed.
The script
ssl.create_default_context verifies the chain and hostname. getpeercert returns parsed fields including notAfter and subjectAltName. A non-zero exit code makes it easy to alert from cron or CI.
#!/usr/bin/env python3
"""Warn when TLS certificates expire soon."""
import socket, ssl, sys, datetime as dt
WARN_DAYS = 21
ctx = ssl.create_default_context()
bad = 0
for target in sys.argv[1:]:
host, _, port = target.partition(':')
try:
with socket.create_connection((host, int(port or 443)), timeout=10) as s:
with ctx.wrap_socket(s, server_hostname=host) as tls:
cert = tls.getpeercert()
expires = dt.datetime.fromtimestamp(ssl.cert_time_to_seconds(cert['notAfter']), dt.timezone.utc)
days = (expires - dt.datetime.now(dt.timezone.utc)).days
sans = [v for k, v in cert.get('subjectAltName', []) if k == 'DNS']
status = 'OK' if days > WARN_DAYS else 'WARN'
bad += status != 'OK'
print(f'{status:4} {host:30} {days:4} days {expires:%Y-%m-%d} SAN={",".join(sans[:3])}')
except (ssl.SSLError, ssl.CertificateError, OSError) as e:
bad += 1
print(f'FAIL {host:30} {e}')
sys.exit(1 if bad else 0)Lab: test the edge cases
Run the script against a few sites you operate. Then test failure cases using a lab server: a certificate issued for a different name, an expired lab certificate (create one with openssl req -days 0 or use a public test site intended for this, such as badssl.com), and a self-signed certificate. Confirm each prints FAIL with a clear reason.
python3 certwatch.py example.com expired.badssl.com wrong.host.badssl.com self-signed.badssl.com echo exit=$?
Schedule it
Run daily from a systemd timer, cron, or a CI scheduled workflow and send failures to chat or email. Keep the host list in a file under version control. Monitoring from outside your network also catches cases where an internal proxy hides a problem.
Diagram
Certificate monitoring
- Connect
TLS connection with hostname verification.
- Read
notAfter and SANs from getpeercert().
- Decide
WARN under N days; FAIL on verification errors.
- Alert
Non-zero exit triggers a notification.
Check yourself
1. Which call creates a context that verifies certificate chains and hostnames?
2. Which certificate field gives the expiry date?
3. Why exit with a non-zero code on problems?