Les certificats SSL, c’est un peu le Point Of Failure du web. Si vous n’êtes pas un admin rigoureux et ne les monitorez pas, vous pouvez passer à côté d’un renouvellement et planter votre site ou votre app.

Note : Si vous utilisez Let’s Encrypt, vous pouvez tout de même mettre à profit cet article, mais vous pouvez aussi mettre en place un cron de renouvellement automatique 😄

En perl, il existe des tas de modules dont le très cool Net::SSL::ExpireDate, permettant de faire des scripts très courts et simples, pour éviter cette situation. Vous pouvez même les coupler à une sonde Centreon/Nagios pour les inclure dans votre supervision. Démo.

Tout d’abord, il faut installer le module, soit par votre gestionnaire de packages favori, soit via cpanm. Vous pouvez vérifier que le module est bien présent dans votre environnement perl standard via la commande suivante :

$ perl -MNet::SSL::ExpireDate -e "print("OK\n")"

Elle peut répondre soit OK, et on est bon pour la suite, soit :

Can't locate Net/SSL/ExpireDate.pm in @INC (you may need to install the Net::SSL::ExpireDate module) (@INC contains: /home/kharec/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux /home/kharec/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 /home/kharec/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2/x86_64-linux /home/kharec/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2).
BEGIN failed--compilation aborted.

Et il faudra revoir l’installation. Une fois installé, la base de notre script est le chargement de ce module :

#!perl -w
use strict;

use Net::SSL::ExpireDate;

Notez au passage qu’utiliser un shebang relatif nous permet d’utiliser la version de perl liée à notre environnement. Très utile si celle-ci n’est pas celle du système (notamment pour des raisons de versions), comme dans le cas de perlbrew.

On construit ensuite notre objet, en instanciant ledit module avec en argument, l’url ou l’ip de l’application dont on veut monitorer le certificat.

my $ssl = Net::SSL::ExpireDate->new( https => 'blog.kharec.info' );

D’autres versions de ce constructeur sont possibles pour d’autres protocoles que le HTTPS, j’y reviens plus tard. Une fois le module instancié, nous avons deux méthodes principales à notre disposition : expire_date et is_expired.

La première nous indique sa date de fin de validité, pas très utile à des fins de monitoring. La deuxième peut par contre nous indiquer par un retour booléen si le certificat expire dans tant de temps :

if ($ssl->is_expired('30 days')) {
    return('WARNING');
}

Plutôt simple d’utilisation. On peut donc facilement imaginer une sonde de monitoring prenant en argument une url et remontant un warning si le certificat expire dans moins de 30 jours, puis un critical si le certificat expire dans moins de 15 jours. Ça aurait cette allure :

#!perl -w
use strict;

use Net::SSL::ExpireDate;

die("No url found, please provide one.") if !@ARGV;

my $ssl = Net::SSL::ExpireDate->new( https => $ARGV[0] );

if ($ssl->is_expired('15 days')) {
    return('CRITICAL');
}
elsif ($ssl->is_expired('30 days')) {
    return('WARNING');
}

exit(0);

Il est nécessaire évidemment de commencer par la valeur la plus faible, pour rentrer dans la condition, question de logique : si le certificat expire dans moins de 15 jours, il expire forcément dans moins de 30 jours.

Deuxième utilisation possible de ce module, avec la méthode expire_date qui permet (cap’tain obviouuuus) de nous retourner la date d’expiration d’un certificat pour une url donnée :

#!perl -w
use strict;

use Net::SSL::ExpireDate;
my $ssl;
die("No url found, please provide one.") if !@ARGV;
print("ssl,date\n");

for(@ARGV) {
    $ssl = Net::SSL::ExpireDate->new( https => $_ );
    print($_.",".$ssl->expire_date()."\n");
}

exit(0);

Ce script bouclera sur la liste d’arguments données, des urls donc, et nous donnera les dates d’expirations de chaque dans un joli csv fait à la mano.

$ ./ssl.pl "blog.kharec.info" "cv.sandrocazzaniga.fr"
ssl,date
blog.kharec.info,2020-12-27T12:00:00
cv.sandrocazzaniga.fr,2021-03-09T12:00:00

En début d’article, j’ai dit que je reviendrais plus tard sur les autres protocoles et méthodes disponibles. Cette modification se fait dans le constructeur uniquement et simplement, le reste du script est identique. Quelques exemples :

# pour une app qui serait accessible en HTTPS mais sur un autre port que le 443
$ssl = Net::SSL::ExpireDate->new( https => 'ma.super.app:8443' );

# pour un serveur autre que https (smtp par exemple) utilisant du ssl
$ssl = Net::SSL::ExpireDate->new( ssl => 'mail.toto.fr:465' );

# sur un certificat lui-même
$ssl = Net::SSL::ExpireDate->new( file  => './ssl/wildcard.toto.app.pem' );

Et en cadeau, quelques one-liners :

# pour un site https
$ perl -MNet::SSL::ExpireDate -e 'print(Net::SSL::ExpireDate->new(https => $ARGV[0])->expire_date()."\n");' blog.kharec.info 

# pour une app utilisant du ssl autre que https
$ perl -MNet::SSL::ExpireDate -e 'print(Net::SSL::ExpireDate->new(ssl => $ARGV[0])->expire_date()."\n");' smtp.toto.fr

# pour un ssl en mode fichier
$ perl -MNet::SSL::ExpireDate -e 'print(Net::SSL::ExpireDate->new(file => $ARGV[0])->expire_date()."\n");' /etc/apache/ssl/ssl.pem

Non, perl n’est pas mort. Oui, perl c’est cool.

Continuez de l’utiliser 😄