diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2019-12-23 15:12:31 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2019-12-23 15:12:31 +0200 |
commit | 438e1817a21dd79024cdf33ecc630c5bb0dc8cf0 (patch) | |
tree | 5f4cb20e89764e5ad294a50af5f2e7d538ce3bda /lib | |
parent | 4185f852471106b55ba430b81a1c5f09decdf16e (diff) | |
download | acmeman-438e1817a21dd79024cdf33ecc630c5bb0dc8cf0.tar.gz acmeman-438e1817a21dd79024cdf33ecc630c5bb0dc8cf0.tar.bz2 |
Define envvars prior to running postrenew. Other improvements.
* Changes: Update.
* acmeman: Document changes.
* lib/App/Acmeman.pm: Version 3.02.90.
Pass information about renewed certificate to postrenew commands
in environment variables.
Remove created challenge files when no longer needed.
* lib/App/Acmeman/Config.pm (mangle): Improve error message.
Use "default" domain source by default.
* lib/App/Acmeman/Source/Apache.pm: Minor changes.
* lib/App/Acmeman/Source/Default.pm: New file. Source for the
"default" domain source.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/App/Acmeman.pm | 39 | ||||
-rw-r--r-- | lib/App/Acmeman/Config.pm | 11 | ||||
-rw-r--r-- | lib/App/Acmeman/Source/Apache.pm | 20 | ||||
-rw-r--r-- | lib/App/Acmeman/Source/Default.pm | 48 |
4 files changed, 99 insertions, 19 deletions
diff --git a/lib/App/Acmeman.pm b/lib/App/Acmeman.pm index 76b8fb1..c6e9497 100644 --- a/lib/App/Acmeman.pm +++ b/lib/App/Acmeman.pm @@ -24,7 +24,7 @@ use Text::ParseWords; use App::Acmeman::Log qw(:all :sysexits); use feature 'state'; -our $VERSION = '3.02'; +our $VERSION = '3.02.90'; my $progdescr = "manages ACME certificates"; @@ -307,20 +307,31 @@ sub renew { } $self->coalesce; - my $renewed = 0; + my @renewed; foreach my $vhost ($self->selected_domains) { if ($self->force_option || $self->domain_cert_expires($vhost)) { if ($self->register_domain_certificate($vhost)) { if (my $cmd = $vhost->postrenew) { - $self->runcmd($cmd); + local $ENV{ACMEMAN_CERTIFICATE_FILE} = + $vhost->certificate_file; + local $ENV{ACMEMAN_DOMAIN_NAME} = $vhost; + local $ENV{ACMEMAN_ALT_NAMES} = join(' ', $vhost->alt); + $self->runcmd($cmd, $vhost); } else { - $renewed++; + push @renewed, $vhost; } } } } - if ($renewed) { + if (@renewed) { + local $ENV{ACMEMAN_CERTIFICATE_COUNT} = @renewed; + local $ENV{ACMEMAN_CERTIFICATE_FILE} = + join(' ', map { $_->certificate_file } @renewed); + local $ENV{ACMEMAN_DOMAIN_NAME} = + join(' ', map { "$_" } @renewed); + local $ENV{ACMEMAN_ALT_NAMES} = + join(' ', map { ($_->alt) } @renewed); if ($self->cf->is_set(qw(core postrenew))) { foreach my $cmd ($self->cf->get(qw(core postrenew))) { $self->runcmd($cmd); @@ -396,6 +407,19 @@ sub debug_to_loglevel { return $lev[$v > $#lev ? $#lev : $v]; } +my @challenge_files; + +END { + if (@challenge_files) { + debug(3, "removing challenge files"); + my $n = unlink @challenge_files; + unless ($n == @challenge_files) { + error("some challenge files were not removed", + prefix => 'warning'); + } + } +} + sub save_challenge { my ($self,$challenge) = @_; my $file = File::Spec->catfile($self->cf->get(qw(core rootdir)), $challenge->get_path); @@ -403,6 +427,7 @@ sub save_challenge { print $fh $self->acme->make_key_authorization($challenge); close $fh; debug(3, "wrote challenge file $file"); + push @challenge_files, $file; } else { error("can't open $file for writing: $!"); die; @@ -614,8 +639,8 @@ sub save_crt { } sub runcmd { - my ($self,$cmd) = @_; - debug(3, "running $cmd"); + my ($self,$cmd,$domain) = @_; + debug(3, "running $cmd".($domain ? " for $domain" : "")); unless ($self->dry_run_option) { system($cmd); if ($? == -1) { diff --git a/lib/App/Acmeman/Config.pm b/lib/App/Acmeman/Config.pm index da60700..7c05985 100644 --- a/lib/App/Acmeman/Config.pm +++ b/lib/App/Acmeman/Config.pm @@ -85,14 +85,15 @@ sub mangle { } } - if (my @source = $self->get(qw(core source))) { - $self->unset(qw(core source)); - foreach my $s (@source) { + if (my $source_node = $self->getnode(qw(core source))) { + $self->unset(qw(core source)); + foreach my $s ($source_node->value) { my ($name, @args) = quotewords('\s+', 0, $s); my $pack = 'App::Acmeman::Source::' . ucfirst($name); my $obj = eval "use $pack; new $pack(\@args);"; if ($@) { - $self->error($@); + $self->error("error loading source module $name: $@", + locus => $source_node->locus); ++$err; next; } @@ -122,7 +123,7 @@ __DATA__ rootdir = STRING :default=/var/www/acme files = STRING time-delta = NUMBER :default=86400 - source = STRING :default=apache :array + source = STRING :default=default :array check-alt-names = BOOL :default=0 check-dns = BOOL :default=1 my-ip = STRING :array diff --git a/lib/App/Acmeman/Source/Apache.pm b/lib/App/Acmeman/Source/Apache.pm index 58ba39e..ecde285 100644 --- a/lib/App/Acmeman/Source/Apache.pm +++ b/lib/App/Acmeman/Source/Apache.pm @@ -168,15 +168,21 @@ sub setup { my $www_root = $self->get(qw(core rootdir)); debug(2, "writing $filename"); unless ($args{dry_run}) { - unless ($self->mkpath($self->layout->incdir())) { - return 0; + my $challenge_dir = "$www_root/.well-known/acme-challenge"; + my $acme_dir = "/etc/ssl/acme"; + + foreach my $dir ($self->layout->incdir(), $challenge_dir, $acme_dir) { + unless ($self->mkpath($dir)) { + return 0; + } } + open(my $fd, '>', $filename) or croak "can't open \"$filename\" for writing: $!"; print $fd <<EOT; <Macro LetsEncryptChallenge> - Alias /.well-known/acme-challenge $www_root/.well-known/acme-challenge - <Directory $www_root/.well-known/acme-challenge> + Alias /.well-known/acme-challenge $challenge_dir + <Directory $challenge_dir> Options None Require all granted </Directory> @@ -196,9 +202,9 @@ sub setup { SSLProtocol all -SSLv2 -SSLv3 SSLHonorCipherOrder on SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA - SSLCertificateFile /etc/ssl/acme/\$domain/cert.pem - SSLCertificateKeyFile /etc/ssl/acme/\$domain/privkey.pem - SSLCACertificateFile /etc/ssl/acme/lets-encrypt-x3-cross-signed.pem + SSLCertificateFile $acme_dir/\$domain/cert.pem + SSLCertificateKeyFile $acme_dir/\$domain/privkey.pem + SSLCACertificateFile $acme_dir/lets-encrypt-x3-cross-signed.pem </Macro> <Macro LetsEncryptServer \$domain> diff --git a/lib/App/Acmeman/Source/Default.pm b/lib/App/Acmeman/Source/Default.pm new file mode 100644 index 0000000..9f9bd86 --- /dev/null +++ b/lib/App/Acmeman/Source/Default.pm @@ -0,0 +1,48 @@ +package App::Acmeman::Source::Default; +use strict; +use warnings; +use parent 'App::Acmeman::Source'; + +sub new { + my $self; + shift; # Skip class name. + eval { + require App::Acmeman::Source::Apache; + $self = new App::Acmeman::Source::Apache(@_); + }; + if ($@) { + (my $s = $@) =~ s{ at /.+$}{}; + chomp($s); + die <<EOT; +No valid domain source configured. + +You are seeing this error because acmeman was unable to load the default +domain source module. + +The default domain source "apache" scans Apache configuration files and +extracts names listed in ServerName and ServerAlias directives which have +LetsEncrypt certificates configured. + +The source module couldn't be loaded because of the following error: + +"$s" + +If you are going to use the "apache" source, fix this error and retry. +Otherwise, please create the /etc/acmeman.conf configuration file, and +configure another domain source, for example: + + [core] + source = file DOMAINFILE + +Please, see acmeman(1) (or run "acmeman --help") for a detailed discussion +of available domain sources. + +EOT +; + } + return $self; +} + +1; + + |