A certificate can be imported successfully while users still receive an older certificate. The wrong profile may have been updated, a hostname may reach another VIP or a second site may retain its previous configuration.
Certificate management therefore needs a check of the certificate actually served to the client. This can be largely independent of the load-balancer vendor: for both F5 and A10, the question is what this hostname receives at this entry point.
Inventory hostnames and entry points
A list of certificate files is not a complete view of the delivered service. A useful inventory includes the application hostname, VIP, port, site, trust context and responsible team.
| Field | Example |
|---|---|
| Application hostname | portal.example.nl |
| VIP to test | 192.0.2.10 |
| TLS port | 443 |
| Site | Datacenter A |
| Trust context | Public or internal CA |
| Owner | Responsible application team |
For a multi-site service, test the hostname at each relevant entry point. This can reveal a renewal that reached one location but not another.
Separate the address from the TLS server name
Testing only an IP address may reach the wrong TLS context. Connect to the selected VIP while supplying the application hostname as the TLS server name.
Python provides a TLS context, server_hostname, peer-certificate information and conversion of certificate timestamps. A standard client context can perform certificate and hostname validation. See the Python SSL reference.
This example checks two entry points:
import json
import math
import socket
import ssl
import time
targets = [
("portal.example.nl", "192.0.2.10", 443),
("portal.example.nl", "192.0.2.20", 443),
]
context = ssl.create_default_context()
threshold_days = 30
failed = False
for hostname, connect_address, port in targets:
try:
with socket.create_connection(
(connect_address, port),
timeout=5,
) as connection:
with context.wrap_socket(
connection,
server_hostname=hostname,
) as tls:
certificate = tls.getpeercert()
expires_at = ssl.cert_time_to_seconds(
certificate["notAfter"]
)
remaining_seconds = expires_at - time.time()
remaining_days = math.floor(
remaining_seconds / 86400
)
status = (
"warning"
if remaining_seconds < threshold_days * 86400
else "ok"
)
if status != "ok":
failed = True
result = {
"hostname": hostname,
"address": connect_address,
"port": port,
"days_remaining": remaining_days,
"status": status,
}
except (OSError, ssl.SSLError, KeyError, ValueError) as error:
failed = True
result = {
"hostname": hostname,
"address": connect_address,
"port": port,
"status": "error",
"error_type": type(error).__name__,
}
print(json.dumps(result))
raise SystemExit(1 if failed else 0)
For an internal PKI, supply a trusted CA file to create_default_context(). Certificate verification remains enabled rather than being bypassed.
Distinguish expiry warnings from connection errors
The script produces two different types of signal. A warning means a verified connection succeeded, but the certificate expires within the chosen threshold. An error means the connection or certificate validation did not complete successfully.
An expired or untrusted certificate may already be rejected during the handshake. It will therefore not produce an ordinary expiry result. This is intentional: the monitoring should expose that the service no longer meets the validation requirements.
Unreachability is also an error. A run that stops producing results must not be interpreted as a healthy service.
Make warnings actionable
“Certificate expiring” is not useful enough if nobody knows who should respond. Associate each inventory entry with an owner, request process and deployment procedure.
Choose the warning threshold according to the real lead time for issuance, approval and rollout. In an automatically renewed environment, approaching expiry may instead indicate a failure in the renewal pipeline. Investigate the complete chain, including delivery to the appropriate devices.
Verify the outcome after renewal
Run the same checks after a certificate change. Compare results across VIPs and sites, then verify application behaviour as well.
The example is not a complete TLS security audit. It does not assess cipher policy or provide a general revocation verdict, and it only checks the selected entry points. Its scope is deliberately narrower: establish that a client can build a trusted TLS connection and that the served leaf certificate is renewed in time.
That tests the outcome that matters to users, rather than relying solely on successful import messages.