Last commit for renew.pl: b7408908e246ab888b84d2f8ae31c99f41cf5508

Add --config option

Piotr Pawlow [2016-04-14 16:02:44]
Add --config option
#!/usr/bin/perl
package SPIN::LE;

use strict;
use warnings;
use autodie qw(:all);
use Date::Parse;
use LWP::UserAgent;
use Getopt::Long;

{
  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';
  our $config_file;

  GetOptions("config=s" => \$config_file);
  require $config_file if defined $config_file;

  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();
ViewGit