authorSergey Poznyakoff <>2018-05-07 08:27:28 (GMT)
committer Sergey Poznyakoff <>2018-05-07 08:27:28 (GMT)
commit95c82ca2a5562e60463a38519157ae1a8b60ac1a (patch) (side-by-side diff)
parentd76f2f28aaff86ffd7e4a049d5ce7cd91885805f (diff)
Improve "file" source
* lib/App/Acmeman/Source/ (new): New options --ignore and --host. * acmeman: Document the "file" source.
Diffstat (more/less context) (ignore whitespace changes)
2 files changed, 77 insertions, 21 deletions
diff --git a/acmeman b/acmeman
index 7edd5e0..143ef43 100755
--- a/acmeman
+++ b/acmeman
@@ -81,13 +81,13 @@ B<acmeman> or B<apache> configuration files, or from both. If the
default B<acmeman> configuration file doesn't exist, the program
scans B<apache> configuration files for a list of domains.
B<Acmeman> is normally run periodically as a cronjob.
If you plan to serve SSL protected domains using apache, you can skip
-right to the B<APACHE> section.
+right to the B<apache> section.
The following is a short introduction to the B<acmeman> configuration. For
a detailed discussion, see the B<CONFIGURATION> section below.
The configuration file, B</etc/acmeman.conf>, consists of statements,
which have the form B<I<KW>=I<VAL>>, grouped into sections, declared
@@ -124,13 +124,15 @@ implicitly if no other B<files> section is defined in the configuration file.
Moreover, the string B<default> is the default identifier, which is used if
the B<domain> section lacks the B<files> keyword.
The special section B<[core]> contains basic settings that control the
program behavior. One of the important settings is B<source>, which declares
an external source from which domain settings must be obtained. As
-of B<acmeman> version 2.00, the only available external source is B<apache>.
+of B<acmeman> version 1.05, the following sources are available:
+B<null>, B<apache>, and B<file>.
Consider the following configuration:
source = apache
It instructs B<acmeman> to read domain settings from Apache configuration
@@ -208,26 +210,30 @@ The command line option B<--time-delta> overrides this setting.
Defines the command to be run at the end of the run if at least one
certificate has been updated. Normally this command reloads the httpd
server (or whatever server is using the certificates). If more than one
B<postrenew> statements are defined, they will be run in sequence, in the
same order as they appeared in the configuration file.
-=item B<source=>I<ID> [I<LAYOUT>]
+=item B<source=>I<ID> [I<ARG>...]
Defines additional source of information. B<App::Acmeman> version 1.05
-is shipped with two sources: B<null> and B<apache>.
+is shipped with three sources: B<null>, B<apache>, and B<file>.
-The B<null> module is an empty source. Use it if all domains are described
-in the configuration file.
+The B<null> module is an empty source. Command line arguments are ignored.
+Use this source if all domains are described in the configuration file.
The B<apache> source module is the default. It scans B<httpd> configuration
-files as described in section B<APACHE>. The optional I<LAYOUT> argument
-defines the apache configuration layout. Allowed values are: B<debian>,
-B<slackware>, B<suse> and B<rh> (for Red Hat). If I<LAYOUT> is absent, it
+files as described in section B<APACHE>. One argument is allowed. If supplied,
+it defines the apache configuration layout. Allowed values are: B<debian>,
+B<slackware>, B<suse> and B<rh> (for Red Hat). Without arguments, the layout
will be autodetected.
+The B<file> source reads domain names from one or more disk files. A
+mandatory argument specifies the name of the directory where the files
+are located. This mode is suitable for use with B<haproxy> pattern files.
=item B<files=>I<NAME>
Identifies the B<[files]> section which describes how to create certificate
files for domains which lack explicit B<files> keyword. Default I<NAME> is
B<default>. See the description of the B<files> statement in B<domain>
@@ -342,31 +348,47 @@ be present if B<type> is set to B<split>.
Defines the name of the meta-variable in I<FILENAME> arguments, which will
be replaced with the actual domain name. Default is B<$domain>.
-=head1 APACHE
+=head1 SOURCES
+=head2 null
+ [core]
+ source = null
+Declares empty source. This means that B<acmeman> will handle only domain
+names explicitly declared in the configuration file using the B<domain>
+=head2 apache
-This is the default mode. It assumes Apache httpd, version 2.4 or later
+ [core]
+ source = apache [LAYOUT]
+This is the default source. It assumes Apache httpd, version 2.4 or later
(although only minor changes are necessary to make it work with version 2.2).
-Four most popular layouts of Apache configuration files are supported:
-Debian, Slackware, SuSe, and Red Hat. A special directory should be
-configured for receiving ACME challenges.
+The optional I<LAYOUT> argument defines the layout of the apache configuration
+files. Allowed layout values are: B<debian>, B<slackware>, B<suse> and
+B<rh> (for Red Hat). If not supplied, the layout is determined automatically.
+A special directory should be configured for receiving ACME challenges.
The package provides two Apache macros: for serving ACME challenges and
declaring SSL virtual hosts.
Upon startup the program scans Apache configuration for virtual hosts
that use ACME certificates, checks their expiration times, and renews those
of the certificates that are nearing their expiration times within a
predefined number of seconds (24 hours by default). If any of the
certificates were updated during the run, B<acmeman> will restart the
B<httpd> server.
-=head2 Setup
+=head3 Setup
To set up the necessary infrastructure, run B<acmeman --setup>. It will
create the configuration file B<httpd-letsencrypt.conf>, defining two
macros for SSL-enabled sites (B<mod_macro> is needed). Finally, it will
create the directory B</var/www/acme>, which will be used for receiving
and serving ACME challenges. If another directory is preferred, it can
@@ -383,13 +405,13 @@ will be created for each domain name needing SSL, and two files will be placed
there: F<cert.pem>, containing the leaf and intermediate certificates for that
domain, and F<privkey.pem>, containing the private key for that domain.
The program will refuse to overwrite existing files B<httpd-letsencrypt.conf>,
unless given the B<--force> (B<-F>) option.
-=head2 Configuring SSL
+=head3 Configuring SSL
To declare that a virtual host needs SSL certificate, add the following
line to the Apache B<VirtualHost> block serving plain HTTP for that host:
Use LetsEncryptChallenge
@@ -462,12 +484,32 @@ will use the B<LetsEncryptSSL> macro to configure the correct certificate:
<VirtualHost *:80>
Use LetsEncryptSSL
+=head2 file
+ [core]
+ source = file PATTERN [--ignore=RX] [--host=HOST]
+Domain names will be read from files matching I<PATTERN>. This argument
+can be either a valid globbing pattern or a directory name. In the latter
+case, the source module will read all files from that directory, except
+those whose names match the following perl regexp: C<^\.|~$|\.bak$|^#.*#$>.
+The default regexp can be overridden using the B<--ignore> (B<-i>) option.
+The input files must contain exactly one domain name per line. No empty
+lines or comments are allowed. The first domain name will become the B<CN>
+of the issued certificate. The rest of domain names will form alternative
+If the B<--host> (B<-h>) option is used, only one certificate will be
+issued. The I<HOST> will be used as its B<CN>. All the domain names read
+from the input files will form the list of its alternative names.
=head1 OPTIONS
=over 4
=item B<-D>, B<--time-delta=>I<N>
diff --git a/lib/App/Acmeman/Source/ b/lib/App/Acmeman/Source/
index 561c279..48a4010 100644
--- a/lib/App/Acmeman/Source/
+++ b/lib/App/Acmeman/Source/
@@ -2,28 +2,38 @@ package App::Acmeman::Source::File;
use strict;
use warnings;
use Carp;
use File::Spec;
use parent 'App::Acmeman::Source';
+use Getopt::Long qw(GetOptionsFromArray :config gnu_getopt no_ignore_case);
sub new {
my $class = shift;
my $pattern = shift || croak "file name or globbing pattern must be given";
my $ignore = '^\.|~$|\.bak$|^#.*#$';
+ my $host;
+ GetOptionsFromArray(\@_,
+ 'ignore|i=s' => \$ignore,
+ 'host|h=s' => \$host);
unless ($pattern =~ m{[][*?]}) {
$pattern =~ s{/$}{};
$pattern = File::Spec->catfile($pattern, '*');
- bless { pattern => $pattern, ignore => $ignore }, $class;
+ bless { pattern => $pattern,
+ ignore => $ignore,
+ host => $host }, $class;
sub scan {
my ($self) = @_;
$self->debug(1, "initializing file list from $self->{pattern}");
my $err = 0;
+ if ($self->{host}) {
+ $self->define_domain($self->{host});
+ }
foreach my $file (glob $self->{pattern}) {
next if $file =~ m{$self->{ignore}};
$err |= $self->load($file);
return $err == 0;
@@ -32,18 +42,22 @@ sub load {
my ($self, $file) = @_;
$self->debug(1, "reading $file");
open(my $fh, '<', $file)
or do {
$self->error("can't open $file: $!");
return 0;
- };
+ };
chomp(my @lines = <$fh>);
close $fh;
- my $cn = shift @lines;
- $self->define_domain($cn);
- $self->define_alias($cn, @lines);
+ if ($self->{host}) {
+ $self->define_alias($self->{host}, @lines);
+ } else {
+ my $cn = shift @lines;
+ $self->define_domain($cn);
+ $self->define_alias($cn, @lines);
+ }
return 1;

Return to:

Send suggestions and report system problems to the System administrator.