Blame


1 536026c5 2021-10-11 op #!/usr/bin/env perl
2 536026c5 2021-10-11 op #
3 536026c5 2021-10-11 op # Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 536026c5 2021-10-11 op #
5 536026c5 2021-10-11 op # Permission to use, copy, modify, and distribute this software for any
6 536026c5 2021-10-11 op # purpose with or without fee is hereby granted, provided that the above
7 536026c5 2021-10-11 op # copyright notice and this permission notice appear in all copies.
8 536026c5 2021-10-11 op #
9 536026c5 2021-10-11 op # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 536026c5 2021-10-11 op # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 536026c5 2021-10-11 op # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 536026c5 2021-10-11 op # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 536026c5 2021-10-11 op # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 536026c5 2021-10-11 op # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 536026c5 2021-10-11 op # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 536026c5 2021-10-11 op #
17 536026c5 2021-10-11 op # You can read the documentation for this script using
18 536026c5 2021-10-11 op #
19 536026c5 2021-10-11 op # $ perldoc renew-certs
20 536026c5 2021-10-11 op #
21 536026c5 2021-10-11 op
22 536026c5 2021-10-11 op use v5.10;
23 536026c5 2021-10-11 op use strict;
24 536026c5 2021-10-11 op use warnings;
25 536026c5 2021-10-11 op
26 536026c5 2021-10-11 op use Getopt::Std;
27 536026c5 2021-10-11 op use Time::Piece;
28 536026c5 2021-10-11 op
29 536026c5 2021-10-11 op my $auto = 0;
30 536026c5 2021-10-11 op my $conf = '/etc/gmid.conf';
31 536026c5 2021-10-11 op my $days = 365;
32 536026c5 2021-10-11 op my $gmid = 'gmid';
33 536026c5 2021-10-11 op my $restart = 0;
34 536026c5 2021-10-11 op my $threshold = 24 * 60 * 60;
35 536026c5 2021-10-11 op
36 536026c5 2021-10-11 op my %options = ();
37 536026c5 2021-10-11 op getopts("ac:d:g:r", \%options);
38 536026c5 2021-10-11 op
39 536026c5 2021-10-11 op foreach my $flag (keys %options) {
40 536026c5 2021-10-11 op if ($flag eq 'a') {
41 536026c5 2021-10-11 op $auto = 1;
42 536026c5 2021-10-11 op } elsif ($flag eq 'c') {
43 536026c5 2021-10-11 op $conf = $options{c};
44 536026c5 2021-10-11 op } elsif ($flag eq 'd') {
45 536026c5 2021-10-11 op $days = int($options{d}) or exit 1;
46 536026c5 2021-10-11 op } elsif ($flag eq 'g') {
47 536026c5 2021-10-11 op $gmid = $options{g};
48 536026c5 2021-10-11 op } elsif ($flag eq 'r') {
49 536026c5 2021-10-11 op $auto = 1;
50 536026c5 2021-10-11 op $restart = 1;
51 536026c5 2021-10-11 op } elsif ($flag eq 't') {
52 536026c5 2021-10-11 op $threshold = int($options{t}) or exit 1;
53 536026c5 2021-10-11 op }
54 536026c5 2021-10-11 op }
55 536026c5 2021-10-11 op
56 536026c5 2021-10-11 op my $now = localtime()->epoch + $threshold;
57 536026c5 2021-10-11 op my $found_one = 0;
58 536026c5 2021-10-11 op
59 536026c5 2021-10-11 op my $c = `$gmid -nn -c $conf @ARGV 2>/dev/null`;
60 536026c5 2021-10-11 op die "$gmid failed to parse $conf" if $? != 0;
61 536026c5 2021-10-11 op
62 536026c5 2021-10-11 op while ($c =~ /server \"(.*)\"/g) {
63 536026c5 2021-10-11 op my $server = $1;
64 536026c5 2021-10-11 op
65 536026c5 2021-10-11 op $c =~ /cert \"(.*)\"/gc;
66 536026c5 2021-10-11 op my $cert = $1;
67 536026c5 2021-10-11 op
68 536026c5 2021-10-11 op $c =~ /key \"(.*)\"/gc;
69 536026c5 2021-10-11 op my $key = $1;
70 536026c5 2021-10-11 op
71 536026c5 2021-10-11 op if (expired($cert)) {
72 536026c5 2021-10-11 op $found_one = 1;
73 536026c5 2021-10-11 op if ($auto) {
74 536026c5 2021-10-11 op renew($server, $cert, $key);
75 536026c5 2021-10-11 op } else {
76 536026c5 2021-10-11 op say $server;
77 536026c5 2021-10-11 op }
78 536026c5 2021-10-11 op }
79 536026c5 2021-10-11 op }
80 536026c5 2021-10-11 op
81 536026c5 2021-10-11 op if ($found_one && $restart) {
82 536026c5 2021-10-11 op my @cmd = ("pkill", "-HUP", $gmid);
83 536026c5 2021-10-11 op system(@cmd);
84 536026c5 2021-10-11 op }
85 536026c5 2021-10-11 op
86 536026c5 2021-10-11 op exit !$found_one;
87 536026c5 2021-10-11 op
88 536026c5 2021-10-11 op sub expired {
89 536026c5 2021-10-11 op my ($cert) = @_;
90 536026c5 2021-10-11 op
91 536026c5 2021-10-11 op my $exp = `openssl x509 -noout -enddate -in $cert`;
92 536026c5 2021-10-11 op die 'failed to execute openssl' if $? != 0;
93 536026c5 2021-10-11 op chomp $exp;
94 536026c5 2021-10-11 op
95 536026c5 2021-10-11 op my $d = Time::Piece->strptime($exp, "notAfter=%b %e %T %Y %Z");
96 536026c5 2021-10-11 op return $d->epoch < $now;
97 536026c5 2021-10-11 op }
98 536026c5 2021-10-11 op
99 536026c5 2021-10-11 op sub renew {
100 536026c5 2021-10-11 op my ($hostname, $cert, $key) = @_;
101 536026c5 2021-10-11 op my @cmd = (
102 536026c5 2021-10-11 op "openssl", "req", "-x509",
103 536026c5 2021-10-11 op "-newkey", "rsa:4096",
104 536026c5 2021-10-11 op "-out", $cert,
105 536026c5 2021-10-11 op "-keyout", $key,
106 536026c5 2021-10-11 op "-days", $days,
107 536026c5 2021-10-11 op "-nodes",
108 536026c5 2021-10-11 op "-subj", "/CN=".$hostname,
109 536026c5 2021-10-11 op );
110 536026c5 2021-10-11 op
111 536026c5 2021-10-11 op system(@cmd) == 0
112 536026c5 2021-10-11 op or die "system @cmd failed: $?";
113 536026c5 2021-10-11 op }
114 536026c5 2021-10-11 op
115 536026c5 2021-10-11 op __END__
116 536026c5 2021-10-11 op
117 536026c5 2021-10-11 op =head1 NAME
118 536026c5 2021-10-11 op
119 536026c5 2021-10-11 op B<renew-certs> - automatically renew gmid certificates
120 536026c5 2021-10-11 op
121 536026c5 2021-10-11 op =head1 SYNOPSIS
122 536026c5 2021-10-11 op
123 536026c5 2021-10-11 op B<renew-certs> [-ar] [-c I<conf>] [-d I<days>] [-g I<gmid>] [-t I<threshold>] [-- I<gmid flags...>]
124 536026c5 2021-10-11 op
125 536026c5 2021-10-11 op =head1 DESCRIPTION
126 536026c5 2021-10-11 op
127 536026c5 2021-10-11 op B<renew-certs> attempts to renew the certificates used by gmid if they
128 536026c5 2021-10-11 op are close to the expiration date and can optionally restart the
129 536026c5 2021-10-11 op server. It's meant to be used in a crontab(5) file.
130 536026c5 2021-10-11 op
131 536026c5 2021-10-11 op B<renew-certs> needs at least B<gmid> 1.8.
132 536026c5 2021-10-11 op
133 536026c5 2021-10-11 op The arguments are as follows:
134 536026c5 2021-10-11 op
135 536026c5 2021-10-11 op =over
136 536026c5 2021-10-11 op
137 536026c5 2021-10-11 op =item -a
138 536026c5 2021-10-11 op
139 536026c5 2021-10-11 op Automatically generate a new set of certificates.
140 536026c5 2021-10-11 op
141 536026c5 2021-10-11 op =item -c I<conf>
142 536026c5 2021-10-11 op
143 536026c5 2021-10-11 op Path to the gmid configuration. By default is F</etc/gmid.conf>.
144 536026c5 2021-10-11 op
145 536026c5 2021-10-11 op =item -d I<days>
146 536026c5 2021-10-11 op
147 536026c5 2021-10-11 op Number of I<days> the newly generated certificates will be valid for;
148 536026c5 2021-10-11 op 365 by default.
149 536026c5 2021-10-11 op
150 536026c5 2021-10-11 op =item -g I<gmid>
151 536026c5 2021-10-11 op
152 536026c5 2021-10-11 op Path to the gmid(1) executable.
153 536026c5 2021-10-11 op
154 536026c5 2021-10-11 op =item -r
155 536026c5 2021-10-11 op
156 536026c5 2021-10-11 op Restart B<gmid> after re-generating the certificates by killing it
157 536026c5 2021-10-11 op with SIGHUP. Implies -a.
158 536026c5 2021-10-11 op
159 536026c5 2021-10-11 op =item -t I<threshold>
160 536026c5 2021-10-11 op
161 536026c5 2021-10-11 op Tweak the expiring I<threshold>. Certificates whose I<notAfter> field
162 536026c5 2021-10-11 op ends before I<threshold> seconds will be considered outdated. By
163 536026c5 2021-10-11 op default is 86400, or 24 * 60 * 60, 24 hours.
164 536026c5 2021-10-11 op
165 536026c5 2021-10-11 op =item I<gmid flags>
166 536026c5 2021-10-11 op
167 536026c5 2021-10-11 op Additional flags to be passed to gmid(1).
168 536026c5 2021-10-11 op
169 536026c5 2021-10-11 op =back
170 536026c5 2021-10-11 op
171 536026c5 2021-10-11 op =head1 EXIT STATUS
172 536026c5 2021-10-11 op
173 536026c5 2021-10-11 op The B<renew-certs> utility exits on 0 when at least one certificate is
174 536026c5 2021-10-11 op about to expire and >0 otherwise, or if an error occurs.
175 536026c5 2021-10-11 op
176 536026c5 2021-10-11 op =head1 EXAMPLES
177 536026c5 2021-10-11 op
178 536026c5 2021-10-11 op Some examples of how to use B<renew-certs> in a crontab(5) file
179 536026c5 2021-10-11 op follows:
180 536026c5 2021-10-11 op
181 536026c5 2021-10-11 op # automatically renew and restart gmid
182 536026c5 2021-10-11 op 0 0 * * * renew-certs -r
183 536026c5 2021-10-11 op
184 536026c5 2021-10-11 op # like the previous, but pass a custom flag to gmid
185 536026c5 2021-10-11 op 0 0 * * * renew-certs -r -- -Dcerts=/etc/ssl/
186 536026c5 2021-10-11 op
187 536026c5 2021-10-11 op # automatically renew the certs but use a custom
188 536026c5 2021-10-11 op # command (rcctl in this case) to restart the server
189 536026c5 2021-10-11 op 0 0 * * * renew-certs -a && rcctl restart gmid
190 536026c5 2021-10-11 op
191 536026c5 2021-10-11 op # only check for expiration. `cmd' can read the names of the
192 536026c5 2021-10-11 op # servers with an expiring certificate from stdin, one per
193 536026c5 2021-10-11 op # line
194 536026c5 2021-10-11 op 0 0 * * * renew-certs | cmd
195 536026c5 2021-10-11 op
196 536026c5 2021-10-11 op =head1 SEE ALSO
197 536026c5 2021-10-11 op
198 536026c5 2021-10-11 op crontab(1) gmid(1) openssl(1) crontab(5)
199 536026c5 2021-10-11 op
200 536026c5 2021-10-11 op =cut