]> Raphaƫl G. Git Repositories - acme/blob - letscron
b566baed5541c05608217fa909add448644418da
[acme] / letscron
1 #! /usr/bin/perl
2
3 # Best practice
4 use strict;
5 use warnings;
6
7 # Fix use of acl
8 use filetest 'access';
9
10 # Load dependancies
11 use Carp qw(carp);
12 use DateTime;
13 use File::stat qw(stat);
14 use File::Spec;
15 use File::Slurp qw(read_file write_file);
16 use JSON qw(decode_json);
17 use IPC::System::Simple qw(capturex $EXITVAL);
18 use acme qw(CERT_DIR CONFIG DS KEY_DIR SERVER_CRT SERVER_KEY);
19
20 # Load POSIX
21 use POSIX qw(strftime EXIT_SUCCESS EXIT_FAILURE);
22
23 # Init debug
24 my $debug = 0;
25
26 # Init config
27 my $config = undef;
28
29 # Strip and enable debug
30 @ARGV = map { if ($_ eq '-d') { $debug = 1; (); } else { $_; } } @ARGV;
31
32 # Check config file
33 if (! -f CONFIG) {
34 print 'Config file '.CONFIG.' do not exists'."\n";
35 exit EXIT_FAILURE;
36 }
37
38 # Load config
39 unless (
40 #XXX: use eval to workaround a fatal in decode_json
41 eval {
42 # Read it
43 ($config = read_file(CONFIG)) &&
44 # Decode it
45 ($config = decode_json($config)) &&
46 # Check hash validity
47 defined($config->{certificates}) &&
48 # Check not empty
49 scalar($config->{certificates}) &&
50 # Check hash validity
51 defined($config->{thumbprint}) &&
52 # Check certificates array
53 ! scalar map {unless(defined($_->{cert}) && defined($_->{key}) && defined($_->{mail}) && defined($_->{domain}) && defined($_->{domains})) {1;} else {();}} @{$config->{certificates}}
54 }
55 ) {
56 print 'Config file '.CONFIG.' is invalid'."\n";
57 exit EXIT_FAILURE;
58 }
59
60 # Deal with certificates
61 foreach (@{$config->{certificates}}) {
62 # Init variables
63 my ($mtime, $dt, $suffix) = undef;
64
65 # Skip certificate without 60 days
66 if (-f $_->{cert} && ($mtime = stat($_->{cert})->mtime) >= (time() - 60*24*3600)) {
67 next;
68 }
69
70 # Extract cert directory and filename
71 my (undef, $certDir) = File::Spec->splitpath($_->{cert});
72
73 # Check that certificate is writable
74 unless (-w $certDir || -w $_->{cert}) {
75 carp('directory '.$certDir.' or file '.$_->{cert}.' must be writable: '.$!);
76 next;
77 }
78
79 # Unlink if is a symlink
80 if (-l KEY_DIR.DS.SERVER_KEY) {
81 unless(unlink(KEY_DIR.DS.SERVER_KEY)) {
82 carp('unlink '.KEY_DIR.DS.SERVER_KEY.' failed: '.$!);
83 next;
84 }
85 }
86
87 # Symlink to key
88 unless(symlink($_->{key}, KEY_DIR.DS.SERVER_KEY)) {
89 carp('symlink '.$_->{key}.' to '.KEY_DIR.DS.SERVER_KEY.' failed: '.$!);
90 next;
91 }
92
93 # Init args
94 my @args = @{$_->{domains}};
95
96 # Prepend mail and domain to other args
97 unshift(@args, $_->{mail}, $_->{domain});
98
99 # Preprend prod
100 if (defined $_->{prod} && $_->{prod}) {
101 unshift(@args, '-p');
102 }
103
104 # Preprend debug
105 if ($debug) {
106 unshift(@args, '-d');
107 }
108
109 # Run letscert with args
110 my @out = capturex([0..1], './letscert', @args);
111
112 # Deal with error
113 if ($EXITVAL != 0) {
114 print join("\n", @out) if ($debug);
115 carp('letscert '.join(', ', @args).' failed: '.$!);
116 next;
117 }
118
119 # Read cert
120 my $content;
121 unless($content = read_file(CERT_DIR.DS.SERVER_CRT)) {
122 carp('read_file '.CERT_DIR.DS.SERVER_CRT.' failed: '.$!);
123 next;
124 }
125
126 # Handle old certificate
127 if (-w $certDir && -f $_->{cert}) {
128 # Extract datetime suffix
129 $suffix = ($dt = DateTime->from_epoch(epoch => $mtime))->ymd('').$dt->hms('');
130
131 # Rename old certificate
132 unless(rename($_->{cert}, $_->{cert}.'.'.$suffix)) {
133 carp('rename '.$_->{cert}.' to '.$_->{cert}.'.'.$suffix.' failed: '.$!);
134 next;
135 }
136 }
137
138 # Save cert
139 unless(write_file($_->{cert}, $content)) {
140 carp('write_file '.$_->{cert}.' failed: '.$!);
141 next;
142 }
143 }
144
145 exit EXIT_SUCCESS;