Checking SSL Certificates With Bash
I help monitor and maintain a whole bunch of websites and of course, have a variety of SSL certificates. Usually, I keep an eye on them periodically using calendar items and the odd visit to SSL Checker but it is a bit of a pain.
I know what you are thinking - why don’t I just use a Cron job to automatically renew them? Well, sometimes automations fail and I like to also add in a human element.
In this case, what I really wanted was (another!) automated task that would do it for me, perhaps by sending an email, weekly. This blog post will show you how you can do that too using some Linux commands and the Bash shell.
Steps Required
The main things that we need to do for this are to:
- Iterate through a list of domains
- Check the SSL status of each domain
- Create a report
- Email that report
- Do it all again, a week later
The Script
To save you some time, here’s the whole script followed by a break-down of how it works.
1 |
|
If you are using the script as-is, open up a file named: check-ssl-certs.sh
and save the above into it. Don’t forget to set the execute bit on the file with: chmod +x check-ssl-certs.sh
though or you won’t be able to execute it directly from the command line.
Initialisation
To begin, the first block of code handles all of our settings:
1 | hosts=("logicalmoon.com") |
hosts
is a array of all the domains I want to check. If you want to add some more, separate them with a space, and some more quotes like this: hosts=("domain.com" domain2.com")
etc.
port
is the HTTPS port number and will most likely be 443
as I have set it.
tmp_file
contains the path of a temporary file, which will hold our report. mktemp
is great in that it will take care of finding an unused filename we can use, and outputs that filename onto the console. We capture the name by using back ticks into the variable tmp_file
.
Lastly, email_to
is a comma separated list of email addresses of people that should receive the report. Other than hosts
you will also definitely want to change this.
The Main Body
1 | for host in "${hosts[@]}"; do |
This is the main loop of the script and its effect is to go through each domain, one by one, setting host
equal to that particular domain.
"${hosts[@]}"
is a way to expand the array like so:
1 | $ echo "${hosts[@]}" |
Actually finding out the date which the SSL certificate is to expire happens next and for this, I drew heavily on this excellent post by Mohamed Ibrahim.
1 | date=`echo | |
Go read that post to get some of the finer details but my additions are that I am looking for the notAfter
string and stripping out the date, which I add to my date field. This is better explained with some example output. Here’s the code:
1 | $echo | |
And here’s what would happen if we used my domain: logicalmoon.com
1 | $ echo | openssl s_client -connect logicalmoon.com:443 2>/dev/null | openssl x509 -dates -noout |
The date that matters most to me is the notAfter
one since that is my expiry date (err, actually, I’d better keep an eye on that!). Let’s take just that line, excluding the other.
1 | $ echo | openssl s_client -connect logicalmoon.com:443 2>/dev/null | openssl x509 -dates -noout | grep notAfter |
Now we want to use just the date portion, so we need to split the string at the equals sign =
and grab the second field. That’s what cut
does next:
1 | $ echo | openssl s_client -connect logicalmoon.com:443 2>/dev/null | openssl x509 -dates -noout | grep notAfter | cut -d"=" -f2 |
Lastly, we assign the output to my variable date
as we have done before.
The remainder of the loop simply appends the results into a file, and sleeps for a second. My use of \r\n
is because this will end up on a Windows machine and I want the line breaks as part of the output.
Final Steps
Almost done. Here’s the last of the script and my line by line explanation:
1 | mail -s "SSL Certs" $email_to < $tmp_file |
The first line sends a mail with a subject of SSL Certs
to the list of email addresses in $email_to
using our temporary file as the body contents.
We then remove our temporary file - all good scouts clean up after themselves!
Finally, we reschedule this very same script to run at 1pm in a week’s time. This is a cheeky way of ensuring a script keeps executing on a schedule, periodically. Alternatively, I could have used Cron, but this is user level so simpler and requires fewer permissions.
The Results
Here’s an example output that I received via email; I’ve masked the real domains I am checking, but everything else is approximately as you would find if you ran this.
1 | Aug 12 09:20:29 2021 GMT domain1.com |
Next Steps
To improve this further, you could:
- Sort the dates - earliest to expire, first
- Use Cron instead of the
at
queue - Reformat the dates - do the times really matter? I dont think so
- Use an alternative language - how about Python?
Anyway, hope this helps someone should they need to do this.
Hi! Did you find this useful or interesting? I have an email list coming soon, but in the meantime, if you ready anything you fancy chatting about, I would love to hear from you. You can contact me here or at stephen ‘at’ logicalmoon.com