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 to openssl s_client. This is crucial because s_client expects 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 tells openssl s_client the 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: Prevents openssl x509 from printing the encoded version of the certificate.
  • -enddate: Extracts and prints the notAfter (expiry) date.
  • 2>/dev/null: This redirects stderr (standard error) to /dev/null. openssl s_client often prints connection details and warnings to stderr, 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 from notAfter=....
  • 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, -servername is 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: Redirecting stderr to /dev/null is 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 removing 2>/dev/null to see the underlying openssl error messages.
  • Certificate Chains: openssl s_client will often return the entire certificate chain. openssl x509 -enddate typically 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_client will fail. Ensure your network path is clear.
  • Client-Side Certificate Authentication: This one-liner focuses on server-side certificates. If your