3f8a559cf3a0f0fa65836635a4294b63f6e11501
[acme] / acmecert
1 #! /usr/bin/perl
2
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
16 # Copyright (C) 2016 - 2017 Raphaël Gertz <acme@rapsys.eu>
17
18 # Best practice
19 use strict;
20 use warnings;
21
22 # Fix use of acl
23 use filetest qw(access);
24
25 # Load dependancies
26 use File::stat qw(stat);
27 use File::Slurp qw(read_file);
28 use JSON qw(decode_json);
29 use Acme;
30
31 # Load POSIX
32 use POSIX qw(EXIT_SUCCESS EXIT_FAILURE);
33
34 # Init debug
35 my $debug = 0;
36
37 # Init config
38 my $config = undef;
39
40 # Init config file name
41 my $configFilename = '/etc/acme/config';
42
43 # Init domains
44 my @domains = ();
45
46 # Strip and enable debug
47 @ARGV = map { if ($_ eq '-d') { $debug = 1; (); } else { $_; } } @ARGV;
48
49 # Strip and enable debug
50 for (my $i = 0; $i <= $#ARGV; $i++) {
51 # Match redhat types
52 if ($ARGV[$i] =~ /^(?:(\-c|\-\-config)(?:=(.+))?)$/) {
53 if (defined($2) && -f $2) {
54 $configFilename = $2;
55 splice(@ARGV, $i, 1);
56 $i--;
57 # Extract next parameter
58 } elsif(defined($ARGV[$i+1]) && $ARGV[$i+1] =~ /^(.+)$/ && -f $1) {
59 $configFilename = $1;
60 splice(@ARGV, $i, 2);
61 $i--;
62 # Set default
63 } else {
64 print 'Config parameter without valid file name'."\n";
65 exit EXIT_FAILURE;
66 }
67 }
68 }
69
70 # Load config
71 unless (
72 #XXX: use eval to workaround a fatal in decode_json
73 eval {
74 # Check file
75 (-f $configFilename) &&
76 # Read it
77 ($config = read_file($configFilename)) &&
78 # Decode it
79 ($config = decode_json($config)) &&
80 # Check hash validity
81 defined($config->{certificates}) &&
82 # Check not empty
83 scalar($config->{certificates}) &&
84 # Check hash validity
85 defined($config->{thumbprint}) &&
86 # Check certificates array
87 ! scalar map {unless(defined($_->{cert}) && defined($_->{key}) && defined($_->{mail}) && defined($_->{domain}) && defined($_->{domains})) {1;} else {();}} @{$config->{certificates}}
88 }
89 ) {
90 print 'Config file '.$configFilename.' is not readable or invalid'."\n";
91 exit EXIT_FAILURE;
92 }
93
94 # Deal with specified domains
95 if (scalar(@ARGV) > 0) {
96 # Check that domains are present in config
97 foreach my $domain (@ARGV) {
98 my $found = undef;
99 foreach my $certificate (@{$config->{certificates}}) {
100 if ($certificate->{domain} eq $domain) {
101 push(@domains, $certificate);
102 $found = 1;
103 }
104 }
105 unless($found) {
106 print 'Domain '.$domain.' not found in config file '.$configFilename."\n";
107 exit EXIT_FAILURE;
108 }
109 }
110 # Without it
111 } else {
112 # Populate domains array with available ones
113 foreach my $certificate (@{$config->{certificates}}) {
114 push(@domains, $certificate);
115 }
116 }
117
118 # Show usage
119 if (scalar(@domains) < 1) {
120 print "Usage: $0 [-(c|-config)[=/etc/acme/config]] [example.com] [...]\n";
121 exit EXIT_FAILURE;
122 }
123
124 # Deal with each domain
125 foreach my $domain (@domains) {
126 # Create new object
127 my $acme = Acme->new($debug, $domain, {thumbprint => $config->{thumbprint}, pending => $config->{pending}, term => $config->{term}});
128
129 # Prepare environement
130 $acme->prepare();
131
132 # Generate required keys
133 $acme->genKeys();
134
135 # Generate csr
136 $acme->genCsr();
137
138 # Directory
139 $acme->directory();
140
141 # Register
142 $acme->register();
143
144 # Authorize
145 $acme->authorize();
146
147 # Issue
148 $acme->issue();
149 }
150
151 # Exit with success
152 exit EXIT_SUCCESS;