Monitoring Website SSL/TLS Certificate Expiration Times with R, {openssl}, {pushoverr}, and {DT}
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
macOS R users who tend to work on the bleeding edge likely noticed some downtime at
The Basic Process
The {openssl} package has a handy function — download_ssl_cert()
— which will, by default, hit a given host on the standard HTTPS port (443/TCP) and grab the site certificate and issuer. We’ll grab the “validity end” field and convert that to a date to use for comparison.
To get the target list of sites to check I used Rapid7’s FDNS data set and a glance at a few certificate transparency logs to put together a current list of “r-project” domains that have been known to have SSL certs. This process could be made more dynamic, but things don’t change that quickly in r-project domain land.
Finally, we use the {DT} package to build a pretty HTML table and the {pushoverr} package to send notifications at normal priority for certs expiring within a week and critical priority for certs that have expired (the package has excellent documentation which will guide you through setting up a Pushover account).
I put this all in a plain R script named r-project-ssl-notify.R
that’s then called from a Linux CRON job which runs:
/usr/bin/Rscript -e 'rmarkdown::render(input="PATH_TO/r-project-ssl-notify.R", output_file="PATH_TO/r-project-cert-status/index.html", quiet=TRUE)'
once a day at 0930 ET to make this status page and also fire off any notifications which I have going to my watch and phone (I did a test send by expanding the delta to 14 days):
Here’s the contents of
#' --- #' title: "r-project SSL/TLS Certificate Status" #' date: "`r format(Sys.time(), '%Y-%m-%d')`" #' output: #' html_document: #' keep_md: false #' theme: simplex #' highlight: monochrome #' --- #+ init, include=FALSE knitr::opts_chunk$set( message = FALSE, warning = FALSE, echo = FALSE, collapse=TRUE ) #+ libs library(DT) library(openssl) library(pushoverr) library(tidyverse) # Setup ----------------------------------------------------------------------------------------------------------- # This env config file contains two lines: # # PUSHOVER_USER=YOUR_PUSHOVER_USER_STRING # PUSHOVER_APP=YOUR_PUSHOVER_APP_KEY # # See the {pushoverr} package for how to setup your Pushover account readRenviron("~/jobs/conf/r-project-ssl-notify.env") # Check certs ----------------------------------------------------------------------------------------------------- # r-project.org domains retrieved from Rapid7's FDNS data set # (https://opendata.rapid7.com/sonar.fdns_v2/) and cert transparency logs #+ work c( "cloud.r-project.org", "cran.at.r-project.org", "cran.ch.r-project.org", "cran.es.r-project.org", "cran.uk.r-project.org", "cran.us.r-project.org", "lists.r-forge.r-project.org", "mac.r-project.org", "r-project.org", "translation.r-project.org", "user2014.r-project.org", "user2016.r-project.org", "user2018.r-project.org", "user2019.r-project.org", "user2020.r-project.org", "user2020muc.r-project.org", "www.user2019.fr" ) -> r_doms # grab each cert r_certs <- map(r_doms, openssl::download_ssl_cert) # make a nice table tibble( dom = r_doms, expires = map_chr(r_certs, ~.x[[1]][["validity"]][[2]]) %>% # this gets us the "validity end" as.Date(format = "%b %d %H:%M:%S %Y", tz = "GMT"), # and converts it to a date object delta = as.numeric(expires - Sys.Date(), "days") # this computes the delta from the day this script was called ) %>% arrange(expires) -> r_certs_expir # Status page generation ------------------------------------------------------------------------------------------ # output nice table DT::datatable(r_certs_expir, list(pageLength = nrow(r_certs_expir))) # if the # of r-proj doms gets too large we'll cap this for pagination # Notifications --------------------------------------------------------------------------------------------------- # See if we need to notify abt things expiring within 1 week # REMOVE THIS or edit the delta max if you want less noise one_week <- filter(r_certs_expir, between(delta, 1, 7)) if (nrow(one_week) > 0) { pushover_normal( title = "There are r-project SSL Certs Expiring Within 1 Week", message = "Check which ones: https://rud.is/r-project-cert-status" ) } # See if we have expired certs expired <- filter(r_certs_expir, delta <= 0) if (nrow(expired) > 0) { pushover_critical( title = "There are expired r-project SSL Certs!", message = "Check which ones: https://rud.is/r-project-cert-status" ) }
FIN
With just a tiny bit of R code we have the ability to monitor expiring SSL certs via a diminutive status page and alerts to any/all devices at our disposal.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.