aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xacmeman87
1 files changed, 39 insertions, 48 deletions
diff --git a/acmeman b/acmeman
index a747a47..84c492b 100755
--- a/acmeman
+++ b/acmeman
@@ -31,7 +31,7 @@ use Getopt::Long qw(:config gnu_getopt no_ignore_case);
use POSIX qw(strftime time floor);
use Data::Dumper;
-our $VERSION = '1.00';
+our $VERSION = '1.01';
=head1 NAME
@@ -40,7 +40,7 @@ acmeman - manages ACME certificates
=head1 SYNOPSIS
B<acmeman>
-[B<-Fadn>]
+[B<-Fadns>]
[B<-D> I<N>]
[B<-f> I<FILE>]
[B<-l> B<slackware>|B<debian>|B<rh>]
@@ -50,10 +50,11 @@ B<acmeman>
[B<--dry-run>]
[B<--force>]
[B<--layout=>B<slackware>|B<debian>|B<rh>]
+[B<--stage>]
[B<--time-delta=>I<N>]
[I<DOMAIN>...]
-B<acmeman> B<--setup> | B<-s>
+B<acmeman> B<--setup> | B<-S>
[B<-Fdn>]
[B<-f> I<FILE>]
[B<-l> B<slackware>|B<debian>|B<rh>]
@@ -254,14 +255,17 @@ B<slackware>, B<debian>, and B<rh> (for Red Hat).
=item B<-n>, B<--dry-run>
-With B<--setup>, don't actually write anything, just print what would
-have been done. Otherwise, use LetsEncrypt staging server, instead of
-production. Implies B<--debug>.
-
-=item B<-s>, B<--setup>
+Don't modify any files, just print what would have been done.
+Implies B<--debug>.
+
+=item B<-S>, B<--setup>
Set up the B<acmeman> infrastructure files.
+=item B<-s>, B<--stage>
+
+Use LetsEncrypt staging server.
+
=back
The following options are informational:
@@ -293,7 +297,7 @@ my $progname = basename($0);
my $progdescr = "manages ACME certificates";
my $debug;
my $dry_run;
-my $acme_host = 'acme-staging.api.letsencrypt.org';
+my $acme_host = 'prod';
my %acme_endpoint = (prod => 'acme-v01.api.letsencrypt.org',
staging => 'acme-staging.api.letsencrypt.org');
my $letsencrypt_root_cert_url =
@@ -358,7 +362,7 @@ sub make_filename {
sub prep_dir {
my $dir = dirname(shift);
if (! -d $dir) {
- debug(2, "creating directory $dir");
+ debug(3, "creating directory $dir");
my @created = make_path("$dir", { error => \my $err } );
if (@$err) {
for my $diag (@$err) {
@@ -390,7 +394,7 @@ sub make_csr {
$req->sign();
if (exists($filename_pattern{key})) {
my $filename = make_filename('key', $cn);
- debug(2, "writing $filename");
+ debug(3, "writing $filename");
prep_dir($filename);
my $u = umask(077);
$req->write_pem_pk($filename);
@@ -404,7 +408,7 @@ sub save_crt {
my $domain = shift;
my $filename = make_filename($type, $domain);
- debug(2, "writing $filename");
+ debug(3, "writing $filename");
prep_dir($filename);
open(my $fd, '>', $filename);
@@ -426,36 +430,26 @@ sub domain_cert_expires {
if (exists($exts->{subjectAltName})) {
my $msg = $check_alt_names
? 'will renew' : 'use -a to trigger renewal';
-
# FIXME: Crypt::OpenSSL::X509 returns extensions as strings,
# instead of as ASN.1 objects. Until it is fixed, the
# following naive logic is implemented to split the string into
# names:
- my @names = split /\.\.+/, $exts->{subjectAltName}->value();
- # First value is irrelevant
- shift @names;
- # Prepare sorted arrays of alternative names and requested
- # names (@vh).
- @names = sort @names;
- my @vh = sort ($domain, @_);
- # Compare them
- if ($#vh != $#names) {
- debug(1, "$crt: number of subject names changed; $msg");
- return 1 if $check_alt_names;
- } else {
- for (my $i = 0; $i <= $#names; $i++) {
- if ($names[$i] ne $vh[$i]) {
- debug(1, "$crt: subject names differ; $msg");
- if ($check_alt_names) {
- return 1;
- } else {
- last;
- }
- }
+ my $blob = $exts->{subjectAltName}->value();
+ my @missing;
+ foreach my $vh (sort { length($b) <=> length($a) }
+ uniq($domain, @_)) {
+ unless ($blob =~ s/\Q$vh\E\b//) {
+ push @missing, $vh;
}
}
+ if (@missing) {
+ debug(1, "$crt: the following SANs are missing: "
+ . join(', ', @missing)
+ ."; $msg");
+ return 1 if $check_alt_names;
+ }
}
-
+
my $expiry = $x509->notAfter();
my $strp = DateTime::Format::Strptime->new(
@@ -475,14 +469,14 @@ sub domain_cert_expires {
} else {
$in = "today";
}
- debug(1, "$crt expires on $expiry, $in");
+ debug(2, "$crt expires on $expiry, $in");
if ($now + $time_delta < $ts) {
return 0;
} else {
- debug(1, "will renew $crt (expires on $expiry, $in)");
+ debug(2, "will renew $crt (expires on $expiry, $in)");
}
} else {
- debug(1, "will renew $crt");
+ debug(2, "will renew $crt");
}
}
return 1;
@@ -490,7 +484,7 @@ sub domain_cert_expires {
sub register_domain_certificate {
my $domain = shift;
-
+
if ($debug) {
my $crt = make_filename('cert', $domain);
if (-f $crt) {
@@ -499,9 +493,10 @@ sub register_domain_certificate {
debug(1, "issuing $crt: CN=$domain, alternatives=@_");
}
}
+ return if $dry_run;
my $acme = Protocol::ACME->new(
- host => $acme_host,
+ host => $acme_endpoint{$acme_host},
account_key => {
buffer => $account_key->get_private_key_string(),
format => 'PEM' },
@@ -803,7 +798,7 @@ EOT
sub coalesce {
my $ref = shift;
- debug(1, "coalescing virtual hosts");
+ debug(2, "coalescing virtual hosts");
my $i = 0;
my @vhost;
foreach my $ent (sort { $a->{names}[0] cmp $b->{names}[0] }
@@ -860,6 +855,7 @@ GetOptions("h" => sub {
},
"debug|d+" => \$debug,
"dry-run|n" => \$dry_run,
+ "stage|s" => sub { $acme_host = 'staging' },
"force|F" => \$force,
"time-delta|D=n" => \$time_delta,
"layout|l=s" => sub {
@@ -868,19 +864,14 @@ GetOptions("h" => sub {
unless exists $apache_layout_tab{$arg};
$apache_layout = $apache_layout_tab{$arg};
},
- "setup|s" => \$setup,
+ "setup|S" => \$setup,
"config-file|f=s" => sub {
$apache_layout = { config => $_[1] }
},
"alt-names|a" => \$check_alt_names
) or exit(EX_USAGE);
-if ($dry_run) {
- ++$debug;
- $acme_host = $acme_endpoint{staging};
-} else {
- $acme_host = $acme_endpoint{prod};
-}
+++$debug if $dry_run;
unless (defined($apache_layout)) {
while (my ($name, $layout) = each %apache_layout_tab) {

Return to:

Send suggestions and report system problems to the System administrator.