1 |
davidg |
2774 |
#! /usr/bin/perl -w |
2 |
|
|
# |
3 |
|
|
# dctcs-cli.pl - DigiCert TCS server certificate generator CLI |
4 |
|
|
# see below at end for documentation and arguments |
5 |
|
|
# |
6 |
|
|
# @(#)$Id$ |
7 |
|
|
# David Groep, Nikhef, 2015 - www.nikhef.nl/grid |
8 |
|
|
# |
9 |
|
|
# As per doc https://www.digicert.com/services/v2/documentation |
10 |
|
|
# |
11 |
|
|
use strict; |
12 |
|
|
use LWP::UserAgent; |
13 |
|
|
use LWP::Protocol::https; |
14 |
|
|
use IO::Socket::SSL; |
15 |
|
|
use JSON; |
16 |
|
|
use Data::Dumper; |
17 |
|
|
use Getopt::Long; |
18 |
|
|
$Getopt::Long::ignorecase = 0; |
19 |
|
|
|
20 |
|
|
# ########################################################################### |
21 |
|
|
# basic configuration - you SHOULD probably change apikeyfile and orgid! |
22 |
|
|
# where apikeyfile may be the empty string (will then ask key from STDIN) |
23 |
|
|
# |
24 |
|
|
my $resturl = "https://www.digicert.com/services/v2"; |
25 |
|
|
my $thishost = `hostname -f`; chomp($thishost); |
26 |
|
|
my $dirprefix = "tcs-"; |
27 |
|
|
my $apikeylen = 82; # length in characters of API key, seems to be 47 or 82 |
28 |
|
|
# |
29 |
|
|
# CONFIGURE these values or override with args each time |
30 |
|
|
my $orgid = "@ORGNAME@"; # CONFIG: Org ID from DigiCert, see also GUI |
31 |
|
|
my $apikeyfile = "@APIKEYFILE@"; # CONFIG: provide your own filename here |
32 |
|
|
|
33 |
|
|
my $hostname; |
34 |
|
|
my $dir; # target directory (default to tcs-<hostname>) |
35 |
|
|
my $subdir=""; # target subdirecty (defaults to empty) |
36 |
|
|
my $userelease=0; # use NDPF vlaai release mechanism |
37 |
|
|
my $mode_req=0; |
38 |
|
|
my $mode_retr=0; |
39 |
|
|
my $mode_appr=0; |
40 |
|
|
my $basedir=""; |
41 |
davidg |
2776 |
my $product = "@PRODUCTDEFAULT@"; |
42 |
davidg |
2774 |
my $validity = 1; |
43 |
|
|
my $approve = ""; |
44 |
|
|
my $help = 0; |
45 |
|
|
|
46 |
|
|
&GetOptions( |
47 |
|
|
'd|destdir=s' => \$dir, |
48 |
|
|
's|subsir=s' => \$subdir, |
49 |
|
|
'R|release' => \$userelease, |
50 |
|
|
'K|keyfile=s' => \$apikeyfile, |
51 |
|
|
'prefix=s' => \$dirprefix, |
52 |
|
|
'P|product=s' => \$product, |
53 |
|
|
'A|approve=s' => \$approve, |
54 |
|
|
'O|orgid=s' => \$orgid, |
55 |
|
|
'V|val|validity=i' => \$validity, |
56 |
|
|
'r|request' => \$mode_req, |
57 |
|
|
'a|postapprove' => \$mode_appr, |
58 |
|
|
'i|install|retrieve' => \$mode_retr, |
59 |
|
|
'h|help' => \$help |
60 |
|
|
) or exit 1; |
61 |
|
|
|
62 |
|
|
if ( $help ) { &help; exit 0 } |
63 |
|
|
|
64 |
|
|
# ########################################################################### |
65 |
|
|
# |
66 |
|
|
# validate options and input |
67 |
|
|
die "Requires a mode (-r or -i or -a) for running\n" |
68 |
|
|
if ! $mode_req and ! $mode_retr and ! $mode_appr; |
69 |
|
|
|
70 |
|
|
$hostname = $ARGV[0] or die "No argument hostname"; |
71 |
|
|
$hostname =~ s/^$dirprefix//; # if you used the dirname instead ... |
72 |
|
|
die "Invalid hostname $hostname\n" |
73 |
|
|
if ( $hostname !~ /^[a-z][-0-9a-z]+\.[a-z][-0-9a-z\.]+$/ ); |
74 |
|
|
|
75 |
|
|
$dir = "$dirprefix$hostname" unless $dir; |
76 |
|
|
$basedir=$dir; |
77 |
|
|
if ( $subdir ne "" ) { $dir .= "/$subdir"; } |
78 |
|
|
|
79 |
|
|
die "Invalid subdir (not a SUBdir) in $subdir\n" if $subdir =~ /^\//; |
80 |
|
|
die "Cannot retrieve cert for $hostname if not requested this way,\n". |
81 |
|
|
"and directory $dir does not exist\n" if ( $mode_retr and ! -d $dir ); |
82 |
|
|
die "Cannot re-use existing $dir for request\n" |
83 |
|
|
if $mode_req and -d $dir; |
84 |
|
|
die "TCS directory $dir has no orderID\n" |
85 |
|
|
if $mode_retr and ! -e "$dir/orderid.txt"; |
86 |
|
|
die "Invalid validity (not a number or too long): $validity\n" |
87 |
|
|
if $validity !~ /^\d+$/ or $validity > 3; |
88 |
|
|
die "Cannot approve on installation (but on request or explicit only)\n" |
89 |
|
|
if $mode_retr && $approve; |
90 |
|
|
die "Cannot approve without an approval comment\n" |
91 |
|
|
if $mode_appr && ( $approve eq "" ); |
92 |
|
|
|
93 |
|
|
die "Expected argument FQDN\n" if ( $#ARGV < 0 ); |
94 |
|
|
|
95 |
|
|
# ########################################################################### |
96 |
|
|
# |
97 |
|
|
# read password if needed from file or use env var DIGICERTAPIKEY or STDIN |
98 |
|
|
my $apikey; |
99 |
|
|
if ( $apikeyfile ne "none" && $apikeyfile ne "" && -e $apikeyfile ) { |
100 |
|
|
open FH,"<$apikeyfile"; |
101 |
|
|
$apikey = <FH>; chomp($apikey); |
102 |
|
|
close FH; |
103 |
|
|
} elsif (defined($ENV{DIGICERTAPIKEY})) { |
104 |
|
|
$apikey = $ENV{DIGICERTAPIKEY}; |
105 |
|
|
} else { |
106 |
|
|
print "Provide API key: "; |
107 |
|
|
system("stty -echo"); |
108 |
|
|
$apikey = <STDIN>; chomp($apikey); |
109 |
|
|
system("stty echo"); print "***\n"; |
110 |
|
|
} |
111 |
|
|
#die "Invalid API key length\n" if length($apikey) != $apikeylen; |
112 |
|
|
|
113 |
|
|
# ########################################################################### |
114 |
|
|
# setup defaults and LWP |
115 |
|
|
# |
116 |
|
|
# initialise UA |
117 |
|
|
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); |
118 |
|
|
$ua->agent("dctcs/0.2 (libwww-perl/$]; TERENA-TCS; $^O)"); |
119 |
|
|
$ua->default_header('X-DC-DEVKEY' => $apikey); |
120 |
|
|
$ua->default_header('Content-Type' => "application/json"); |
121 |
|
|
$ua->default_header('Accept' => "application/json"); |
122 |
|
|
|
123 |
|
|
# ########################################################################### |
124 |
|
|
# Actions: request or install |
125 |
|
|
# |
126 |
|
|
if ( $mode_req ) { |
127 |
|
|
# ######################################################################### |
128 |
|
|
# |
129 |
|
|
print "Creating request and order for $hostname\n"; |
130 |
|
|
|
131 |
|
|
if ( ! -d $basedir && $subdir ne "" ) { |
132 |
|
|
mkdir $basedir or |
133 |
|
|
die "Cannot create new basedir $basedir for $hostname: $!\n"; |
134 |
|
|
} |
135 |
|
|
mkdir $dir or die "Cannot create new subdir $dir for $hostname: $!\n"; |
136 |
|
|
|
137 |
|
|
# generate request using OpenSSL |
138 |
|
|
my $rc = system("openssl req -new -nodes -keyout $dir/key-$hostname.pem ". |
139 |
|
|
"-out $dir/req-$hostname.pem -newkey rsa:2048 ". |
140 |
|
|
"-subj '/C=NL/O=Nikhef/CN=$hostname'"); |
141 |
|
|
die "Generation of CSR in $dir failed: $rc\n" if $rc; |
142 |
|
|
|
143 |
|
|
# construct request for DigiCert API v2 |
144 |
|
|
open CSR,"<$dir/req-$hostname.pem" or die "Cannot open CSR: $!\n"; |
145 |
|
|
my $csr = ""; while (<CSR>) { $csr .= $_; }; close CSR; |
146 |
|
|
|
147 |
|
|
# if the org is named, we need to resolve it to an ID in the |
148 |
|
|
# user's current container |
149 |
|
|
if ( $orgid !~ /^\d+$/ ) { # it is a named org, so resolve |
150 |
|
|
my $containerid = &getContainerId($ua); |
151 |
|
|
print "Resolving org $orgid in container $containerid\n"; |
152 |
|
|
|
153 |
|
|
my $oanswer = &getDump($ua,"GET", |
154 |
|
|
"container/$containerid/order/organization" ); |
155 |
|
|
|
156 |
|
|
foreach my $iorg ( @{$oanswer->{"organizations"}} ) { |
157 |
|
|
if ( lc($iorg->{"name"}) eq lc($orgid) ) { |
158 |
|
|
$orgid = $iorg->{"id"}; last; |
159 |
|
|
} |
160 |
|
|
} |
161 |
|
|
die "Cannot resolve org $orgid via API\n" if ( $orgid !~ /^\d+$/ ); |
162 |
|
|
print "Processing order for org #$orgid in container #$containerid\n"; |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
my %request = ( |
166 |
|
|
"certificate" => { |
167 |
|
|
"common_name", "$hostname", |
168 |
|
|
"dns_names" , [ ], |
169 |
|
|
"csr", "$csr", |
170 |
|
|
"signature_hash", "sha256" |
171 |
|
|
}, |
172 |
|
|
"organization" => { "id", "$orgid" }, |
173 |
|
|
"validity_years", $validity, |
174 |
|
|
"comments", "Requested on $thishost for NDPF host $hostname ". |
175 |
|
|
"by ".getpwuid( $< ), |
176 |
|
|
); |
177 |
|
|
|
178 |
|
|
# populate subjectAltName, with at least the primary FQDN |
179 |
|
|
$request{"certificate"}{"dns_names"} = []; |
180 |
|
|
foreach my $hn ( @ARGV ) { |
181 |
|
|
push @{$request{"certificate"}{"dns_names"}},$hn; |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
my $answer = &getDump($ua,"POST", |
185 |
|
|
"order/certificate/$product",to_json(\%request) ); |
186 |
|
|
|
187 |
|
|
# store the OrderID, used by the installed, in a special file |
188 |
|
|
if ( defined $answer->{"id"} ) { |
189 |
|
|
open FH,">$dir/orderid.txt" and do { |
190 |
|
|
print FH $answer->{"id"}."\n"; |
191 |
|
|
close FH; |
192 |
|
|
}; |
193 |
|
|
}; |
194 |
|
|
|
195 |
|
|
# save the full JSON answer, including requestID, for later perusal |
196 |
|
|
open FH,">$dir/response.json" and do { |
197 |
|
|
print FH to_json($answer); |
198 |
|
|
close FH; |
199 |
|
|
}; |
200 |
|
|
|
201 |
|
|
# approval: needs a comment, and can only be done by Admins of CertCentral |
202 |
|
|
if ( $approve ne "" ) { |
203 |
|
|
my $requestid = $answer->{"requests"}[0]{"id"}; |
204 |
|
|
if ( $requestid =~ /^\d+$/ ) { |
205 |
|
|
print "Approving request (attempt) for Request ID $requestid\n"; |
206 |
|
|
my %approval = ( |
207 |
|
|
"status" => "approved", |
208 |
|
|
"processor_comment" => "By ".getpwuid( $< ).": $approve" |
209 |
|
|
); |
210 |
|
|
my $approvalresult = &getDump($ua,"PUTDUMP","request/$requestid/status", |
211 |
|
|
to_json(\%approval)); |
212 |
|
|
print "Approved: $approvalresult\n"; |
213 |
|
|
} else { |
214 |
|
|
warn "Invalid request ID found ($requestid), sorry!\n"; |
215 |
|
|
} |
216 |
|
|
} |
217 |
|
|
# |
218 |
|
|
# ######################################################################### |
219 |
|
|
|
220 |
|
|
} elsif ( $mode_retr ) { |
221 |
|
|
|
222 |
|
|
# ######################################################################### |
223 |
|
|
# |
224 |
|
|
|
225 |
|
|
# works based on stored order number in dirctory |
226 |
|
|
print "Retrieving order for $hostname\n"; |
227 |
|
|
|
228 |
|
|
open ORDERID,"<$dir/orderid.txt" or die "Cannot open OrderID $!\n"; |
229 |
|
|
my $orderid = <ORDERID>; chomp($orderid); |
230 |
|
|
die "Invalid order ID $orderid\n" unless $orderid =~ /^\d+$/; |
231 |
|
|
|
232 |
|
|
my $answer = &getDump($ua,"GET","order/certificate/$orderid"); |
233 |
|
|
|
234 |
|
|
die "No certificate for order $orderid (yet)\n" |
235 |
|
|
unless defined $answer->{"certificate"}{"id"} and |
236 |
|
|
$answer->{"certificate"}{"id"} =~ /^\d+$/; |
237 |
|
|
my $certid = $answer->{"certificate"}{"id"}; |
238 |
|
|
|
239 |
|
|
print "Retrieving order $orderid certificate ID $certid\n"; |
240 |
|
|
|
241 |
|
|
my $certdata = |
242 |
|
|
&getDump($ua,"GETDUMP","/certificate/$certid/download/format/pem_all"); |
243 |
|
|
|
244 |
|
|
# this splits the full PEM file of all into per-cert chunks with good naming |
245 |
|
|
my $i = 0; my $str = ""; my $processing = 0; my $eecname=""; my $l; |
246 |
|
|
|
247 |
|
|
my @certlist = split /[\r\n]+/, $certdata; # irrespective of EOLN in input |
248 |
|
|
foreach $l ( @certlist ) { |
249 |
|
|
if ( $l eq "-----BEGIN CERTIFICATE-----" ) { |
250 |
|
|
$str = ""; $processing = 1; |
251 |
|
|
} |
252 |
|
|
if ( $l eq "-----END CERTIFICATE-----" ) { |
253 |
|
|
my ($subj,$olstr,$cn,$fname); |
254 |
|
|
|
255 |
|
|
$str .= "$l\n"; $processing = 0; |
256 |
|
|
( $olstr = $str ) =~ s/\n/\\n/gm; |
257 |
|
|
|
258 |
|
|
$subj = `echo -ne "$olstr" | openssl x509 -noout -subject`; |
259 |
|
|
chomp($subj); $subj =~ s/^[^=]+= *//; |
260 |
|
|
|
261 |
|
|
( $cn = $subj ) =~ s/^.*\/CN=//; |
262 |
|
|
( $fname = $cn ) =~ s/\s+//g; |
263 |
|
|
open CF,">$dir/cert-$fname.pem" and do { |
264 |
|
|
print CF $str; |
265 |
|
|
close CF; |
266 |
|
|
if ( $i == 0 and $cn eq $hostname ) { |
267 |
|
|
$eecname = "$dir/cert-$fname.pem"; |
268 |
|
|
} |
269 |
|
|
}; |
270 |
|
|
# informational output follows |
271 |
|
|
print "Certificate $i (cert-$cn):\n"; |
272 |
|
|
print " Subject: $subj\n"; |
273 |
|
|
$i++; |
274 |
|
|
} |
275 |
|
|
if ( $processing ) { $str .= "$l\n"; } |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
# cater for subdir and release to usercert/userkey.pem |
279 |
|
|
if ( $userelease and $eecname ) { |
280 |
|
|
if ( $eecname ne "$dir/cert-$hostname.pem") { |
281 |
|
|
die "Naming inconsistency for $eecname, oops! ". |
282 |
|
|
"It is not $basedir/$subdir/cert-$hostname.pem\n"; |
283 |
|
|
} |
284 |
|
|
if ( ( -e "$basedir/usercert.pem" && ! -l "$basedir/usercert.pem" ) |
285 |
|
|
|| ( -e "$basedir/userkey.pem" && ! -l "$basedir/userkey.pem" ) ) { |
286 |
|
|
warn "One of $basedir/user{key,cert}.pem not a symlink, release skipped" |
287 |
|
|
} else { |
288 |
dennisvd |
3041 |
unlink "$basedir/usercert.pem"; |
289 |
|
|
unlink "$basedir/userkey.pem"; |
290 |
davidg |
2774 |
$subdir = "." unless $subdir; |
291 |
|
|
symlink "$subdir/key-$hostname.pem","$basedir/userkey.pem"; |
292 |
|
|
symlink "$subdir/cert-$hostname.pem","$basedir/usercert.pem"; |
293 |
|
|
open FH,">$basedir/release.state" and close FH; |
294 |
|
|
} |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
# |
298 |
|
|
# ######################################################################### |
299 |
|
|
|
300 |
|
|
} elsif ( $mode_appr ) { |
301 |
|
|
|
302 |
|
|
# ######################################################################### |
303 |
|
|
# |
304 |
|
|
|
305 |
|
|
# works based on stored order number in dirctory |
306 |
|
|
print "Approving first request for order for $hostname\n"; |
307 |
|
|
|
308 |
|
|
open ORDERID,"<$dir/orderid.txt" or die "Cannot open OrderID $!\n"; |
309 |
|
|
my $orderid = <ORDERID>; chomp($orderid); |
310 |
|
|
die "Invalid order ID $orderid\n" unless $orderid =~ /^\d+$/; |
311 |
|
|
|
312 |
|
|
my $answer = &getDump($ua,"GET","order/certificate/$orderid"); |
313 |
|
|
|
314 |
|
|
die "No request for order $orderid (yet)\n" |
315 |
|
|
unless defined $answer->{"requests"}[0]{"id"} and |
316 |
|
|
$answer->{"requests"}[0]{"id"} =~ /^\d+$/; |
317 |
|
|
my $requestid = $answer->{"requests"}[0]{"id"}; |
318 |
|
|
|
319 |
|
|
if ( $answer->{"status"} ne "needs_approval" ) { |
320 |
|
|
print "Order $orderid (request $requestid) already approved, skipping\n"; |
321 |
|
|
exit 0; |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
print "Retrieving order $orderid request ID $requestid\n"; |
325 |
|
|
|
326 |
|
|
# approval: needs a comment, and can only be done by Admins of CertCentral |
327 |
|
|
print "Approving request (attempt) for Request ID $requestid\n"; |
328 |
|
|
my %approval = ( |
329 |
|
|
"status" => "approved", |
330 |
|
|
"processor_comment" => "By ".getpwuid( $< ).": $approve" |
331 |
|
|
); |
332 |
|
|
my $approvalresult = &getDump($ua,"PUTDUMP","request/$requestid/status", |
333 |
|
|
to_json(\%approval)); |
334 |
|
|
print "Approved: $approvalresult\n"; |
335 |
|
|
|
336 |
|
|
# save the full JSON answer, including requestID, for later perusal |
337 |
|
|
open FH,">$dir/response-approval.json" and do { |
338 |
|
|
print FH $approvalresult; |
339 |
|
|
close FH; |
340 |
|
|
}; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
exit 0; |
344 |
|
|
|
345 |
|
|
# ########################################################################### |
346 |
|
|
# |
347 |
|
|
|
348 |
|
|
# getDump($ua,"(GET|PUT|GETDUMP|PUTDUMP|POST|POSTDUMP)",$url,[$content]) |
349 |
|
|
# where the "DUMP" modes will return plain text from the answer, but |
350 |
|
|
# the default modes will return a perl object created from the JSON |
351 |
|
|
# |
352 |
|
|
sub getDump($$$$) { |
353 |
|
|
my ($ua,$type,$request,$content) = @_; |
354 |
|
|
my $data; |
355 |
|
|
$type = "GET" unless (defined $type and $type ne ""); |
356 |
|
|
die "Invalid call with GET and contents\n" |
357 |
|
|
if ( $type eq "GET" and defined $content and $content ne ""); |
358 |
|
|
|
359 |
|
|
my $outtype = $type; |
360 |
|
|
$type =~ s/DUMP$//; |
361 |
|
|
|
362 |
|
|
my $req = HTTP::Request->new($type => "$resturl/$request"); |
363 |
|
|
if ( ( $type eq "POST" || $type eq "PUT" ) |
364 |
|
|
and defined $content and $content ne "" ) { |
365 |
|
|
$req->content($content); |
366 |
|
|
} |
367 |
|
|
|
368 |
|
|
my $res = $ua->request($req); |
369 |
|
|
|
370 |
|
|
if ($res->is_success) { |
371 |
|
|
if ( $outtype =~ /DUMP/ ) { $data = $res->content; } |
372 |
|
|
else { $data = from_json($res->content); } |
373 |
|
|
} else { |
374 |
|
|
die "Invalid API call: ", $res->status_line, "\n"; |
375 |
|
|
} |
376 |
|
|
return $data; |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
# example of a specific API wrapper |
380 |
|
|
sub getContainerId($) { |
381 |
|
|
my ($ua) = @_; |
382 |
|
|
my $data; |
383 |
|
|
|
384 |
|
|
my $req = HTTP::Request->new(GET => "$resturl/user/me"); |
385 |
|
|
my $res = $ua->request($req); |
386 |
|
|
|
387 |
|
|
if ($res->is_success) { |
388 |
|
|
$data = from_json($res->content); |
389 |
|
|
} else { |
390 |
|
|
die "Invalid API call: ", $res->status_line, "\n"; |
391 |
|
|
} |
392 |
|
|
return $data->{container}{id}; |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
# ########################################################################### |
396 |
|
|
# Valid products |
397 |
|
|
# |
398 |
|
|
# 'name_id' => 'client_digital_signature_plus', |
399 |
|
|
# 'name_id' => 'client_email_security_plus', |
400 |
|
|
# 'name_id' => 'client_grid_premium', |
401 |
|
|
# 'name_id' => 'client_grid_robot_email', |
402 |
|
|
# 'name_id' => 'client_grid_robot_fqdn', |
403 |
|
|
# 'name_id' => 'client_grid_robot_name', |
404 |
|
|
# 'name_id' => 'client_premium', |
405 |
|
|
# 'name_id' => 'code_signing', |
406 |
|
|
# 'name_id' => 'code_signing_ev', |
407 |
|
|
# 'name_id' => 'document_signing_org_1', |
408 |
|
|
# 'name_id' => 'document_signing_org_2', |
409 |
|
|
# 'name_id' => 'grid_host_ssl', |
410 |
|
|
# 'name_id' => 'grid_host_ssl_multi_domain', |
411 |
|
|
# 'name_id' => 'ssl_ev_multi_domain', |
412 |
|
|
# 'name_id' => 'ssl_ev_plus', |
413 |
|
|
# 'name_id' => 'ssl_multi_domain', |
414 |
|
|
# 'name_id' => 'ssl_plus', |
415 |
|
|
# 'name_id' => 'ssl_wildcard', |
416 |
|
|
# |
417 |
|
|
|
418 |
|
|
# ########################################################################### |
419 |
|
|
# HELP |
420 |
|
|
|
421 |
|
|
sub help() { |
422 |
|
|
|
423 |
|
|
( my $base = $0 ) =~ s/^.*\///; |
424 |
|
|
print <<EOF; |
425 |
|
|
Request and retrieve certificates from the TCS DigiCert service via the API |
426 |
|
|
|
427 |
|
|
$base [-P product] [-R] [-s path] [-d basedir] [-A comment] [-K keyfile] |
428 |
|
|
[-O orgid] |
429 |
|
|
[-r|-i] hostname [altname ...] |
430 |
|
|
|
431 |
dennisvd |
3041 |
-r enter REQUEST mode + either -r, -i, or -a required |
432 |
davidg |
2774 |
-i enter INSTALLATION mode + |
433 |
dennisvd |
3041 |
-a enter APPROVE mode (admins only) |
434 |
davidg |
2774 |
|
435 |
|
|
-P <product> order <product>, with "grid_host_ssl_multi_domain" the default |
436 |
|
|
but "ssl_multi_domain" also useful. See below |
437 |
|
|
-V validity validity request period in years (default: 1) |
438 |
|
|
-K keyfile file with the API key for the user as a single line |
439 |
|
|
-O orgid Organisation name or ID |
440 |
|
|
-s subdir use <subdir> for key, cert, and orderid storage (no default) |
441 |
|
|
-R use the NDPF vlaai symlink & release.state mechanism |
442 |
|
|
which works best with subdir usage (will touch release.state) |
443 |
|
|
-A comment Approve request as well, with "comment" (admins only) |
444 |
|
|
--prefix=dir dir prefix (defaults to "tcs-") |
445 |
|
|
|
446 |
|
|
All certs are requested with SHA256 digest. Other products that might |
447 |
|
|
work with this script are: |
448 |
|
|
grid_host_ssl, grid_host_ssl_multi_domain |
449 |
|
|
ssl_ev_multi_domain, ssl_ev_plus |
450 |
|
|
ssl_multi_domain, ssl_plus |
451 |
|
|
But note that EV requires an extra approval step by the EV admin, and |
452 |
|
|
that wildcard certs will mess up the directory naming. |
453 |
|
|
EOF |
454 |
|
|
return 0; |
455 |
|
|
} |