summaryrefslogtreecommitdiff
path: root/renew.pl
diff options
context:
space:
mode:
Diffstat (limited to 'renew.pl')
-rwxr-xr-xrenew.pl120
1 files changed, 120 insertions, 0 deletions
diff --git a/renew.pl b/renew.pl
new file mode 100755
index 0000000..2cdb8e8
--- /dev/null
+++ b/renew.pl
@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+package SPIN::LE;
+
+use strict;
+use warnings;
+use autodie qw(:all);
+use Date::Parse;
+use LWP::UserAgent;
+
+{
+ our @restart_services_cmd = qw(sudo service httpd graceful);
+ our @openssl_bin = qw(/usr/bin/openssl);
+ our @acme_tiny_bin = qw(/usr/bin/python acme-tiny/acme_tiny.py);
+ our $account_key_path = 'data/account.key';
+ our $challenges_dir = 'data/challenges/';
+ our $days_left_limit = 14;
+ our $csr_dir = 'data';
+
+ require 'config.pm' if -e 'config.pm';
+
+ sub get_intermediate_cert_url {
+ my $crt_fn = shift;
+ open my $openssl, '-|', @openssl_bin, 'x509', '-in', $crt_fn, '-text', '-noout';
+ while(my $line = <$openssl>) {
+ return $1 if $line =~ m|^\s*CA Issuers - URI:(.*?)\s*$|;
+ }
+ die("Cert URL not found");
+ }
+
+ sub get_intermediate_cert {
+ my $crt_fn = shift;
+ my $pem_fn = shift;
+ my $der_fn = "$pem_fn.tmp";
+ my $url = get_intermediate_cert_url($crt_fn);
+ my $ua = LWP::UserAgent->new;
+ my $response = $ua->mirror($url, $der_fn);
+ die("Failed to download $url\n") if !($response->is_success);
+ system(@openssl_bin, 'x509', '-inform', 'der', '-in', $der_fn, '-out', $pem_fn);
+ unlink($der_fn);
+ }
+
+ sub get_cert {
+ my ($csr_fn, $crt_fn, $int_crt_fn) = @_;
+ open my $acme_tiny, '-|', @acme_tiny_bin,
+ '--account-key', $account_key_path, '--csr', $csr_fn,
+ '--acme-dir', $challenges_dir;
+ my $cert_start = 0;
+ my $cert_end = 0;
+ my $lines = '';
+ while (my $line = <$acme_tiny>) {
+ print $line;
+ $cert_start = 1 if $line =~ /^-----BEGIN CERTIFICATE-----$/;
+ $cert_end = 1 if $line =~ /^-----END CERTIFICATE-----$/;
+ $lines .= $line;
+ }
+ if ($cert_start && $cert_end) {
+ my $tmp_fn = "$crt_fn.tmp";
+ my $int_tmp_fn = "$int_crt_fn.tmp";
+ open my $crt, '>', $tmp_fn;
+ print $crt $lines or die("Write failed: $!\n");
+ close $crt;
+ get_intermediate_cert($tmp_fn, $int_tmp_fn);
+ rename $tmp_fn,$crt_fn;
+ rename $int_tmp_fn,$int_crt_fn;
+ } else {
+ die("Output doesn't look like a certificate\n");
+ }
+ }
+
+ sub renew {
+ my %fmap = @_;
+ my $certs_updated = 0;
+ for my $csr_fn (keys %fmap) {
+ my $crt_fn = $fmap{$csr_fn}->[0];
+ my $int_crt_fn = $fmap{$csr_fn}->[1];
+ if (!-e $crt_fn) {
+ print "Getting new certificate $crt_fn\n";
+ get_cert($csr_fn, $crt_fn, $int_crt_fn);
+ next;
+ }
+ open my $openssl, '-|', @openssl_bin, 'x509', '-enddate', '-noout', '-in', $crt_fn;
+ my $ret = <$openssl>;
+ if ($ret =~ /^notAfter=(.*)$/) {
+ my $date_str = $1;
+ my $time = str2time($date_str);
+ if (defined $time) {
+ if ($time - time < $days_left_limit*24*60*60) {
+ my $days = (($time - time)/60/60/24)|0;
+ print "Certificate $crt_fn ";
+ if ($days >= 1) { print "will expire in $days days" }
+ elsif ($days == 0) { print "will expire today" }
+ elsif ($days == -1) { print "expired yesterday" }
+ else { printf "expired %d days ago", -$days };
+ print "\nRenewing...\n";
+ get_cert($csr_fn, $crt_fn, $int_crt_fn);
+ $certs_updated++;
+ }
+ } else {
+ warn "Failed parsing expiration date for $crt_fn: openssl returned $ret\n";
+ }
+ } else {
+ warn "Failed getting expiration date for $crt_fn: openssl returned $ret\n";
+ }
+ }
+ return $certs_updated;
+ }
+
+ sub run {
+ opendir(my $dh, $csr_dir);
+ my %fmap =
+ map { my $base = $_; $base =~ s/\.csr$//; $_ => [ "$base.crt", "$base-int.crt" ] }
+ map { "$csr_dir/$_" }
+ grep { /\.csr$/ }
+ readdir $dh;
+
+ system(@restart_services_cmd) if renew(%fmap);
+ }
+}
+
+__PACKAGE__->run() unless caller();