Grafana Dashboard for SSL Certificate Expiry Visualization
SSL/TLS certificates are the bedrock of secure communication on the internet. Forgetting to renew one can lead to service outages, security warnings for your users, and a frantic scramble to restore trust. While dedicated monitoring tools exist (and are often recommended for their reliability), many engineering teams leverage their existing observability stack to gain visibility into certificate lifecycles. If you're already using Grafana, it's a powerful platform for visualizing certificate expiry data.
This article will walk you through building a Grafana dashboard to track your SSL certificate expiry, covering data collection, dashboard construction, and important real-world considerations.
Why Grafana for SSL Expiry?
Grafana excels at turning time-series data into actionable insights. If you have a diverse infrastructure, chances are you're already using it to monitor server metrics, application performance, and network health. Extending it to include certificate expiry offers several benefits:
- Centralized View: Keep all your critical operational data in one place. No need to jump between multiple dashboards or tools.
- Visual Trends: See at a glance which certificates are nearing expiry, allowing for proactive renewal planning rather than reactive firefighting.
- Leverage Existing Infrastructure: If you're already running Prometheus, InfluxDB, or another time-series database, integrating certificate data is a natural extension.
- Customizable Alerts: Grafana's alerting capabilities can notify you through various channels (Slack, email, PagerDuty) when certificates approach their expiry threshold.
However, it's crucial to understand a key limitation: Grafana is a visualization and alerting tool, not a data collector for this specific problem. The real challenge with certificate expiry monitoring isn't displaying the data, but reliably collecting it from all your diverse services. This is where a DIY approach can become complex, especially at scale.
Data Collection – The Hard Part
Before you can build a fancy Grafana dashboard, you need data. This involves querying your certificates and extracting their expiry dates. Here are a few common approaches, each with its pros and cons.
Method 1: Scripting with openssl and a Custom Exporter
This is the most hands-on method, giving you maximum flexibility but also requiring the most effort to maintain. The core idea is to use openssl to query certificate details and then expose that data in a format your time-series database can ingest (e.g., Prometheus Exposition Format, InfluxDB line protocol).
Let's say you want to monitor www.example.com and api.internal.net. You can get the expiry date using openssl:
# For a standard HTTPS website
echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | openssl x509 -noout -enddate
# Example output: notAfter=Jan 15 12:00:00 2025 GMT
# To get a Unix timestamp for easier calculation:
END_DATE_STR=$(echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d'=' -f2)
END_TIMESTAMP=$(date -d "$END_DATE_STR" +%s)
CURRENT_TIMESTAMP=$(date +%s)
SECONDS_LEFT=$((END_TIMESTAMP - CURRENT_TIMESTAMP))
echo "www_example_com_ssl_expiry_seconds_left $SECONDS_LEFT"
You would then wrap this in a script that iterates through a list of hosts and ports, generating metrics.
Example Script Snippet (for Prometheus textfile collector):
#!/bin/bash
# List of hosts to check (format: host:port:servername)
CERT_TARGETS=(
"www.example.com:443:www.example.com"
"api.internal.net:8443:api.internal.net"
"legacyapp.olddomain.com:443:legacyapp.olddomain.com"
)
OUTPUT_FILE="/var/lib/node_exporter/textfile_collector/ssl_expiry.prom"
TEMP_FILE=$(mktemp)
echo "# HELP ssl_certificate_expiry_seconds_left Seconds until SSL certificate expires." > "$TEMP_FILE"
echo "# TYPE ssl_certificate_expiry_seconds_left gauge" >> "$TEMP_FILE"
for target in "${CERT_TARGETS[@]}"; do
HOST=$(echo "$target" | cut -d':' -f1)
PORT=$(echo "$target" | cut -d':' -f2)
SERVERNAME=$(echo "$target" | cut -d':' -f3) # Important for SNI
END_DATE_STR=$(echo | openssl s_client -servername "$SERVERNAME" -connect "$HOST:$PORT" 2>/dev/null | openssl x509 -noout -enddate | cut -d'=' -f2)
if [ -n "$END_DATE_STR" ]; then
END_TIMESTAMP=$(date -d "$END_DATE_STR" +%s)
CURRENT_TIMESTAMP=$(date +%s)
SECONDS_LEFT=$((END_TIMESTAMP - CURRENT_TIMESTAMP))
echo "ssl_certificate_expiry_seconds_left{host=\"$HOST\",port=\"$PORT\",servername=\"$SERVERNAME\"} $SECONDS_LEFT" >> "$TEMP_FILE"
else
echo "WARN: Could not retrieve expiry for $HOST:$PORT (servername: $SERVERNAME)" >&2
# Optionally, expose a metric indicating failure
echo "ssl_certificate_expiry_seconds_left{host=\"$HOST\",port=\"$PORT\",servername=\"$SERVERNAME\"} -1" >> "$TEMP_FILE"
fi
done
mv "$TEMP_FILE" "$OUTPUT_FILE"
You would run this script via cron every few hours. The node_exporter (if installed on the host running the script) would then pick up ssl_expiry.prom via its textfile collector and expose it to Prometheus.
Pitfalls:
- SNI (Server Name Indication): Crucial for web servers hosting multiple certificates on a single IP address. Always use
-servernameinopenssl s_client. - Non-HTTP/S Services: This
openssl s_clientmethod works for any service that speaks TLS over TCP (e.g., LDAP, SMTP withSTARTTLS, PostgreSQL/MySQL with TLS). You might need to adjustopenssloptions (e.g.,-starttls smtp). - Firewalls: Ensure your monitoring host can reach the target services on their respective TLS ports.
- Error Handling: The script needs robust error handling for unreachable hosts, invalid certificates, or
opensslparsing failures. - Scale: Managing a list of hundreds or thousands of targets in a shell script becomes unwieldy.
Method 2: Blackbox Exporter (for Prometheus users)
For those already using Prometheus, the Blackbox Exporter is an excellent solution for probing external endpoints. It can perform various checks