forked from srvrco/getssl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
getssl
executable file
·3565 lines (3254 loc) · 141 KB
/
getssl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# getssl - Obtain SSL certificates from the letsencrypt.org ACME server
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License at <https://www.gnu.org/licenses/> for
# more details.
# For usage, run "getssl -h" or see https://github.com/srvrco/getssl
# ACMEv2 process is documented at https://tools.ietf.org/html/rfc8555#section-7.4
# Revision history:
# 2016-01-08 Created (v0.1)
# 2016-01-11 type correction and upload to github (v0.2)
# 2016-01-11 added import of any existing cert on -c option (v0.3)
# 2016-01-12 corrected formatting of imported certificate (v0.4)
# 2016-01-12 corrected error on removal of token in some instances (v0.5)
# 2016-01-18 corrected issue with removing tmp if run as root with the -c option (v0.6)
# 2016-01-18 added option to upload a single PEN file ( used by cpanel) (v0.7)
# 2016-01-23 added dns challenge option (v0.8)
# 2016-01-24 create the ACL directory if it does not exist. (v0.9) - dstosberg
# 2016-01-26 correcting a couple of small bugs and allow curl to follow redirects (v0.10)
# 2016-01-27 add a very basic openssl.cnf file if it doesn't exist and tidy code slightly (v0.11)
# 2016-01-28 Typo corrections, quoted file variables and fix bug on DNS_DEL_COMMAND (v0.12)
# 2016-01-28 changed DNS checks to use nslookup and allow hyphen in domain names (v0.13)
# 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge,
# 2016-01-29 add error_exit and cleanup help message (v0.14)
# 2016-01-29 added -a|--all option to renew all configured certificates (v0.15)
# 2016-01-29 added option for elliptic curve keys (v0.16)
# 2016-01-29 added server-type option to use and check cert validity from website (v0.17)
# 2016-01-30 added --quiet option for running in cron (v0.18)
# 2016-01-31 removed usage of xxd to make script more compatible across versions (v0.19)
# 2016-01-31 removed usage of base64 to make script more compatible across platforms (v0.20)
# 2016-01-31 added option to safe a full chain certificate (v0.21)
# 2016-02-01 commented code and added option for copying concatenated certs to file (v0.22)
# 2016-02-01 re-arrange flow for DNS-challenge, to reduce time taken (v0.23)
# 2016-02-04 added options for other server types (ldaps, or any port) and check_remote (v0.24)
# 2016-02-04 added short sleep following service restart before checking certs (v0.25)
# 2016-02-12 fix challenge token location when directory doesn't exist (v0.26)
# 2016-02-17 fix sed -E issue, and reduce length of renew check to 365 days for older systems (v0.27)
# 2016-04-05 Ensure DNS cleanup on error exit. (0.28) - pecigonzalo
# 2016-04-15 Remove NS Lookup of A record when using dns validation (0.29) - pecigonzalo
# 2016-04-17 Improving the wording in a couple of comments and info statements. (0.30)
# 2016-05-04 Improve check for if DNS_DEL_COMMAND is blank. (0.31)
# 2016-05-06 Setting umask to 077 for security of private keys etc. (0.32)
# 2016-05-20 update to reflect changes in staging ACME server json (0.33)
# 2016-05-20 tidying up checking of json following ACME changes. (0.34)
# 2016-05-21 added AUTH_DNS_SERVER to getssl.cfg as optional definition of authoritative DNS server (0.35)
# 2016-05-21 added DNS_WAIT to getssl.cfg as (default = 10 seconds as before) (0.36)
# 2016-05-21 added PUBLIC_DNS_SERVER option, for forcing use of an external DNS server (0.37)
# 2016-05-28 added FTP method of uploading tokens to remote server (blocked for certs as not secure) (0.38)
# 2016-05-28 added FTP method into the default config notes. (0.39)
# 2016-05-30 Add sftp with password to copy files (0.40)
# 2016-05-30 Add version check to see if there is a more recent version of getssl (0.41)
# 2016-05-30 Add [-u|--upgrade] option to automatically upgrade getssl (0.42)
# 2016-05-30 Added backup when auto-upgrading (0.43)
# 2016-05-30 Improvements to auto-upgrade (0.44)
# 2016-05-31 Improved comments - no structural changes
# 2016-05-31 After running for nearly 6 months, final testing prior to a 1.00 stable version. (0.90)
# 2016-06-01 Reorder functions alphabetically as part of code tidy. (0.91)
# 2016-06-03 Version 1.0 of code for release (1.00)
# 2016-06-09 bugfix of issue 44, and add success statement (ignoring quiet flag) (1.01)
# 2016-06-13 test return status of DNS_ADD_COMMAND and error_exit if a problem (hadleyrich) (1.02)
# 2016-06-13 bugfix of issue 45, problem with SERVER_TYPE when it's just a port number (1.03)
# 2016-06-13 bugfix issue 47 - DNS_DEL_COMMAND cleanup was run when not required. (1.04)
# 2016-06-15 add error checking on RELOAD_CMD (1.05)
# 2016-06-20 updated sed and date functions to run on MAC OS X (1.06)
# 2016-06-20 added CHALLENGE_CHECK_TYPE variable to allow checks direct on https rather than http (1.07)
# 2016-06-21 updated grep functions to run on MAC OS X (1.08)
# 2016-06-11 updated to enable running on windows with cygwin (1.09)
# 2016-07-02 Corrections to work with older slackware issue #56 (1.10)
# 2016-07-02 Updating help info re ACL in config file (1.11)
# 2016-07-04 adding DOMAIN_STORAGE as a variable to solve for issue #59 (1.12)
# 2016-07-05 updated order to better handle non-standard DOMAIN_STORAGE location (1.13)
# 2016-07-06 added additional comments about SANS in example template (1.14)
# 2016-07-07 check for duplicate domains in domain / SANS (1.15)
# 2016-07-08 modified to be used on older bash for issue #64 (1.16)
# 2016-07-11 added -w to -a option and comments in domain template (1.17)
# 2016-07-18 remove / regenerate csr when generating new private domain key (1.18)
# 2016-07-21 add output of combined private key and domain cert (1.19)
# 2016-07-21 updated typo (1.20)
# 2016-07-22 corrected issue in nslookup debug option - issue #74 (1.21)
# 2016-07-26 add more server-types based on openssl s_client (1.22)
# 2016-08-01 updated agreement for letsencrypt (1.23)
# 2016-08-02 updated agreement for letsencrypt to update automatically (1.24)
# 2016-08-03 improve messages on test of certificate installation (1.25)
# 2016-08-04 remove carriage return from agreement - issue #80 (1.26)
# 2016-08-04 set permissions for token folders - issue #81 (1.27)
# 2016-08-07 allow default chained file creation - issue #85 (1.28)
# 2016-08-07 use copy rather than move when archiving certs - issue #86 (1.29)
# 2016-08-07 enable use of a single ACL for all checks (if USE_SINGLE_ACL="true" (1.30)
# 2016-08-23 check for already validated domains (issue #93) - (1.31)
# 2016-08-23 updated already validated domains (1.32)
# 2016-08-23 included better force_renew and template for USE_SINGLE_ACL (1.33)
# 2016-08-23 enable insecure certificate on https token check #94 (1.34)
# 2016-08-23 export OPENSSL_CONF so it's used by all openssl commands (1.35)
# 2016-08-25 updated defaults for ACME agreement (1.36)
# 2016-09-04 correct issue #101 when some domains already validated (1.37)
# 2016-09-12 Checks if which is installed (1.38)
# 2016-09-13 Don't check for updates, if -U parameter has been given (1.39)
# 2016-09-17 Improved error messages from invalid certs (1.40)
# 2016-09-19 remove update check on recursive calls when using -a (1.41)
# 2016-09-21 changed shebang for portability (1.42)
# 2016-09-21 Included option to Deactivate an Authorization (1.43)
# 2016-09-22 retry on 500 error from ACME server (1.44)
# 2016-09-22 added additional checks and retry on 500 error from ACME server (1.45)
# 2016-09-24 merged in IPv6 support (1.46)
# 2016-09-27 added additional debug info issue #119 (1.47)
# 2016-09-27 removed IPv6 switch in favour of checking both IPv4 and IPv6 (1.48)
# 2016-09-28 Add -Q, or --mute, switch to mute notifications about successfully upgrading getssl (1.49)
# 2016-09-30 improved portability to work natively on FreeBSD, Slackware and Mac OS X (1.50)
# 2016-09-30 comment out PRIVATE_KEY_ALG from the domain template Issue #125 (1.51)
# 2016-10-03 check remote certificate for right domain before saving to local (1.52)
# 2016-10-04 allow existing CSR with domain name in subject (1.53)
# 2016-10-05 improved the check for CSR with domain in subject (1.54)
# 2016-10-06 prints update info on what was included in latest updates (1.55)
# 2016-10-06 when using -a flag, ignore folders in working directory which aren't domains (1.56)
# 2016-10-12 allow multiple tokens in DNS challenge (1.57)
# 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servers, not just one primary server (1.58)
# 2016-10-14 added archive of chain and private key for each cert, and purge old archives (1.59)
# 2016-10-17 updated info comment on failed cert due to rate limits. (1.60)
# 2016-10-17 fix error messages when using 1.0.1e-fips (1.61)
# 2016-10-20 set secure permissions when generating account key (1.62)
# 2016-10-20 set permissions to 700 for getssl script during upgrade (1.63)
# 2016-10-20 add option to revoke a certificate (1.64)
# 2016-10-21 set revocation server default to acme-v01.api.letsencrypt.org (1.65)
# 2016-10-21 bug fix for revocation on different servers. (1.66)
# 2016-10-22 Tidy up archive code for certificates and reduce permissions for security
# 2016-10-22 Add EC signing for secp384r1 and secp521r1 (the latter not yet supported by Let's Encrypt
# 2016-10-22 Add option to create a new private key for every cert (REUSE_PRIVATE_KEY="true" by default)
# 2016-10-22 Combine EC signing, Private key reuse and archive permissions (1.67)
# 2016-10-25 added CHECK_REMOTE_WAIT option ( to pause before final remote check)
# 2016-10-25 Added EC account key support ( prime256v1, secp384r1 ) (1.68)
# 2016-10-25 Ignore DNS_EXTRA_WAIT if all domains already validated (issue #146) (1.69)
# 2016-10-25 Add option for dual ESA / EDSA certs (1.70)
# 2016-10-25 bug fix Issue #141 challenge error 400 (1.71)
# 2016-10-26 check content of key files, not just recreate if missing.
# 2016-10-26 Improvements on portability (1.72)
# 2016-10-26 Date formatting for busybox (1.73)
# 2016-10-27 bug fix - issue #157 not recognising EC keys on some versions of openssl (1.74)
# 2016-10-31 generate EC account keys and tidy code.
# 2016-10-31 fix warning message if cert doesn't exist (1.75)
# 2016-10-31 remove only specified DNS token #161 (1.76)
# 2016-11-03 Reduce long lines, and remove echo from update (1.77)
# 2016-11-05 added TOKEN_USER_ID (to set ownership of token files )
# 2016-11-05 updated style to work with latest shellcheck (1.78)
# 2016-11-07 style updates
# 2016-11-07 bug fix DOMAIN_PEM_LOCATION starting with ./ #167
# 2016-11-08 Fix for openssl 1.1.0 #166 (1.79)
# 2016-11-08 Add and comment optional sshuserid for ssh ACL (1.80)
# 2016-11-09 Add SKIP_HTTP_TOKEN_CHECK option (Issue #170) (1.81)
# 2016-11-13 bug fix DOMAIN_KEY_CERT generation (1.82)
# 2016-11-17 add PREVENT_NON_INTERACTIVE_RENEWAL option (1.83)
# 2016-12-03 add HTTP_TOKEN_CHECK_WAIT option (1.84)
# 2016-12-03 bugfix CSR renewal when no SANS and when using MINGW (1.85)
# 2016-12-16 create CSR_SUBJECT variable - Issue #193
# 2016-12-16 added fullchain to archive (1.86)
# 2016-12-16 updated DOMAIN_PEM_LOCATION when using DUAL_RSA_ECDSA (1.87)
# 2016-12-19 allow user to ignore permission preservation with nfsv3 shares (1.88)
# 2016-12-19 bug fix for CA (1.89)
# 2016-12-19 included IGNORE_DIRECTORY_DOMAIN option (1.90)
# 2016-12-22 allow copying files to multiple locations (1.91)
# 2016-12-22 bug fix for copying tokens to multiple locations (1.92)
# 2016-12-23 tidy code - place default variables in alphabetical order.
# 2016-12-27 update checks to work with openssl in FIPS mode (1.93)
# 2016-12-28 fix leftover tmpfiles in upgrade routine (1.94)
# 2016-12-28 tidied up upgrade tmpfile handling (1.95)
# 2017-01-01 update comments
# 2017-01-01 create stable release 2.0 (2.00)
# 2017-01-02 Added option to limit number of old versions to keep (2.01)
# 2017-01-03 Created check_config function to list all obvious config issues (2.02)
# 2017-01-10 force renew if FORCE_RENEWAL file exists (2.03)
# 2017-01-12 added drill, dig or host as alternatives to nslookup (2.04)
# 2017-01-18 bugfix issue #227 - error deleting csr if doesn't exist
# 2017-01-18 issue #228 check private key and account key are different (2.05)
# 2017-01-21 issue #231 mingw bugfix and typos in debug messages (2.06)
# 2017-01-29 issue #232 use neutral locale for date formatting (2.07)
# 2017-01-30 issue #243 compatibility with bash 3.0 (2.08)
# 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09)
# 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10)
# 2018-01-04 updating to use the updated letsencrypt APIv2
# 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.11)
# 2019-10-02 issue #425 Case insensitive processing of agreement url because of HTTP/2 (2.12)
# 2019-10-07 update DNS checks to allow use of CNAMEs (2.13)
# 2019-11-18 Rebased master onto APIv2 and added Content-Type: application/jose+json (2.14)
# 2019-11-20 #453 and #454 Add User-Agent to all curl requests
# 2019-11-22 #456 Fix shellcheck issues
# 2019-11-23 #459 Fix missing chain.crt
# 2019-12-18 #462 Use POST-as-GET for ACMEv2 endpoints
# 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET)
# 2020-01-08 Error and exit if rate limited, exit if curl returns nothing
# 2020-01-10 Change domain and getssl templates to v2 (2.15)
# 2020-01-17 #473 and #477 Don't use POST-as-GET when sending ready for challenge for ACMEv1 (2.16)
# 2020-01-22 #475 and #483 Fix grep regex for >9 subdomains in json_get
# 2020-01-24 Add support for CloudDNS
# 2020-01-24 allow file transfer using WebDAV over HTTPS
# 2020-01-26 Use urlbase64_decode() instead of base64 -d
# 2020-01-26 Fix "already verified" error for ACMEv2
# 2020-01-29 Check awk new enough to support json_awk
# 2020-02-05 Fix epoch_date for busybox
# 2020-02-06 Bugfixes for json_awk and nslookup to support old awk versions (2.17)
# 2020-02-11 Add SCP_OPTS and SFTP_OPTS
# 2020-02-12 Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502)
# 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18)
# 2020-02-12 Fix "Registration key already in use" (2.19)
# 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20)
# 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424)
# 2020-02-23 Add dig to config check for systems without drill (ubuntu)
# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME
# 2020-03-12 Fix bug with DNS validation and multiple domains (#524)
# 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup)
# 2020-03-23 Fix staging server URL in domain template (2.21)
# 2020-03-30 Fix error message find_dns_utils from over version of "command"
# 2020-03-30 Fix problems if domain name isn't in lowercase (2.22)
# 2020-04-16 Add alternative working dirs '/etc/getssl/' '${PROGDIR}/conf' '${PROGDIR}/.getssl'
# 2020-04-16 Add -i|--install command line option (2.23)
# 2020-04-19 Remove dependency on seq, ensure clean_up doesn't try to delete /tmp (2.24)
# 2020-04-20 Check for domain using all DNS utilities (2.25)
# 2020-04-22 Fix HAS_HOST and HAS_NSLOOKUP checks - wolfaba
# 2020-04-22 Fix domain case conversion for different locales - glynge (2.26)
# 2020-04-26 Fixed ipv4 confirmation with nslookup - Cyber1000
# 2020-04-29 Fix ftp/sftp problems if challenge starts with a dash
# 2020-05-06 Fix missing fullchain.ec.crt when creating dual certificates (2.27)
# 2020-05-14 Add --notify-valid option (exit 2 if certificate is valid)
# 2020-05-23 Fix --revoke (didn't work with ACMEv02) (2.28)
# 2020-06-06 Fix missing URL_revoke definition when no CA directory suffix (#566)
# 2020-06-18 Fix CHECK_REMOTE for DUAL_RSA_ECDSA (#570)
# 2020-07-14 Support space separated SANS (#574) (2.29)
# 2020-08-06 Use -sigalgs instead of -cipher when checking remote for tls1.3 (#570)
# 2020-08-31 Fix slow fork bomb when directory containing getssl isn't writeable (#440)
# 2020-09-01 Use RSA-PSS when checking remote for DUAL_RSA_ECDSA (#570)
# 2020-09-02 Fix issue when SANS is space and comma separated (#579) (2.30)
# 2020-10-02 Various fixes to get_auth_dns and changes to support unit tests (#308)
# 2020-10-04 Add CHECK_PUBLIC_DNS_SERVER to check the DNS challenge has been updated there
# 2020-10-13 Bugfix: strip comments in drill/dig output (mhameed)
# 2020-11-18 Wildcard support (#347)(#400)(2.31)
# 2020-12-08 Fix mktemp template on alpine (#612)
# 2020-12-17 Fix delimiter issues with ${alldomains[]} in create_csr (#614)(vietw)
# 2020-12-18 Wrong SANS when domain contains a minus character (atisne)
# 2020-12-22 Fixes to get_auth_dns
# 2020-12-22 Check that dig doesn't return an error (#611)(2.32)
# 2020-12-29 Fix dig SOA lookup (#617)(2.33)
# 2021-01-05 Show error if running in POSIX mode (#611)
# 2021-01-16 Fix double slash when using root directory with DAVS (ionos)
# 2021-01-22 Add FTP_OPTIONS
# 2021-01-27 Add the ability to set several reload commands (atisne)
# 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630)
# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (Benno-K)(2.34)
# 2021-02-09 Prevent listing the complete file if version tag missing (#637)(softins)
# 2021-02-12 Add PREFERRED_CHAIN
# 2021-02-15 ADD ftp explicit SSL with curl for upload the challenge (CoolMischa)
# 2021-02-18 Add FULL_CHAIN_INCLUDE_ROOT
# 2021-03-25 Fix DNS challenge completion check if CNAMEs on different NS are used (sideeffect42)(2.35)
# 2021-05-08 Merge from tlhackque/getssl: GoDaddy, split-view, tempfile permissions fixes, --version(2.36)
# 2021-07-07 Request new certificate if SANs have changed (#669)(#673)
# 2021-07-12 Do not redirect outputs on remote commands when the debug option is used (atisne)
# 2021-07-20 Use +noidnout to enable certificates for IDN domains (#679)(2.37)
# 2021-07-22 Only pass +noidnout param to dig/drill(#682)(2.38)
# 2021-07-25 Fix copy_file_to_location failures with ssh when suffix applied to file lacking an extension (tlhackque)(#686)
# 2021-07-27 Support ftps:https://, FTPS_OPTIONS, remove default --insecure parameter to ftpes. Report caller(s) of error_exit in debug and test modes (tlhackque)(#687)(2.39)
# 2021-07-30 Prefer API V2 when both offered (tlhackque) (#690) (2.40)
# 2021-07-30 Run tests with -d to catch intermittent failures, Use fork's repo for upgrade tests. (tlhackque) (#692) (2.41)
# 2021-08-26 Improve upgrade check & make upgrade do a full install when possible (tlhackque) (#694) (2.42)
# 2021-09-02 Fix version compare - cURL v8 may have single digit minor numbers. (tlhackque) (2.43)
# 2021-09-26 Delete key file when key algorithm has changed (makuhama)
# 2021-09-30 better error if curl returns 60 (#709)
# 2021-10-01 Fix -preferred-chain argument (#712)
# 2021-10-01 Show help if no domain specified (#705)(2.44)
# 2021-10-08 Extract release tag from release api using awk (fix BSD issues)
# 2021-10-11 Fix broken upgrade url (#718)(2.45)
# 2021-10-22 Copy fullchain to DOMAIN_CHAIN_LOCATION (amartin-git)
# 2021-11-10 Detect Solaris and use gnu tools (#701)(miesi)
# 2021-11-12 Support acme-dns and fix CNAME issues (#722)(#308)
# 2021-12-14 Enhancements for GoDaddy (support more levels of domain names, no longer require GODADDY_BASE, and actual deletion of resource records)
# 2021-12-22 Don't show usage if run with --upgrade (#728)
# 2021-12-23 Don't use +idnout if dig shows a warning (#688)
# 2022-01-06 Support --account-id (#716)(2.46)
# 2022-03-09 Support for ISPConfig API
# 2022-05-03 Windows Server and IIS support (2.47)
# 2022-05-18 Add FTP_ARGS
# 2022-11-01 Add FTP_PORT
# 2023-02-04 Create newline to ensure [SAN] section can be parsed (#792)(MRigal)
# 2023-02-22 Remove cronie from deb package dependencies (2.48)
# ----------------------------------------------------------------------------------------
case :$SHELLOPTS: in
*:posix:*) echo -e "${0##*/}: Running with POSIX mode enabled is not supported" >&2; exit 1;;
esac
PROGNAME=${0##*/}
PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)"
VERSION="2.48"
# defaults
ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY_TYPE="rsa"
CA_CERT_LOCATION=""
CA="https://acme-staging-v02.api.letsencrypt.org/directory"
CHALLENGE_CHECK_TYPE="http"
CHECK_REMOTE_WAIT=0
CHECK_REMOTE="true"
if [[ -n "${GITHUB_REPOSITORY}" ]] ; then
CODE_LOCATION="https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/master/getssl"
RELEASE_API="https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest"
else
CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl"
RELEASE_API="https://api.github.com/repos/srvrco/getssl/releases/latest"
fi
CSR_SUBJECT="/"
CURL_USERAGENT="${PROGNAME}/${VERSION}"
DEACTIVATE_AUTH="false"
DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org"
DOMAIN_KEY_LENGTH=4096
DUAL_RSA_ECDSA="false"
FTP_OPTIONS=""
FTPS_OPTIONS=""
FTP_ARGS=""
FTP_PORT=""
FULL_CHAIN_INCLUDE_ROOT="false"
GETSSL_IGNORE_CP_PRESERVE="false"
HTTP_TOKEN_CHECK_WAIT=0
IGNORE_DIRECTORY_DOMAIN="false"
OCSP_MUST_STAPLE="false"
ORIG_UMASK=$(umask)
PREFERRED_CHAIN="" # Set this to use an alternative root certificate
PREVIOUSLY_VALIDATED="true"
PRIVATE_KEY_ALG="rsa"
RELOAD_CMD=""
RENEW_ALLOW="30"
REUSE_PRIVATE_KEY="true"
SERVER_TYPE="https"
SKIP_HTTP_TOKEN_CHECK="false"
SSLCONF="$(openssl version -d 2>/dev/null| cut -d\" -f2)/openssl.cnf"
TOKEN_USER_ID=""
USE_SINGLE_ACL="false"
WORKING_DIR_CANDIDATES=("/etc/getssl" "${PROGDIR}/conf" "${PROGDIR}/.getssl" "${HOME}/.getssl")
# Variables used when validating using a DNS entry
VALIDATE_VIA_DNS="" # Set this to "true" to enable DNS validation
export AUTH_DNS_SERVER="" # Use this DNS server to check the challenge token has been set
export DNS_CHECK_OPTIONS="" # Options (such as TSIG file) required by DNS_CHECK_FUNC
export PUBLIC_DNS_SERVER="" # Use this DNS server to find the authoritative DNS servers for the domain
CHECK_ALL_AUTH_DNS="false" # Check the challenge token has been set on all authoritative DNS servers
CHECK_PUBLIC_DNS_SERVER="true" # Check the public DNS server as well as the authoritative DNS servers
DNS_ADD_COMMAND="" # Use this command/script to add the challenge token to the DNS entries for the domain
DNS_DEL_COMMAND="" # Use this command/script to remove the challenge token from the DNS entries for the domain
DNS_WAIT_COUNT=100 # How many times to wait for the DNS record to update
DNS_WAIT=10 # How long to wait before checking the DNS record again
DNS_EXTRA_WAIT=60 # How long to wait after the DNS entries are visible to us before telling the ACME server to check.
DNS_WAIT_RETRY_ADD="false" # Try the dns_add_command again if the DNS record hasn't updated
# Private variables
_CHECK_ALL=0
_CREATE_CONFIG=0
_CURL_VERSION=""
_FORCE_RENEW=0
_MUTE=0
_NOTIFY_VALID=0
_NOMETER=""
_QUIET=0
_RECREATE_CSR=0
_REDIRECT_OUTPUT="1>/dev/null 2>&1"
_REVOKE=0
_SHOW_ACCOUNT_ID=0
_TEST_SKIP_CNAME_CALL=0
_TEST_SKIP_SOA_CALL=0
_UPGRADE=0
_UPGRADE_CHECK=1
_UPGRADE_TO_TAG=""
_USE_DEBUG=0
_ONLY_CHECK_CONFIG=0
config_errors="false"
export LANG=C
API=1
# store copy of original command in case of upgrading script and re-running
ORIGCMD="$0 $*"
# Define all functions (in alphabetical order)
auto_upgrade_v2() { # Automatically update clients to v2
if [[ "${CA}" == *"acme-v01."* ]] || [[ "${CA}" == *"acme-staging."* ]]; then
OLDCA=${CA}
# shellcheck disable=SC2001
CA=$(echo "${OLDCA}" | sed "s/v01/v02/g")
# shellcheck disable=SC2001
CA=$(echo "${CA}" | sed "s/staging/staging-v02/g")
info "Upgraded to v2 (changed ${OLDCA} to ${CA})"
fi
debug "Using certificate issuer: ${CA}"
}
cert_archive() { # Archive certificate file by copying files to dated archive dir.
debug "creating an archive copy of current new certs"
date_time=$(date +%Y_%m_%d_%H_%M)
mkdir -p "${DOMAIN_DIR}/archive/${date_time}"
umask 077
cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.crt"
cp "$DOMAIN_DIR/${DOMAIN}.csr" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.csr"
cp "$DOMAIN_DIR/${DOMAIN}.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.key"
cp "$CA_CERT" "${DOMAIN_DIR}/archive/${date_time}/chain.crt"
cat "$CERT_FILE" "$CA_CERT" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.crt"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
cp "${CERT_FILE%.*}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.crt"
cp "$DOMAIN_DIR/${DOMAIN}.ec.csr" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.csr"
cp "$DOMAIN_DIR/${DOMAIN}.ec.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.key"
cp "${CA_CERT%.*}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/chain.ec.crt"
cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.ec.crt"
fi
umask "$ORIG_UMASK"
debug "purging old GetSSL archives"
purge_archive "$DOMAIN_DIR"
}
base64url_decode() {
awk '{ if (length($0) % 4 == 3) print $0"="; else if (length($0) % 4 == 2) print $0"=="; else print $0; }' | tr -- '-_' '+/' | base64 -d
}
cert_install() { # copy certs to the correct location (creating concatenated files as required)
umask 077
copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION"
copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION"
copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then
copy_file_to_location "ec domain certificate" \
"${CERT_FILE%.*}.ec.crt" \
"${DOMAIN_CERT_LOCATION}" \
"ec"
fi
if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then
copy_file_to_location "ec private key" \
"$DOMAIN_DIR/${DOMAIN}.ec.key" \
"${DOMAIN_KEY_LOCATION}" \
"ec"
fi
if [[ -n "$CA_CERT_LOCATION" ]]; then
copy_file_to_location "ec CA certificate" \
"${CA_CERT%.*}.ec.crt" \
"${CA_CERT_LOCATION%.*}.crt" \
"ec"
fi
fi
# if DOMAIN_CHAIN_LOCATION is not blank, then create and copy file.
if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then
if [[ "$(dirname "$DOMAIN_CHAIN_LOCATION")" == "." ]]; then
to_location="${DOMAIN_DIR}/${DOMAIN_CHAIN_LOCATION}"
else
to_location="${DOMAIN_CHAIN_LOCATION}"
fi
cat "$FULL_CHAIN" > "$TEMP_DIR/${DOMAIN}_chain.pem"
copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec"
copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}" "ec"
fi
fi
# if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file.
if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then
if [[ "$(dirname "$DOMAIN_KEY_CERT_LOCATION")" == "." ]]; then
to_location="${DOMAIN_DIR}/${DOMAIN_KEY_CERT_LOCATION}"
else
to_location="${DOMAIN_KEY_CERT_LOCATION}"
fi
cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem"
copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec"
copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}" "ec"
fi
fi
# if DOMAIN_PEM_LOCATION is not blank, then create and copy file.
if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then
if [[ "$(dirname "$DOMAIN_PEM_LOCATION")" == "." ]]; then
to_location="${DOMAIN_DIR}/${DOMAIN_PEM_LOCATION}"
else
to_location="${DOMAIN_PEM_LOCATION}"
fi
cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem"
copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location"
if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec"
copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}" "ec"
fi
fi
# end of copying certs.
umask "$ORIG_UMASK"
}
check_challenge_completion() { # checks with the ACME server if our challenge is OK
uri=$1
domain=$2
keyauthorization=$3
info "sending request to ACME server saying we're ready for challenge"
# check response from our request to perform challenge
if [[ $API -eq 1 ]]; then
send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
if [[ -n "$code" ]] && [[ ! "$code" == '202' ]] ; then
error_exit "$domain:Challenge error: $code"
fi
else # APIv2
send_signed_request "$uri" "{}"
if [[ -n "$code" ]] && [[ ! "$code" == '200' ]] ; then
detail=$(echo "$response" | grep "detail" | awk -F\" '{print $4}')
error_exit "$domain:Challenge error: $code:Detail: $detail"
fi
fi
# loop "forever" to keep checking for a response from the ACME server.
while true ; do
info "checking if challenge is complete"
if [[ $API -eq 1 ]]; then
if ! get_cr "$uri" ; then
error_exit "$domain:Verify error:$code"
fi
else # APIv2
send_signed_request "$uri" ""
fi
status=$(json_get "$response" status)
# If ACME response is valid, then break out of loop
if [[ "$status" == "valid" ]] ; then
info "Verified $domain"
break;
fi
# if ACME response is that their check gave an invalid response, error exit
if [[ "$status" == "invalid" ]] ; then
err_detail=$(echo "$response" | grep "detail")
# TODO need to check for "DNS problem: SERVFAIL looking up CAA ..." and retry
error_exit "$domain:Verify error:$err_detail"
fi
# if ACME response is pending (they haven't completed checks yet)
# or valid (completed checks but not created certificate) then wait and try again.
if [[ "$status" == "pending" ]] || [[ "$status" == "valid" ]]; then
info "Pending"
else
err_detail=$(echo "$response" | grep "detail")
error_exit "$domain:Verify error:$status:$err_detail"
fi
debug "sleep 5 secs before testing verify again"
sleep 5
done
}
check_challenge_completion_dns() { # perform validation via DNS challenge
d=${1}
rr=${2}
primary_ns=${3}
auth_key=${4}
# check for token at public dns server, waiting for a valid response.
for ns in $primary_ns; do
info "checking DNS at $ns"
# add +noidnout if idn-domain so search for domain in results works
if [[ "${d}" == xn--* || "${d}" == *".xn--"* ]]; then
if [[ "$DNS_CHECK_FUNC" == "nslookup" || "$DNS_CHECK_FUNC" == "host" || ("$DNS_CHECK_FUNC" == "$HAS_DIG_OR_DRILL" && "$DIG_SUPPORTS_NOIDNOUT" == "false") ]]; then
info "Info: idn domain but $DNS_CHECK_FUNC doesn't support +noidnout"
else
debug "adding +noidnout to DNS_CHECK_OPTIONS"
DNS_CHECK_OPTIONS="$DNS_CHECK_OPTIONS +noidnout"
fi
fi
ntries=0
check_dns="fail"
while [[ "$check_dns" == "fail" ]]; do
if [[ "$os" == "cygwin" || "$os" == "mingw64_nt" || "$os" == "msys_nt" ]]; then
check_result=$(nslookup -type=txt "${rr}." "${ns}" \
| grep ^_acme -A2\
| grep '"'|awk -F'"' '{ print $2}')
elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then
# shellcheck disable=SC2086
debug "$DNS_CHECK_FUNC" $DNS_CHECK_OPTIONS TXT "${rr}" "@${ns}"
# shellcheck disable=SC2086
check_output=$($DNS_CHECK_FUNC $DNS_CHECK_OPTIONS TXT "${rr}" "@${ns}")
check_result=$(grep -i "^${rr}"<<<"${check_output}"|grep 'IN\WTXT'|awk -F'"' '{ print $2}')
debug "check_result=\"$check_result\""
# Check if rr is a CNAME
if [[ -z "$check_result" ]]; then
rr_cname=$(grep -i "^${rr}"<<<"${check_output}"|grep 'IN\WCNAME'|awk '{ print $5}')
debug "cname check=\"$rr_cname\""
if [[ -n "$rr_cname" ]]; then
check_result=$(grep -i "^${rr_cname}"<<<"${check_output}"|grep 'IN\WTXT'|awk -F'"' '{ print $2}' | uniq)
fi
fi
if [[ -z "$check_result" ]]; then
# shellcheck disable=SC2086
debug "$DNS_CHECK_FUNC" $DNS_CHECK_OPTIONS ANY "${rr}" "@${ns}"
# shellcheck disable=SC2086
check_result=$($DNS_CHECK_FUNC $DNS_CHECK_OPTIONS ANY "${rr}" "@${ns}" \
| grep -i "^${rr}" \
| grep 'IN\WTXT'|awk -F'"' '{ print $2}')
debug "check_result=\"$check_result\""
fi
elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then
debug "$DNS_CHECK_FUNC" -t TXT "${rr}" "${ns}"
check_result=$($DNS_CHECK_FUNC -t TXT "${rr}" "${ns}" \
| grep 'descriptive text'|awk -F'"' '{ print $2}')
debug "check_result=\"$check_result\""
else
debug "$DNS_CHECK_FUNC" -type=txt "${rr}" "${ns}"
check_result=$(nslookup -type=txt "${rr}" "${ns}" \
| grep 'text ='|awk -F'"' '{ print $2}')
debug "check_result=\"$check_result\""
if [[ -z "$check_result" ]]; then
debug "$DNS_CHECK_FUNC" -type=any "${rr}" "${ns}"
check_result=$(nslookup -type=any "${rr}" "${ns}" \
| grep 'text ='|awk -F'"' '{ print $2}')
debug "check_result=\"$check_result\""
fi
fi
debug "expecting \"$auth_key\""
debug "${ns} gave ... \"$check_result\""
if [[ "$check_result" == *"$auth_key"* ]]; then
check_dns="success"
else
if [[ $ntries -lt $DNS_WAIT_COUNT ]]; then
ntries=$(( ntries + 1 ))
if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then
debug "Deleting DNS via command: ${DNS_DEL_COMMAND}"
del_dns_rr "${d}" "${auth_key}"
debug "Retrying adding DNS via command: ${DNS_ADD_COMMAND}"
add_dns_rr "${d}" "${auth_key}" \
|| error_exit "DNS_ADD_COMMAND failed for domain ${d}"
fi
info "checking DNS at ${ns} for ${rr}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\
"waiting $DNS_WAIT secs before checking again"
sleep $DNS_WAIT
else
debug "dns check failed - removing existing value"
del_dns_rr "${d}" "${auth_key}"
error_exit "checking \"${rr}\" gave \"$check_result\" not \"$auth_key\""
fi
fi
done
done
if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then
info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME server to check the dns"
sleep "$DNS_EXTRA_WAIT"
fi
}
# end of ... perform validation if via DNS challenge
check_config() { # check the config files for all obvious errors
debug "checking config"
# check keys
case "$ACCOUNT_KEY_TYPE" in
rsa|prime256v1|secp384r1|secp521r1)
debug "checked ACCOUNT_KEY_TYPE " ;;
*)
info "${DOMAIN}: invalid ACCOUNT_KEY_TYPE - $ACCOUNT_KEY_TYPE"
config_errors=true ;;
esac
if [[ "$ACCOUNT_KEY" == "$DOMAIN_DIR/${DOMAIN}.key" ]]; then
info "${DOMAIN}: ACCOUNT_KEY and domain key ( $DOMAIN_DIR/${DOMAIN}.key ) must be different"
config_errors=true
fi
case "$PRIVATE_KEY_ALG" in
rsa|prime256v1|secp384r1|secp521r1)
debug "checked PRIVATE_KEY_ALG " ;;
*)
info "${DOMAIN}: invalid PRIVATE_KEY_ALG - '$PRIVATE_KEY_ALG'"
config_errors=true ;;
esac
if [[ "$DUAL_RSA_ECDSA" == "true" ]] && [[ "$PRIVATE_KEY_ALG" == "rsa" ]]; then
info "${DOMAIN}: PRIVATE_KEY_ALG not set to an EC type and DUAL_RSA_ECDSA=\"true\""
config_errors=true
fi
# get all domains into an array
if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then
read -r -a alldomains <<< "${SANS//[, ]/ }"
else
read -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")"
fi
if [[ -z "${alldomains[*]}" ]]; then
info "${DOMAIN}: no domains specified"
config_errors=true
fi
if [[ $VALIDATE_VIA_DNS == "true" ]]; then # using dns-01 challenge
if [[ -z "$DNS_ADD_COMMAND" ]]; then
info "${DOMAIN}: DNS_ADD_COMMAND not defined (whilst VALIDATE_VIA_DNS=\"true\")"
config_errors=true
fi
if [[ -z "$DNS_DEL_COMMAND" ]]; then
info "${DOMAIN}: DNS_DEL_COMMAND not defined (whilst VALIDATE_VIA_DNS=\"true\")"
config_errors=true
fi
fi
dn=0
tmplist=$(mktemp 2>/dev/null || mktemp -t getssl.XXXXXX) || error_exit "mktemp failed"
for d in "${alldomains[@]}"; do # loop over domains (dn is domain number)
debug "checking domain $d"
if [[ "$(grep "^${d}$" "$tmplist")" = "$d" ]]; then
info "${DOMAIN}: $d appears to be duplicated in domain, SAN list"
config_errors=true
elif [[ "$d" != "${d##\*.}" ]] && [[ "$VALIDATE_VIA_DNS" != "true" ]]; then
info "${DOMAIN}: cannot use http-01 validation for wildcard domains"
config_errors=true
else
echo "$d" >> "$tmplist"
fi
if [[ "$USE_SINGLE_ACL" == "true" ]]; then
DOMAIN_ACL="${ACL[0]}"
else
DOMAIN_ACL="${ACL[$dn]}"
fi
if [[ $VALIDATE_VIA_DNS != "true" ]]; then # using http-01 challenge
if [[ -z "${DOMAIN_ACL}" ]]; then
info "${DOMAIN}: ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg"
config_errors=true
fi
# check domain exists using all DNS utilities. DNS_CHECK_OPTIONS may bind IP address or provide TSIG
found_ip=false
if [[ -n "$HAS_DIG_OR_DRILL" ]]; then
# add +noidnout if idn-domain so search for domain in results works
DIG_CHECK_OPTIONS="$DNS_CHECK_OPTIONS"
if [[ ("${d}" == xn--* || "${d}" == *".xn--"* ) && "$DIG_SUPPORTS_NOIDNOUT" == "true" ]]; then
DIG_CHECK_OPTIONS="$DNS_CHECK_OPTIONS +noidnout"
fi
debug "DNS lookup using $HAS_DIG_OR_DRILL $DIG_CHECK_OPTIONS ${d}"
# shellcheck disable=SC2086
if [[ "$($HAS_DIG_OR_DRILL $DIG_CHECK_OPTIONS -t SOA "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then
found_ip=true
elif [[ "$($HAS_DIG_OR_DRILL $DIG_CHECK_OPTIONS -t A "${d}"|grep -c -i "^${d}")" -ge 1 ]]; then
found_ip=true
elif [[ "$($HAS_DIG_OR_DRILL $DIG_CHECK_OPTIONS -t AAAA "${d}"|grep -c -i "^${d}")" -ge 1 ]]; then
found_ip=true
fi
fi
if [[ "$HAS_HOST" == "true" ]]; then
debug "DNS lookup using host $DNS_CHECK_OPTIONS ${d}"
# shellcheck disable=SC2086
if [[ "$(host $DNS_CHECK_OPTIONS "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then
found_ip=true
fi
fi
if [[ "$HAS_NSLOOKUP" == "true" ]]; then
debug "DNS lookup using nslookup $DNS_CHECK_OPTIONS -query AAAA ${d}"
# shellcheck disable=SC2086
if [[ "$(nslookup $DNS_CHECK_OPTIONS -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then
debug "found IPv6 record for ${d}"
found_ip=true
elif [[ "$(nslookup $DNS_CHECK_OPTIONS "${d}"| grep -c ^Name)" -ge 1 ]]; then
debug "found IPv4 record for ${d}"
found_ip=true
fi
fi
if [[ "$found_ip" == "false" ]]; then
info "${DOMAIN}: DNS lookup failed for $d"
config_errors=true
fi
fi # end using dns-01 challenge
((dn++))
done
# tidy up
rm -f "$tmplist"
if [[ "$config_errors" == "true" ]]; then
error_exit "${DOMAIN}: exiting due to config errors"
fi
debug "${DOMAIN}: check_config completed - all OK"
}
check_getssl_upgrade() { # check if a more recent release is available
# Check GitHub for latest stable release, or a specified tag
if [[ -n "$_UPGRADE_TO_TAG" ]]; then
RELEASE_API="$RELEASE_API/tags/$_UPGRADE_TO_TAG"
fi
local release_data release_tag release_ver local_ver release_desc NEWCMD
debug "Checking for releases at $RELEASE_API"
# shellcheck disable=SC2086
release_data="$(curl ${_NOMETER:---silent} --user-agent "$CURL_USERAGENT" -H 'Accept: application/vnd.github.v3+json' "$RELEASE_API")"
errcode=$?
if [[ $errcode -eq 60 ]]; then
error_exit "curl needs updating, your version does not support SNI (multiple SSL domains on a single IP)"
elif [[ $errcode -gt 0 ]]; then
error_exit "curl error checking releases: $errcode"
fi
# Replace error in release description with _error (which is ignored by check_output_for_errors() in the tests)
sanitised_release_data=${release_data//error/_error}
sanitised_release_data=${sanitised_release_data//warning/_warning}
debug "${sanitised_release_data//error/_error}"
# awk from https://stackoverflow.com/questions/1761341/awk-print-next-record-following-matched-record
release_tag=$(awk -F'"' '/tag_name/ {f=NR} f&&NR-1==f' RS=":|," <<<"${release_data}" | sed -e's/"//g')
if [[ "${release_tag:0:1}" != 'v' ]] ; then
if [[ ${_MUTE} -eq 0 ]]; then
info "The current repository has no releases or is improperly tagged; can't check for upgrades: '$release_tag'"
fi
return 0
fi
release_ver="$( tr -d '.v' <<<"${release_tag}")"
local_ver="$( tr -d '.' <<<"${VERSION}")"
debug "current code is version ${VERSION}"
debug "Most recent version is ${release_tag:1}"
if [[ -z "$_UPGRADE_TO_TAG" ]] ; then
if [[ "$local_ver" -ge "$release_ver" ]] ; then return 0; fi
else
if [[ "$local_ver" -eq "$release_ver" ]] ; then return 0; fi
fi
if [[ ${_UPGRADE} -ne 1 ]]; then
if [[ ${_MUTE} -eq 0 ]]; then
release_desc="$(sed -e'/^"body": *"/!d;s/^"body": *"\([^""]*\).*$/\1/;s/\\r/\r/g;s/\\n/\n/g' <<<"$release_data")"
info ""
info "A more recent version (${release_tag}) than $VERSION of getssl is available, please update"
info "The easiest way is to use the -u or --upgrade flag"
info ""
info "Release ${release_tag} summary"
# Replace error in release description with _error (which is ignored by check_output_for_errors() in the tests)
info "${release_desc//error/_error}"
info ""
fi
return 0;
fi
# Download the latest tag
TEMP_UPGRADE_FILE="$(mktemp 2>/dev/null || mktemp -t getssl.XXXXXX)"
if [ "$TEMP_UPGRADE_FILE" == "" ]; then
error_exit "mktemp failed"
fi
CODE_LOCATION=$(sed -e"s/master/${release_tag}/" <<<"$CODE_LOCATION")
# shellcheck disable=SC2086
debug curl ${_NOMETER:---silent} --user-agent "$CURL_USERAGENT" "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE"
# shellcheck disable=SC2086
status=$(curl ${_NOMETER:---silent} -w "%{http_code}" --user-agent "$CURL_USERAGENT" "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE")
errcode=$?
debug curl errcode=$errcode
if [[ $errcode -eq 60 ]]; then
error_exit "curl needs updating, your version does not support SNI (multiple SSL domains on a single IP)"
elif [[ $errcode -gt 0 ]]; then
error_exit "curl error downloading release: $errcode"
fi
if [[ $status -ne 200 ]]; then
error_exit "curl didn't find the updated version of getssl at $CODE_LOCATION"
fi
if ! install "$0" "${0}.v${VERSION}"; then
error_exit "problem renaming old version while updating, check permissions"
fi
if ! install -m 700 "$TEMP_UPGRADE_FILE" "$0"; then
error_exit "problem installing new version while updating, check permissions"
fi
check=$(bash "$0" -U -v)
release_tag_upper=$(echo "$release_tag" | tr "[:lower:]" "[:upper:]")
if [[ "$check" != "getssl ${release_tag_upper}" ]]; then
info "problem running new version, rolling back to old version"
if ! install "${0}.v${VERSION}" "$0"; then
error_exit "problem rolling back, you'll need to manually check $0 and $0.${VERSION}"
fi
error_exit "problem calling new version; output of $TEMP_UPGRADE_FILE -v was \"$check\", expected \"getssl ${release_tag_upper}\""
fi
if [[ ${_MUTE} -eq 0 ]]; then
echo "Updated getssl from v${VERSION} to ${release_tag}"
echo "The old version remains as ${0}.v${VERSION} and should be removed"
echo "These update notifications can be turned off using the -Q option"
echo ""
echo "Updates are:"
awk "/\(${VERSION}\)$/ {s=1} s; /\(${release_tag}\)$/ || /^# ----/ {s=0}" "$TEMP_UPGRADE_FILE" | awk '{if(NR>1)print}'
echo ""
fi
# Delete old versions, but not the version just upgraded (which can't be removed since disappearing can confuse bash)
declare -a getssl_versions
shopt -s nullglob
for getssl_version in "$0".v*; do
if [[ "$getssl_version" != "${0}.v${VERSION}" ]] ; then
getssl_versions[${#getssl_versions[@]}]="$getssl_version"
fi
done
shopt -u nullglob
if [[ -n "${getssl_versions[*]}" ]] ; then
rm "${getssl_versions[@]}"
fi
# Inhibit check for upgrades when running the new version
NEWCMD="$(sed -e's/ -\(u\|-upgrade\|U\|-nocheck\)//g;s/^\([^ ]* \)/\1--nocheck /' <<<"$ORIGCMD")"
clean_up
if [[ ${_MUTE} -eq 0 ]]; then
info "Installed $release_tag, restarting with $NEWCMD"
fi
if ! eval "$NEWCMD"; then
error_exit "Running upgraded getssl failed"
fi
graceful_exit
}
check_version() { # true if version string $1 >= $2
local v1 v2 i n1 n2 n
# $1 and $2 can be different lengths, but all parts must be numeric
if [[ "$1" == "$2" ]] ; then return 0; fi
local IFS='.'
# shellcheck disable=SC2206
v1=($1)
# shellcheck disable=SC2206
v2=($2)
n1="${#v1[@]}"
n2="${#v2[@]}"
if [[ "$n1" -ge "$n2" ]] ; then n="$n1" ; else n="$n2" ; fi
for ((i=0; i<n; i++)) do
n1="${v1[$i]:-0}"
n2="${v2[$i]:-0}"
if [[ $((10#$n1)) -gt $((10#$n2)) ]] ; then return 0 ; fi
if [[ $((10#$n1)) -lt $((10#$n2)) ]] ; then return 1 ; fi
done
return 0
}
clean_up() { # Perform pre-exit housekeeping
umask "$ORIG_UMASK"
if [[ $VALIDATE_VIA_DNS == "true" ]]; then
# Tidy up DNS entries if things failed part way though.
shopt -s nullglob
for dnsfile in "$TEMP_DIR"/dns_verify/*; do
# shellcheck source=/dev/null
. "$dnsfile"
debug "attempting to clean up DNS entry for $d"
del_dns_rr "${d}" "${auth_key}"
done
shopt -u nullglob
fi
if [[ -n "$DOMAIN_DIR" ]]; then
if [ "${TEMP_DIR}" -ef "/tmp" ]; then
info "Not going to delete TEMP_DIR ${TEMP_DIR} as it appears to be /tmp"
else
rm -rf "${TEMP_DIR:?}"
fi
fi
}
copy_file_to_location() { # copies a file, using scp, sftp or ftp if required.
cert=$1 # descriptive name, just used for display
from=$2 # current file location
to=$3 # location to move file to.
suffix=$4 # (optional) optional suffix for DUAL_RSA_ECDSA, i.e. save to private.key becomes save to private.ec.key
IFS=\; read -r -a copy_locations <<<"$3"
for to in "${copy_locations[@]}"; do
if [[ -n "$suffix" ]]; then
bname="$(basename "$to")"
if [[ "${bname##*.}" == "$bname" ]]; then
to="${to}.${suffix}"
else
to="${to%.*}.${suffix}.${to##*.}"
fi
fi
info "copying $cert to $to"
if [[ "${to:0:4}" == "ssh:" ]] ; then
debug "using scp -q $SCP_OPTS $from ${to:4}"
# shellcheck disable=SC2086
if ! scp -q $SCP_OPTS "$from" "${to:4}" >/dev/null 2>&1 ; then
error_exit "problem copying file to the server using scp.
scp $from ${to:4}"
fi
debug "userid $TOKEN_USER_ID"
if [[ "$cert" == "challenge token" ]] && [[ -n "$TOKEN_USER_ID" ]]; then
servername=$(echo "$to" | awk -F":" '{print $2}')
tofile=$(echo "$to" | awk -F":" '{print $3}')
debug "servername $servername"
debug "file $tofile"