From 2f7d94071d9825a38f41f552dd0cf75d8c563d5c Mon Sep 17 00:00:00 2001 From: JTSage Date: Fri, 27 Nov 2020 12:36:16 -0500 Subject: [PATCH] Add "certbot-certs" This plugin will semi-auto discover certbot (letsencrypt) certificates and output their expiry in days. --- plugins/ssl/certbot_certs | 156 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100755 plugins/ssl/certbot_certs diff --git a/plugins/ssl/certbot_certs b/plugins/ssl/certbot_certs new file mode 100755 index 00000000..2eacbc6d --- /dev/null +++ b/plugins/ssl/certbot_certs @@ -0,0 +1,156 @@ +#!/usr/bin/perl +# -*- perl -*- + +=encoding utf8 + +=head1 NAME + +certbot_certs - Munin plugin to certbot (letsencrypt) certificate expiry + +=head1 APPLICABLE SYSTEMS + +Servers running certbot certificates. This script auto-discovers the current set of certificates + +=head1 CONFIGURATION + +This script requires root permissions to run, in your munin-node configuration: + + [certbot_certs] + user root + +=head1 CAPABILITIES + +This plugin supports L + +=head1 LICENSE + +Copyright (C) 2018 J.T.Sage (jtsage@gmail.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see L. + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities= + +=cut + +use warnings; +use strict; +use Munin::Plugin; +use Date::Parse; +use POSIX; + +if ( defined($ARGV[0]) && $ARGV[0] eq "autoconf" ) { + if ( -e "/usr/bin/openssl" ) { + print "no (Root User Required)\n"; + } else { + print "no (OpenSSL Not Found)\n"; + } + exit; +} + + +my %thecerts = get_data(); + + +if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { + # Do the config step for each set of graphs + do_config(%thecerts); + + # If dirtyconfig is not supported, or turned off, exit here. Otherwise, continue to fetch section + if ( !defined($ENV{'MUNIN_CAP_DIRTYCONFIG'}) || !$ENV{'MUNIN_CAP_DIRTYCONFIG'} ) { exit 0; } +} + +# Do the fetch step for each set of graphs +do_values(%thecerts); + + + +sub do_config { + print "graph_title LetsEncrypt Certificate Expiry\n"; + print "graph_args --upper-limit 90 -l 0\n"; + print "graph_vlabel days\n"; + print "graph_category security\n"; + print "graph_info This graph shows the number of days until certificate expiry\n"; + + my ( %certs ) = @_; + + foreach my $key ( keys %certs ) { + print $certs{$key}{"safename"} . ".label " . $certs{$key}{"name"} . "\n"; + print $certs{$key}{"safename"} . ".info " . $certs{$key}{"info"} . "\n"; + print $certs{$key}{"safename"} . ".warning 29:\n"; + print $certs{$key}{"safename"} . ".critical 15:\n"; + } +} + +sub do_values { + my ( %certs ) = @_; + + foreach my $key ( keys %certs ) { + print $certs{$key}{"safename"} . ".value " . $certs{$key}{"expdays"} . "\n"; + } +} + +sub get_data { + my $runtime = time(); + my %cert_files; + # Get Renewal Files + opendir(DIR, "/etc/letsencrypt/renewal"); + my @renew_files = grep(/\.conf$/,readdir(DIR)); + closedir(DIR); + + # Each renewal file + foreach my $file (@renew_files) { + my @this_renew = `cat /etc/letsencrypt/renewal/$file`; + + my $cert_name = $file; + $cert_name =~ s/\.conf//; + + my $safe_name = $cert_name; + $safe_name =~ s/\./_/g; + + + $cert_files{$cert_name}{"name"} = $cert_name; + $cert_files{$cert_name}{"safename"} = $safe_name; + + # cert is listed + foreach my $line ( @this_renew ) { + if ( $line =~ /cert = (.+?)$/ ) { + $cert_files{$cert_name}{"cert"} = $1; + } + } + + # use openssl for the info + my @ssl_info = `openssl x509 -noout -in $cert_files{$cert_name}{"cert"} -text -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux -enddate`; + + # parse for SAN and expiry + foreach my $line ( @ssl_info ) { + if ( $line =~ /notAfter=(.+?)$/ ) { + $cert_files{$cert_name}{"expdate"} = $1; + $cert_files{$cert_name}{"expdays"} = floor((str2time($1) - $runtime)/(60*60*24)); + } + if ( $line =~ /(DNS:.+?)$/ ) { + my $san_line = $1; + $san_line =~ s/DNS://g; + $cert_files{$cert_name}{"info"} = $san_line; + } + } + } + + return %cert_files; + +} + +