IT Operations teams frequently have the responsibility to ensure that [SSL certificates](https://en.wikipedia.org/wiki/Public_key_certificate) for various websites are valid and renewed on a regular basis. While SSL certificate vendors often provide reminders and warnings when the certificates are about to expire, this is not always effective–especially when a variety of different SSL vendors have been used, or different parties are responsible for purchasing and maintaining the certificate.
To prevent SSL certificate expirations from going unnoticed, I wrote an application that checks the certificates from a variety of sites and ensures that they will remain valid for a certain number of days in the future.
In order to track the different sites, we set up integration with [Dead Man’s Snitch](https://deadmanssnitch.com/) (DMS). This ensures that SSL certificate checks are being performed and that the results are valid. If a check does not execute or does not return a valid response (because the certificate will be expiring within a configured look-ahead period), DMS creates an alert and lets me know that action is required.
## The Concept
The application takes a set of hostnames and associated DMS snitch IDs, retrieves the SSL certificate for each host, evaluates if the SSL certificate’s expiration date is valid within the configured look-ahead period, and then notifies the DMS snitch if the expiration date is valid. It’s intended to be run on a regular basis–at least daily. The configured look-ahead period should be long enough to ensure that any expiring certificates can be renewed in time to avoid a service interruption.
The application uses `SSLSocket` from Ruby’s `OpenSSL` library to retrieve the SSL certificate from a given host:
tcp_socket = TCPSocket.new('atomicobject.com', 443) ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket) ssl_socket.hostname='atomicobject.com' ssl_socket.connect certificate = ssl_socket.peer_cert ssl_socket.sysclose tcp_socket.close
It returns an `OpenSSL::X509::Certificate` object:
=> #<OpenSSL::X509::Certificate: subject=#<OpenSSL::X509::Name:0x007f8769015cc8>, issuer=#<OpenSSL::X509::Name:0x007f8769015ca0>, serial=#<OpenSSL::BN:0x007f8769015c78>, not_before=2015-04-24 00:00:00 UTC, not_after=2016-04-24 23:59:59 UTC>
The SSL certificate expiration date is stored in the `not_after` attribute:
expiration = OpenSSL::X509::Certificate.new(certificate).not_after.to_datetime
=> #<DateTime: 2016-04-24T23:59:59+00:00 ((2457503j,86399s,0n),+0s,2299161j)>
If the certificate expiration date is further into the future than our configured look-ahead date, then the SSL certificate is considered valid, and the associated DMS snitch is notified.
For example, if we configured our look-ahead period to be four weeks (28 days), the expiration date would be considered valid:
look_ahead = DateTime.now + 28 expiration > look_ahead => true
If we configured our look-ahead period to be four months (120 days), the expiration date would not be considered valid:
look_ahead = DateTime.now + 120 expiration > look_ahead => false
## The Application
I’ve published the [application on GitHub](https://github.com/atomicobject/ssl_certificate_expiration_checker). You can test it out by cloning it. All you need to do is add a `config.yml` following the example provided in `config.yml.example`.
There are two `rake` tasks: one that simply performs checks, and another that performs checks and notifies the configured DMS snitch. The current application only sends notifications to DMS, but it could be easily extended to notify other services. Our default look-ahead period is two weeks (14 days), but that can be altered within the `Rakefile`.
A `config.yml` like:
- host: expired.atomicobject.com comment: "dummy certificate" port: 443 snitch: aabbccddff - host: atomicobject.com comment: "primary certificate" port: 443 snitch: aabbccddee
will check the SSL certificates for ‘expired.atomicobject.com’ and ‘atomicobject.com’. The following is sample output from running `bundle exec rake ssl:check_and_notify`:
E, [2016-01-10T13:06:13.617778 #9772] ERROR -- : Certificate for expired.atomicobject.com is expired. I, [2016-01-10T13:06:13.807550 #9772] INFO -- : Certificate for atomicobject.com is within expiry. I, [2016-01-10T13:06:13.807608 #9772] INFO -- : Notifying snitch aabbccddee I, [2016-01-10T13:06:14.011433 #9772] INFO -- : Notifying snitch aabbccddee succeeded.
Running `bundle exec rake ssl:check` will perform the checks without notifying DMS.
This application helps ensure that the SSL certificates for all of Atomic’s sites and many of our client sites are kept current and not allowed to expire. Whenever one of the checks for a SSL certificate fails, we are immediately notified. This gives us time to begin the process of renewing the certificate so that we can install it on the necessary servers well ahead of the expiration date.
This application has been extremely valuable to us, and I hope it will be useful to others as well.