openssl s_client one-liner for cert expiry
Certificate expiry is a perennial headache for engineers. We've all been there: a service suddenly stops working, users are impacted, and after some frantic debugging, you discover a certificate quietly expired in the middle of the night. While robust monitoring is the long-term solution, sometimes you just need a quick, ad-hoc check. That's where openssl s_client shines. It's a powerful, versatile command-line tool that can connect to a remote server, perform an SSL/TLS handshake, and display certificate information.
In this article, we'll dive into crafting an openssl s_client one-liner to quickly check a server's certificate expiry date. We'll cover the basics, refine the output for clarity, and discuss important pitfalls and edge cases.
The Core openssl s_client Command
At its heart, openssl s_client simulates an SSL/TLS client connection. To retrieve certificate details, you need to tell it where to connect and what information to output.
The most basic form to connect to a host and get its certificate information looks like this:
echo | openssl s_client -servername example.com -connect example.com:443
Let's break down these essential components:
echo |: This pipes an empty string toopenssl s_client. This is crucial becauses_clientexpects some input to complete the handshake and allow you to interact with the server. For simply retrieving certificate info, an empty input is sufficient.-servername example.com: This specifies the Server Name Indication (SNI). SNI is vital for servers that host multiple SSL/TLS certificates on the same IP address (common with virtual hosts). If you omit this, the server might return a default certificate, which might not be the one you're interested in. Always include this if you know the hostname.-connect example.com:443: This tellsopenssl s_clientthe target host and port to connect to. Port 443 is standard for HTTPS, but you might need to adjust this for other services (e.g., 993 for IMAPS, 3306 for MySQL with SSL).
The output of this command will be verbose, including handshake details, cipher suites, and then the full certificate chain in PEM format.
Extracting Expiry Information
The raw output from openssl s_client is a lot to sift through. To get just the expiry date, we need to pipe its output to openssl x509, which is designed for parsing X.509 certificates.
We'll use a few more options:
-noout: Preventsopenssl x509from printing the encoded version of the certificate.-enddate: Extracts and prints thenotAfter(expiry) date.2>/dev/null: This redirectsstderr(standard error) to/dev/null.openssl s_clientoften prints connection details and warnings tostderr, which we usually want to suppress for a clean one-liner output.
Combining these, our one-liner to get the raw expiry date looks like this:
echo | openssl s_client -servername certfly.io -connect certfly.io:443 2>/dev/null | openssl x509 -noout -enddate
This will output something like: notAfter=Dec 25 12:00:00 2024 GMT. This is a good start, but it's not ideal for quick comparisons or automated scripts.
Making it Human-Readable and Useful
A raw expiry date is useful, but what we often want to know is "how many days until expiry?" We can achieve this by converting the expiry date to a Unix timestamp and comparing it with the current timestamp. This usually involves date and some awk or sed magic.
Let's refine our one-liner to give us the number of days remaining. We'll extract the notAfter value, convert it to a Unix timestamp, and then calculate the difference.
Here's a more advanced one-liner:
EXPIRY_DATE=$(echo | openssl s_client -servername certfly.io -connect certfly.io:443 2>/dev/null | openssl x509 -noout -enddate | cut -d'=' -f2)
if [ -z "$EXPIRY_DATE" ]; then
echo "Failed to retrieve expiry date."
else
EXPIRY_TIMESTAMP=$(date -d "$EXPIRY_DATE" +%s)
CURRENT_TIMESTAMP=$(date +%s)
DAYS_REMAINING=$(( (EXPIRY_TIMESTAMP - CURRENT_TIMESTAMP) / 86400 ))
echo "Certificate for certfly.io expires on $EXPIRY_DATE. Days remaining: $DAYS_REMAINING"
fi
This script snippet, while not a true one-liner anymore due to the if statement, demonstrates the full logic. If you absolutely need a single line, you can chain commands with && and || and use command substitution more aggressively, but readability suffers.
For a true single-line command that outputs just the days remaining (or an error if it fails cleanly enough):
Example 1: Checking AWS S3 Certificate Expiry
Let's check a common service like an AWS S3 bucket endpoint, which often uses certificates managed by AWS. We'll use s3.amazonaws.com as a generic example.
echo | openssl s_client -servername s3.amazonaws.com -connect s3.amazonaws.com:443 2>/dev/null | \
openssl x509 -noout -enddate | \
awk -F'=' '{print $2}' | \
xargs -I {} date -d {} +%s | \
xargs -I {} bash -c 'echo $(( ({} - $(date +%s)) / 86400 ))'
This chained command will output a single integer representing the days remaining.
awk -F'=' '{print $2}': Extracts just the date string fromnotAfter=....xargs -I {} date -d {} +%s: Takes the date string, converts it to a Unix timestamp, and passes it to the next command.xargs -I {} bash -c 'echo $(( ({} - $(date +%s)) / 86400 ))': Calculates the difference between the expiry timestamp and the current timestamp, then divides by 86400 (seconds in a day) to get days remaining.
This provides a very concise output, perfect for quick checks or integrating into simple scripts.
Pitfalls and Edge Cases
While powerful, these one-liners have limitations. Understanding them is key to avoiding false positives or missed issues.
- SNI is Critical: As mentioned,
-servernameis not optional for most modern web servers. If you omit it, you might get the server's default certificate (often for its IP address or a wildcard), which isn't the certificate your application actually uses. Always specify the exact hostname. - Non-Standard Ports: Not all TLS services run on port 443. Remember to adjust the port number (
:443) if you're checking an IMAP server (993), SMTP (465/587 with STARTTLS), or a custom application. - Error Handling and
2>/dev/null: Redirectingstderrto/dev/nullis great for clean output, but it also hides connection errors, handshake failures, or certificate validation issues. If your one-liner returns an empty result or an unexpected error, try removing2>/dev/nullto see the underlyingopensslerror messages. - Certificate Chains:
openssl s_clientwill often return the entire certificate chain.openssl x509 -enddatetypically extracts the expiry of the leaf certificate (the one issued directly to your server name). This is usually what you want, but be aware that intermediate certificates in the chain also expire. This one-liner doesn't easily check intermediate certificate expiry. - Multiple Certificates on a Host: A single IP address might serve different certificates based on the SNI, or even based on client IP. The one-liner checks one specific hostname and port at a time. It won't tell you about other certificates served by the same physical server.
- Network Connectivity and Firewalls: If your machine can't reach the target host and port (e.g., due to a firewall, proxy issues, or DNS problems),
openssl s_clientwill fail. Ensure your network path is clear. - Client-Side Certificate Authentication: This one-liner focuses on server-side certificates. If your