]> git.proxmox.com Git - mirror_acme.sh.git/blob - acme.sh
Merge pull request #975 from Neilpang/dev
[mirror_acme.sh.git] / acme.sh
1 #!/usr/bin/env sh
2
3 VER=2.7.3
4
5 PROJECT_NAME="acme.sh"
6
7 PROJECT_ENTRY="acme.sh"
8
9 PROJECT="https://github.com/Neilpang/$PROJECT_NAME"
10
11 DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME"
12 _SCRIPT_="$0"
13
14 _SUB_FOLDERS="dnsapi deploy"
15
16 _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
17 DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory"
18 DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
19
20 DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
21 DEFAULT_ACCOUNT_EMAIL=""
22
23 DEFAULT_ACCOUNT_KEY_LENGTH=2048
24 DEFAULT_DOMAIN_KEY_LENGTH=2048
25
26 DEFAULT_OPENSSL_BIN="openssl"
27
28 STAGE_CA="https://acme-staging.api.letsencrypt.org/directory"
29 _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
30
31 VTYPE_HTTP="http-01"
32 VTYPE_DNS="dns-01"
33 VTYPE_TLS="tls-sni-01"
34 #VTYPE_TLS2="tls-sni-02"
35
36 LOCAL_ANY_ADDRESS="0.0.0.0"
37
38 MAX_RENEW=60
39
40 DEFAULT_DNS_SLEEP=120
41
42 NO_VALUE="no"
43
44 W_TLS="tls"
45
46 MODE_STATELESS="stateless"
47
48 STATE_VERIFIED="verified_ok"
49
50 NGINX="nginx:"
51 NGINX_START="#ACME_NGINX_START"
52 NGINX_END="#ACME_NGINX_END"
53
54 BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
55 END_CSR="-----END CERTIFICATE REQUEST-----"
56
57 BEGIN_CERT="-----BEGIN CERTIFICATE-----"
58 END_CERT="-----END CERTIFICATE-----"
59
60 RENEW_SKIP=2
61
62 ECC_SEP="_"
63 ECC_SUFFIX="${ECC_SEP}ecc"
64
65 LOG_LEVEL_1=1
66 LOG_LEVEL_2=2
67 LOG_LEVEL_3=3
68 DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
69
70 DEBUG_LEVEL_1=1
71 DEBUG_LEVEL_2=2
72 DEBUG_LEVEL_3=3
73 DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
74 DEBUG_LEVEL_NONE=0
75
76 HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
77
78 SYSLOG_ERROR="user.error"
79 SYSLOG_INFO="user.info"
80 SYSLOG_DEBUG="user.debug"
81
82 #error
83 SYSLOG_LEVEL_ERROR=3
84 #info
85 SYSLOG_LEVEL_INFO=6
86 #debug
87 SYSLOG_LEVEL_DEBUG=7
88 #debug2
89 SYSLOG_LEVEL_DEBUG_2=8
90 #debug3
91 SYSLOG_LEVEL_DEBUG_3=9
92
93 SYSLOG_LEVEL_DEFAULT=$SYSLOG_LEVEL_ERROR
94 #none
95 SYSLOG_LEVEL_NONE=0
96
97 _DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh"
98
99 _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations"
100
101 _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
102
103 __INTERACTIVE=""
104 if [ -t 1 ]; then
105 __INTERACTIVE="1"
106 fi
107
108 __green() {
109 if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
110 printf '\033[1;31;32m'
111 fi
112 printf -- "%b" "$1"
113 if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
114 printf '\033[0m'
115 fi
116 }
117
118 __red() {
119 if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
120 printf '\033[1;31;40m'
121 fi
122 printf -- "%b" "$1"
123 if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" ]; then
124 printf '\033[0m'
125 fi
126 }
127
128 _printargs() {
129 if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
130 printf -- "%s" "[$(date)] "
131 fi
132 if [ -z "$2" ]; then
133 printf -- "%s" "$1"
134 else
135 printf -- "%s" "$1='$2'"
136 fi
137 printf "\n"
138 }
139
140 _dlg_versions() {
141 echo "Diagnosis versions: "
142 echo "openssl:$ACME_OPENSSL_BIN"
143 if _exists "${ACME_OPENSSL_BIN:-openssl}"; then
144 ${ACME_OPENSSL_BIN:-openssl} version 2>&1
145 else
146 echo "$ACME_OPENSSL_BIN doesn't exists."
147 fi
148
149 echo "apache:"
150 if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
151 $_APACHECTL -V 2>&1
152 else
153 echo "apache doesn't exists."
154 fi
155
156 echo "nginx:"
157 if _exists "nginx"; then
158 nginx -V 2>&1
159 else
160 echo "nginx doesn't exists."
161 fi
162
163 echo "nc:"
164 if _exists "nc"; then
165 nc -h 2>&1
166 else
167 _debug "nc doesn't exists."
168 fi
169 }
170
171 #class
172 _syslog() {
173 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
174 return
175 fi
176 _logclass="$1"
177 shift
178 if [ -z "$__logger_i" ]; then
179 if _contains "$(logger --help 2>&1)" "-i"; then
180 __logger_i="logger -i"
181 else
182 __logger_i="logger"
183 fi
184 fi
185 $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1
186 }
187
188 _log() {
189 [ -z "$LOG_FILE" ] && return
190 _printargs "$@" >>"$LOG_FILE"
191 }
192
193 _info() {
194 _log "$@"
195 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_INFO" ]; then
196 _syslog "$SYSLOG_INFO" "$@"
197 fi
198 _printargs "$@"
199 }
200
201 _err() {
202 _syslog "$SYSLOG_ERROR" "$@"
203 _log "$@"
204 if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
205 printf -- "%s" "[$(date)] " >&2
206 fi
207 if [ -z "$2" ]; then
208 __red "$1" >&2
209 else
210 __red "$1='$2'" >&2
211 fi
212 printf "\n" >&2
213 return 1
214 }
215
216 _usage() {
217 __red "$@" >&2
218 printf "\n" >&2
219 }
220
221 _debug() {
222 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
223 _log "$@"
224 fi
225 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
226 _syslog "$SYSLOG_DEBUG" "$@"
227 fi
228 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
229 _printargs "$@" >&2
230 fi
231 }
232
233 #output the sensitive messages
234 _secure_debug() {
235 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
236 if [ "$OUTPUT_INSECURE" = "1" ]; then
237 _log "$@"
238 else
239 _log "$1" "$HIDDEN_VALUE"
240 fi
241 fi
242 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
243 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
244 fi
245 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
246 if [ "$OUTPUT_INSECURE" = "1" ]; then
247 _printargs "$@" >&2
248 else
249 _printargs "$1" "$HIDDEN_VALUE" >&2
250 fi
251 fi
252 }
253
254 _debug2() {
255 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
256 _log "$@"
257 fi
258 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
259 _syslog "$SYSLOG_DEBUG" "$@"
260 fi
261 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
262 _printargs "$@" >&2
263 fi
264 }
265
266 _secure_debug2() {
267 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
268 if [ "$OUTPUT_INSECURE" = "1" ]; then
269 _log "$@"
270 else
271 _log "$1" "$HIDDEN_VALUE"
272 fi
273 fi
274 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
275 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
276 fi
277 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
278 if [ "$OUTPUT_INSECURE" = "1" ]; then
279 _printargs "$@" >&2
280 else
281 _printargs "$1" "$HIDDEN_VALUE" >&2
282 fi
283 fi
284 }
285
286 _debug3() {
287 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
288 _log "$@"
289 fi
290 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
291 _syslog "$SYSLOG_DEBUG" "$@"
292 fi
293 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
294 _printargs "$@" >&2
295 fi
296 }
297
298 _secure_debug3() {
299 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
300 if [ "$OUTPUT_INSECURE" = "1" ]; then
301 _log "$@"
302 else
303 _log "$1" "$HIDDEN_VALUE"
304 fi
305 fi
306 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
307 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
308 fi
309 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
310 if [ "$OUTPUT_INSECURE" = "1" ]; then
311 _printargs "$@" >&2
312 else
313 _printargs "$1" "$HIDDEN_VALUE" >&2
314 fi
315 fi
316 }
317
318 _upper_case() {
319 # shellcheck disable=SC2018,SC2019
320 tr 'a-z' 'A-Z'
321 }
322
323 _lower_case() {
324 # shellcheck disable=SC2018,SC2019
325 tr 'A-Z' 'a-z'
326 }
327
328 _startswith() {
329 _str="$1"
330 _sub="$2"
331 echo "$_str" | grep "^$_sub" >/dev/null 2>&1
332 }
333
334 _endswith() {
335 _str="$1"
336 _sub="$2"
337 echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
338 }
339
340 _contains() {
341 _str="$1"
342 _sub="$2"
343 echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
344 }
345
346 _hasfield() {
347 _str="$1"
348 _field="$2"
349 _sep="$3"
350 if [ -z "$_field" ]; then
351 _usage "Usage: str field [sep]"
352 return 1
353 fi
354
355 if [ -z "$_sep" ]; then
356 _sep=","
357 fi
358
359 for f in $(echo "$_str" | tr "$_sep" ' '); do
360 if [ "$f" = "$_field" ]; then
361 _debug2 "'$_str' contains '$_field'"
362 return 0 #contains ok
363 fi
364 done
365 _debug2 "'$_str' does not contain '$_field'"
366 return 1 #not contains
367 }
368
369 # str index [sep]
370 _getfield() {
371 _str="$1"
372 _findex="$2"
373 _sep="$3"
374
375 if [ -z "$_findex" ]; then
376 _usage "Usage: str field [sep]"
377 return 1
378 fi
379
380 if [ -z "$_sep" ]; then
381 _sep=","
382 fi
383
384 _ffi="$_findex"
385 while [ "$_ffi" -gt "0" ]; do
386 _fv="$(echo "$_str" | cut -d "$_sep" -f "$_ffi")"
387 if [ "$_fv" ]; then
388 printf -- "%s" "$_fv"
389 return 0
390 fi
391 _ffi="$(_math "$_ffi" - 1)"
392 done
393
394 printf -- "%s" "$_str"
395
396 }
397
398 _exists() {
399 cmd="$1"
400 if [ -z "$cmd" ]; then
401 _usage "Usage: _exists cmd"
402 return 1
403 fi
404
405 if eval type type >/dev/null 2>&1; then
406 eval type "$cmd" >/dev/null 2>&1
407 elif command >/dev/null 2>&1; then
408 command -v "$cmd" >/dev/null 2>&1
409 else
410 which "$cmd" >/dev/null 2>&1
411 fi
412 ret="$?"
413 _debug3 "$cmd exists=$ret"
414 return $ret
415 }
416
417 #a + b
418 _math() {
419 _m_opts="$@"
420 printf "%s" "$(($_m_opts))"
421 }
422
423 _h_char_2_dec() {
424 _ch=$1
425 case "${_ch}" in
426 a | A)
427 printf "10"
428 ;;
429 b | B)
430 printf "11"
431 ;;
432 c | C)
433 printf "12"
434 ;;
435 d | D)
436 printf "13"
437 ;;
438 e | E)
439 printf "14"
440 ;;
441 f | F)
442 printf "15"
443 ;;
444 *)
445 printf "%s" "$_ch"
446 ;;
447 esac
448
449 }
450
451 _URGLY_PRINTF=""
452 if [ "$(printf '\x41')" != 'A' ]; then
453 _URGLY_PRINTF=1
454 fi
455
456 _ESCAPE_XARGS=""
457 if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
458 _ESCAPE_XARGS=1
459 fi
460
461 _h2b() {
462 if _exists xxd; then
463 xxd -r -p
464 return
465 fi
466
467 hex=$(cat)
468 ic=""
469 jc=""
470 _debug2 _URGLY_PRINTF "$_URGLY_PRINTF"
471 if [ -z "$_URGLY_PRINTF" ]; then
472 if [ "$_ESCAPE_XARGS" ] && _exists xargs; then
473 _debug2 "xargs"
474 echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf
475 else
476 for h in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do
477 if [ -z "$h" ]; then
478 break
479 fi
480 printf "\x$h%s"
481 done
482 fi
483 else
484 for c in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\)/ \1/g'); do
485 if [ -z "$ic" ]; then
486 ic=$c
487 continue
488 fi
489 jc=$c
490 ic="$(_h_char_2_dec "$ic")"
491 jc="$(_h_char_2_dec "$jc")"
492 printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s"
493 ic=""
494 jc=""
495 done
496 fi
497
498 }
499
500 _is_solaris() {
501 _contains "${__OS__:=$(uname -a)}" "solaris" || _contains "${__OS__:=$(uname -a)}" "SunOS"
502 }
503
504 #_ascii_hex str
505 #this can only process ascii chars, should only be used when od command is missing as a backup way.
506 _ascii_hex() {
507 _debug2 "Using _ascii_hex"
508 _str="$1"
509 _str_len=${#_str}
510 _h_i=1
511 while [ "$_h_i" -le "$_str_len" ]; do
512 _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
513 printf " %02x" "'$_str_c"
514 _h_i="$(_math "$_h_i" + 1)"
515 done
516 }
517
518 #stdin output hexstr splited by one space
519 #input:"abc"
520 #output: " 61 62 63"
521 _hex_dump() {
522 if _exists od; then
523 od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n"
524 elif _exists hexdump; then
525 _debug3 "using hexdump"
526 hexdump -v -e '/1 ""' -e '/1 " %02x" ""'
527 elif _exists xxd; then
528 _debug3 "using xxd"
529 xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
530 else
531 _debug3 "using _ascii_hex"
532 str=$(cat)
533 _ascii_hex "$str"
534 fi
535 }
536
537 #url encode, no-preserved chars
538 #A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
539 #41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a
540
541 #a b c d e f g h i j k l m n o p q r s t u v w x y z
542 #61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a
543
544 #0 1 2 3 4 5 6 7 8 9 - _ . ~
545 #30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
546
547 #stdin stdout
548 _url_encode() {
549 _hex_str=$(_hex_dump)
550 _debug3 "_url_encode"
551 _debug3 "_hex_str" "$_hex_str"
552 for _hex_code in $_hex_str; do
553 #upper case
554 case "${_hex_code}" in
555 "41")
556 printf "%s" "A"
557 ;;
558 "42")
559 printf "%s" "B"
560 ;;
561 "43")
562 printf "%s" "C"
563 ;;
564 "44")
565 printf "%s" "D"
566 ;;
567 "45")
568 printf "%s" "E"
569 ;;
570 "46")
571 printf "%s" "F"
572 ;;
573 "47")
574 printf "%s" "G"
575 ;;
576 "48")
577 printf "%s" "H"
578 ;;
579 "49")
580 printf "%s" "I"
581 ;;
582 "4a")
583 printf "%s" "J"
584 ;;
585 "4b")
586 printf "%s" "K"
587 ;;
588 "4c")
589 printf "%s" "L"
590 ;;
591 "4d")
592 printf "%s" "M"
593 ;;
594 "4e")
595 printf "%s" "N"
596 ;;
597 "4f")
598 printf "%s" "O"
599 ;;
600 "50")
601 printf "%s" "P"
602 ;;
603 "51")
604 printf "%s" "Q"
605 ;;
606 "52")
607 printf "%s" "R"
608 ;;
609 "53")
610 printf "%s" "S"
611 ;;
612 "54")
613 printf "%s" "T"
614 ;;
615 "55")
616 printf "%s" "U"
617 ;;
618 "56")
619 printf "%s" "V"
620 ;;
621 "57")
622 printf "%s" "W"
623 ;;
624 "58")
625 printf "%s" "X"
626 ;;
627 "59")
628 printf "%s" "Y"
629 ;;
630 "5a")
631 printf "%s" "Z"
632 ;;
633
634 #lower case
635 "61")
636 printf "%s" "a"
637 ;;
638 "62")
639 printf "%s" "b"
640 ;;
641 "63")
642 printf "%s" "c"
643 ;;
644 "64")
645 printf "%s" "d"
646 ;;
647 "65")
648 printf "%s" "e"
649 ;;
650 "66")
651 printf "%s" "f"
652 ;;
653 "67")
654 printf "%s" "g"
655 ;;
656 "68")
657 printf "%s" "h"
658 ;;
659 "69")
660 printf "%s" "i"
661 ;;
662 "6a")
663 printf "%s" "j"
664 ;;
665 "6b")
666 printf "%s" "k"
667 ;;
668 "6c")
669 printf "%s" "l"
670 ;;
671 "6d")
672 printf "%s" "m"
673 ;;
674 "6e")
675 printf "%s" "n"
676 ;;
677 "6f")
678 printf "%s" "o"
679 ;;
680 "70")
681 printf "%s" "p"
682 ;;
683 "71")
684 printf "%s" "q"
685 ;;
686 "72")
687 printf "%s" "r"
688 ;;
689 "73")
690 printf "%s" "s"
691 ;;
692 "74")
693 printf "%s" "t"
694 ;;
695 "75")
696 printf "%s" "u"
697 ;;
698 "76")
699 printf "%s" "v"
700 ;;
701 "77")
702 printf "%s" "w"
703 ;;
704 "78")
705 printf "%s" "x"
706 ;;
707 "79")
708 printf "%s" "y"
709 ;;
710 "7a")
711 printf "%s" "z"
712 ;;
713 #numbers
714 "30")
715 printf "%s" "0"
716 ;;
717 "31")
718 printf "%s" "1"
719 ;;
720 "32")
721 printf "%s" "2"
722 ;;
723 "33")
724 printf "%s" "3"
725 ;;
726 "34")
727 printf "%s" "4"
728 ;;
729 "35")
730 printf "%s" "5"
731 ;;
732 "36")
733 printf "%s" "6"
734 ;;
735 "37")
736 printf "%s" "7"
737 ;;
738 "38")
739 printf "%s" "8"
740 ;;
741 "39")
742 printf "%s" "9"
743 ;;
744 "2d")
745 printf "%s" "-"
746 ;;
747 "5f")
748 printf "%s" "_"
749 ;;
750 "2e")
751 printf "%s" "."
752 ;;
753 "7e")
754 printf "%s" "~"
755 ;;
756 #other hex
757 *)
758 printf '%%%s' "$_hex_code"
759 ;;
760 esac
761 done
762 }
763
764 #options file
765 _sed_i() {
766 options="$1"
767 filename="$2"
768 if [ -z "$filename" ]; then
769 _usage "Usage:_sed_i options filename"
770 return 1
771 fi
772 _debug2 options "$options"
773 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
774 _debug "Using sed -i"
775 sed -i "$options" "$filename"
776 else
777 _debug "No -i support in sed"
778 text="$(cat "$filename")"
779 echo "$text" | sed "$options" >"$filename"
780 fi
781 }
782
783 _egrep_o() {
784 if ! egrep -o "$1" 2>/dev/null; then
785 sed -n 's/.*\('"$1"'\).*/\1/p'
786 fi
787 }
788
789 #Usage: file startline endline
790 _getfile() {
791 filename="$1"
792 startline="$2"
793 endline="$3"
794 if [ -z "$endline" ]; then
795 _usage "Usage: file startline endline"
796 return 1
797 fi
798
799 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
800 if [ -z "$i" ]; then
801 _err "Can not find start line: $startline"
802 return 1
803 fi
804 i="$(_math "$i" + 1)"
805 _debug i "$i"
806
807 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
808 if [ -z "$j" ]; then
809 _err "Can not find end line: $endline"
810 return 1
811 fi
812 j="$(_math "$j" - 1)"
813 _debug j "$j"
814
815 sed -n "$i,${j}p" "$filename"
816
817 }
818
819 #Usage: multiline
820 _base64() {
821 [ "" ] #urgly
822 if [ "$1" ]; then
823 _debug3 "base64 multiline:'$1'"
824 ${ACME_OPENSSL_BIN:-openssl} base64 -e
825 else
826 _debug3 "base64 single line."
827 ${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
828 fi
829 }
830
831 #Usage: multiline
832 _dbase64() {
833 if [ "$1" ]; then
834 ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
835 else
836 ${ACME_OPENSSL_BIN:-openssl} base64 -d
837 fi
838 }
839
840 #Usage: hashalg [outputhex]
841 #Output Base64-encoded digest
842 _digest() {
843 alg="$1"
844 if [ -z "$alg" ]; then
845 _usage "Usage: _digest hashalg"
846 return 1
847 fi
848
849 outputhex="$2"
850
851 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
852 if [ "$outputhex" ]; then
853 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
854 else
855 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
856 fi
857 else
858 _err "$alg is not supported yet"
859 return 1
860 fi
861
862 }
863
864 #Usage: hashalg secret_hex [outputhex]
865 #Output binary hmac
866 _hmac() {
867 alg="$1"
868 secret_hex="$2"
869 outputhex="$3"
870
871 if [ -z "$secret_hex" ]; then
872 _usage "Usage: _hmac hashalg secret [outputhex]"
873 return 1
874 fi
875
876 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
877 if [ "$outputhex" ]; then
878 (${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' '
879 else
880 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary
881 fi
882 else
883 _err "$alg is not supported yet"
884 return 1
885 fi
886
887 }
888
889 #Usage: keyfile hashalg
890 #Output: Base64-encoded signature value
891 _sign() {
892 keyfile="$1"
893 alg="$2"
894 if [ -z "$alg" ]; then
895 _usage "Usage: _sign keyfile hashalg"
896 return 1
897 fi
898
899 _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
900 if [ "$alg" = "sha256" ]; then
901 _sign_openssl="$_sign_openssl -$alg"
902 else
903 _err "$alg is not supported yet"
904 return 1
905 fi
906
907 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
908 $_sign_openssl | _base64
909 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
910 if ! _signedECText="$($_sign_openssl | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
911 _err "Sign failed: $_sign_openssl"
912 _err "Key file: $keyfile"
913 _err "Key content:$(wc -l <"$keyfile") lines"
914 return 1
915 fi
916 _debug3 "_signedECText" "$_signedECText"
917 _ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
918 _debug3 "_ec_r" "$_ec_r"
919 _ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
920 _debug3 "_ec_s" "$_ec_s"
921 printf "%s" "$_ec_r$_ec_s" | _h2b | _base64
922 else
923 _err "Unknown key file format."
924 return 1
925 fi
926
927 }
928
929 #keylength or isEcc flag (empty str => not ecc)
930 _isEccKey() {
931 _length="$1"
932
933 if [ -z "$_length" ]; then
934 return 1
935 fi
936
937 [ "$_length" != "1024" ] \
938 && [ "$_length" != "2048" ] \
939 && [ "$_length" != "3072" ] \
940 && [ "$_length" != "4096" ] \
941 && [ "$_length" != "8192" ]
942 }
943
944 # _createkey 2048|ec-256 file
945 _createkey() {
946 length="$1"
947 f="$2"
948 _debug2 "_createkey for file:$f"
949 eccname="$length"
950 if _startswith "$length" "ec-"; then
951 length=$(printf "%s" "$length" | cut -d '-' -f 2-100)
952
953 if [ "$length" = "256" ]; then
954 eccname="prime256v1"
955 fi
956 if [ "$length" = "384" ]; then
957 eccname="secp384r1"
958 fi
959 if [ "$length" = "521" ]; then
960 eccname="secp521r1"
961 fi
962
963 fi
964
965 if [ -z "$length" ]; then
966 length=2048
967 fi
968
969 _debug "Use length $length"
970
971 if ! touch "$f" >/dev/null 2>&1; then
972 _f_path="$(dirname "$f")"
973 _debug _f_path "$_f_path"
974 if ! mkdir -p "$_f_path"; then
975 _err "Can not create path: $_f_path"
976 return 1
977 fi
978 fi
979
980 if _isEccKey "$length"; then
981 _debug "Using ec name: $eccname"
982 ${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null >"$f"
983 else
984 _debug "Using RSA: $length"
985 ${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null >"$f"
986 fi
987
988 if [ "$?" != "0" ]; then
989 _err "Create key error."
990 return 1
991 fi
992 }
993
994 #domain
995 _is_idn() {
996 _is_idn_d="$1"
997 _debug2 _is_idn_d "$_is_idn_d"
998 _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-')
999 _debug2 _idn_temp "$_idn_temp"
1000 [ "$_idn_temp" ]
1001 }
1002
1003 #aa.com
1004 #aa.com,bb.com,cc.com
1005 _idn() {
1006 __idn_d="$1"
1007 if ! _is_idn "$__idn_d"; then
1008 printf "%s" "$__idn_d"
1009 return 0
1010 fi
1011
1012 if _exists idn; then
1013 if _contains "$__idn_d" ','; then
1014 _i_first="1"
1015 for f in $(echo "$__idn_d" | tr ',' ' '); do
1016 [ -z "$f" ] && continue
1017 if [ -z "$_i_first" ]; then
1018 printf "%s" ","
1019 else
1020 _i_first=""
1021 fi
1022 idn --quiet "$f" | tr -d "\r\n"
1023 done
1024 else
1025 idn "$__idn_d" | tr -d "\r\n"
1026 fi
1027 else
1028 _err "Please install idn to process IDN names."
1029 fi
1030 }
1031
1032 #_createcsr cn san_list keyfile csrfile conf
1033 _createcsr() {
1034 _debug _createcsr
1035 domain="$1"
1036 domainlist="$2"
1037 csrkey="$3"
1038 csr="$4"
1039 csrconf="$5"
1040 _debug2 domain "$domain"
1041 _debug2 domainlist "$domainlist"
1042 _debug2 csrkey "$csrkey"
1043 _debug2 csr "$csr"
1044 _debug2 csrconf "$csrconf"
1045
1046 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf"
1047
1048 if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
1049 #single domain
1050 _info "Single domain" "$domain"
1051 else
1052 domainlist="$(_idn "$domainlist")"
1053 _debug2 domainlist "$domainlist"
1054 if _contains "$domainlist" ","; then
1055 alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")"
1056 else
1057 alt="DNS:$domainlist"
1058 fi
1059 #multi
1060 _info "Multi domain" "$alt"
1061 printf -- "\nsubjectAltName=$alt" >>"$csrconf"
1062 fi
1063 if [ "$Le_OCSP_Staple" ] || [ "$Le_OCSP_Stable" ]; then
1064 _savedomainconf Le_OCSP_Staple "$Le_OCSP_Staple"
1065 _cleardomainconf Le_OCSP_Stable
1066 printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
1067 fi
1068
1069 _csr_cn="$(_idn "$domain")"
1070 _debug2 _csr_cn "$_csr_cn"
1071 if _contains "$(uname -a)" "MINGW"; then
1072 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
1073 else
1074 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
1075 fi
1076 }
1077
1078 #_signcsr key csr conf cert
1079 _signcsr() {
1080 key="$1"
1081 csr="$2"
1082 conf="$3"
1083 cert="$4"
1084 _debug "_signcsr"
1085
1086 _msg="$(${ACME_OPENSSL_BIN:-openssl} x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
1087 _ret="$?"
1088 _debug "$_msg"
1089 return $_ret
1090 }
1091
1092 #_csrfile
1093 _readSubjectFromCSR() {
1094 _csrfile="$1"
1095 if [ -z "$_csrfile" ]; then
1096 _usage "_readSubjectFromCSR mycsr.csr"
1097 return 1
1098 fi
1099 ${ACME_OPENSSL_BIN:-openssl} req -noout -in "$_csrfile" -subject | tr ',' "\n" | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d ' \n'
1100 }
1101
1102 #_csrfile
1103 #echo comma separated domain list
1104 _readSubjectAltNamesFromCSR() {
1105 _csrfile="$1"
1106 if [ -z "$_csrfile" ]; then
1107 _usage "_readSubjectAltNamesFromCSR mycsr.csr"
1108 return 1
1109 fi
1110
1111 _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
1112 _debug _csrsubj "$_csrsubj"
1113
1114 _dnsAltnames="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
1115 _debug _dnsAltnames "$_dnsAltnames"
1116
1117 if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
1118 _debug "AltNames contains subject"
1119 _dnsAltnames="$(printf "%s" "$_dnsAltnames," | sed "s/DNS:$_csrsubj,//g")"
1120 else
1121 _debug "AltNames doesn't contain subject"
1122 fi
1123
1124 printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
1125 }
1126
1127 #_csrfile
1128 _readKeyLengthFromCSR() {
1129 _csrfile="$1"
1130 if [ -z "$_csrfile" ]; then
1131 _usage "_readKeyLengthFromCSR mycsr.csr"
1132 return 1
1133 fi
1134
1135 _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
1136 _debug2 _outcsr "$_outcsr"
1137 if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
1138 _debug "ECC CSR"
1139 echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
1140 else
1141 _debug "RSA CSR"
1142 _rkl="$(echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1)"
1143 if [ "$_rkl" ]; then
1144 echo "$_rkl"
1145 else
1146 echo "$_outcsr" | tr "\t" " " | _egrep_o "RSA Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
1147 fi
1148 fi
1149 }
1150
1151 _ss() {
1152 _port="$1"
1153
1154 if _exists "ss"; then
1155 _debug "Using: ss"
1156 ss -ntpl 2>/dev/null | grep ":$_port "
1157 return 0
1158 fi
1159
1160 if _exists "netstat"; then
1161 _debug "Using: netstat"
1162 if netstat -h 2>&1 | grep "\-p proto" >/dev/null; then
1163 #for windows version netstat tool
1164 netstat -an -p tcp | grep "LISTENING" | grep ":$_port "
1165 else
1166 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null; then
1167 netstat -an -p tcp | grep LISTEN | grep ":$_port "
1168 elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null; then
1169 #for solaris
1170 netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
1171 elif netstat -help 2>&1 | grep "\-p" >/dev/null; then
1172 #for full linux
1173 netstat -ntpl | grep ":$_port "
1174 else
1175 #for busybox (embedded linux; no pid support)
1176 netstat -ntl 2>/dev/null | grep ":$_port "
1177 fi
1178 fi
1179 return 0
1180 fi
1181
1182 return 1
1183 }
1184
1185 #outfile key cert cacert [password [name [caname]]]
1186 _toPkcs() {
1187 _cpfx="$1"
1188 _ckey="$2"
1189 _ccert="$3"
1190 _cca="$4"
1191 pfxPassword="$5"
1192 pfxName="$6"
1193 pfxCaname="$7"
1194
1195 if [ "$pfxCaname" ]; then
1196 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname"
1197 elif [ "$pfxName" ]; then
1198 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName"
1199 elif [ "$pfxPassword" ]; then
1200 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword"
1201 else
1202 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
1203 fi
1204
1205 }
1206
1207 #domain [password] [isEcc]
1208 toPkcs() {
1209 domain="$1"
1210 pfxPassword="$2"
1211 if [ -z "$domain" ]; then
1212 _usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
1213 return 1
1214 fi
1215
1216 _isEcc="$3"
1217
1218 _initpath "$domain" "$_isEcc"
1219
1220 _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword"
1221
1222 if [ "$?" = "0" ]; then
1223 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
1224 fi
1225
1226 }
1227
1228 #domain [isEcc]
1229 toPkcs8() {
1230 domain="$1"
1231
1232 if [ -z "$domain" ]; then
1233 _usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]"
1234 return 1
1235 fi
1236
1237 _isEcc="$2"
1238
1239 _initpath "$domain" "$_isEcc"
1240
1241 ${ACME_OPENSSL_BIN:-openssl} pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH"
1242
1243 if [ "$?" = "0" ]; then
1244 _info "Success, $CERT_PKCS8_PATH"
1245 fi
1246
1247 }
1248
1249 #[2048]
1250 createAccountKey() {
1251 _info "Creating account key"
1252 if [ -z "$1" ]; then
1253 _usage "Usage: $PROJECT_ENTRY --createAccountKey --accountkeylength 2048"
1254 return
1255 fi
1256
1257 length=$1
1258 _create_account_key "$length"
1259
1260 }
1261
1262 _create_account_key() {
1263
1264 length=$1
1265
1266 if [ -z "$length" ] || [ "$length" = "$NO_VALUE" ]; then
1267 _debug "Use default length $DEFAULT_ACCOUNT_KEY_LENGTH"
1268 length="$DEFAULT_ACCOUNT_KEY_LENGTH"
1269 fi
1270
1271 _debug length "$length"
1272 _initpath
1273
1274 mkdir -p "$CA_DIR"
1275 if [ -f "$ACCOUNT_KEY_PATH" ]; then
1276 _info "Account key exists, skip"
1277 return
1278 else
1279 #generate account key
1280 _createkey "$length" "$ACCOUNT_KEY_PATH"
1281 fi
1282
1283 }
1284
1285 #domain [length]
1286 createDomainKey() {
1287 _info "Creating domain key"
1288 if [ -z "$1" ]; then
1289 _usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
1290 return
1291 fi
1292
1293 domain=$1
1294 _cdl=$2
1295
1296 if [ -z "$_cdl" ]; then
1297 _debug "Use DEFAULT_DOMAIN_KEY_LENGTH=$DEFAULT_DOMAIN_KEY_LENGTH"
1298 _cdl="$DEFAULT_DOMAIN_KEY_LENGTH"
1299 fi
1300
1301 _initpath "$domain" "$_cdl"
1302
1303 if [ ! -f "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then
1304 if _createkey "$_cdl" "$CERT_KEY_PATH"; then
1305 _savedomainconf Le_Keylength "$_cdl"
1306 _info "The domain key is here: $(__green $CERT_KEY_PATH)"
1307 fi
1308 else
1309 if [ "$IS_RENEW" ]; then
1310 _info "Domain key exists, skip"
1311 return 0
1312 else
1313 _err "Domain key exists, do you want to overwrite the key?"
1314 _err "Add '--force', and try again."
1315 return 1
1316 fi
1317 fi
1318
1319 }
1320
1321 # domain domainlist isEcc
1322 createCSR() {
1323 _info "Creating csr"
1324 if [ -z "$1" ]; then
1325 _usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
1326 return
1327 fi
1328
1329 domain="$1"
1330 domainlist="$2"
1331 _isEcc="$3"
1332
1333 _initpath "$domain" "$_isEcc"
1334
1335 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
1336 _info "CSR exists, skip"
1337 return
1338 fi
1339
1340 if [ ! -f "$CERT_KEY_PATH" ]; then
1341 _err "The key file is not found: $CERT_KEY_PATH"
1342 _err "Please create the key file first."
1343 return 1
1344 fi
1345 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
1346
1347 }
1348
1349 _url_replace() {
1350 tr '/+' '_-' | tr -d '= '
1351 }
1352
1353 _time2str() {
1354 #Linux
1355 if date -u -d@"$1" 2>/dev/null; then
1356 return
1357 fi
1358
1359 #BSD
1360 if date -u -r "$1" 2>/dev/null; then
1361 return
1362 fi
1363
1364 #Soaris
1365 if _exists adb; then
1366 _t_s_a=$(echo "0t${1}=Y" | adb)
1367 echo "$_t_s_a"
1368 fi
1369
1370 #Busybox
1371 if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
1372 return
1373 fi
1374 }
1375
1376 _normalizeJson() {
1377 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
1378 }
1379
1380 _stat() {
1381 #Linux
1382 if stat -c '%U:%G' "$1" 2>/dev/null; then
1383 return
1384 fi
1385
1386 #BSD
1387 if stat -f '%Su:%Sg' "$1" 2>/dev/null; then
1388 return
1389 fi
1390
1391 return 1 #error, 'stat' not found
1392 }
1393
1394 #keyfile
1395 _calcjwk() {
1396 keyfile="$1"
1397 if [ -z "$keyfile" ]; then
1398 _usage "Usage: _calcjwk keyfile"
1399 return 1
1400 fi
1401
1402 if [ "$JWK_HEADER" ] && [ "$__CACHED_JWK_KEY_FILE" = "$keyfile" ]; then
1403 _debug2 "Use cached jwk for file: $__CACHED_JWK_KEY_FILE"
1404 return 0
1405 fi
1406
1407 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
1408 _debug "RSA key"
1409 pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
1410 if [ "${#pub_exp}" = "5" ]; then
1411 pub_exp=0$pub_exp
1412 fi
1413 _debug3 pub_exp "$pub_exp"
1414
1415 e=$(echo "$pub_exp" | _h2b | _base64)
1416 _debug3 e "$e"
1417
1418 modulus=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
1419 _debug3 modulus "$modulus"
1420 n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
1421 _debug3 n "$n"
1422
1423 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
1424 _debug3 jwk "$jwk"
1425
1426 JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
1427 JWK_HEADERPLACE_PART1='{"nonce": "'
1428 JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
1429 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
1430 _debug "EC key"
1431 crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
1432 _debug3 crv "$crv"
1433
1434 if [ -z "$crv" ]; then
1435 _debug "Let's try ASN1 OID"
1436 crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
1437 _debug3 crv_oid "$crv_oid"
1438 case "${crv_oid}" in
1439 "prime256v1")
1440 crv="P-256"
1441 ;;
1442 "secp384r1")
1443 crv="P-384"
1444 ;;
1445 "secp521r1")
1446 crv="P-521"
1447 ;;
1448 *)
1449 _err "ECC oid : $crv_oid"
1450 return 1
1451 ;;
1452 esac
1453 _debug3 crv "$crv"
1454 fi
1455
1456 pubi="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
1457 pubi=$(_math "$pubi" + 1)
1458 _debug3 pubi "$pubi"
1459
1460 pubj="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
1461 pubj=$(_math "$pubj" - 1)
1462 _debug3 pubj "$pubj"
1463
1464 pubtext="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
1465 _debug3 pubtext "$pubtext"
1466
1467 xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
1468 xlen=$(_math "$xlen" / 4)
1469 _debug3 xlen "$xlen"
1470
1471 xend=$(_math "$xlen" + 1)
1472 x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")"
1473 _debug3 x "$x"
1474
1475 x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _url_replace)"
1476 _debug3 x64 "$x64"
1477
1478 xend=$(_math "$xend" + 1)
1479 y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)"
1480 _debug3 y "$y"
1481
1482 y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)"
1483 _debug3 y64 "$y64"
1484
1485 jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
1486 _debug3 jwk "$jwk"
1487
1488 JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}'
1489 JWK_HEADERPLACE_PART1='{"nonce": "'
1490 JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}'
1491 else
1492 _err "Only RSA or EC key is supported."
1493 return 1
1494 fi
1495
1496 _debug3 JWK_HEADER "$JWK_HEADER"
1497 __CACHED_JWK_KEY_FILE="$keyfile"
1498 }
1499
1500 _time() {
1501 date -u "+%s"
1502 }
1503
1504 _utc_date() {
1505 date -u "+%Y-%m-%d %H:%M:%S"
1506 }
1507
1508 _mktemp() {
1509 if _exists mktemp; then
1510 if mktemp 2>/dev/null; then
1511 return 0
1512 elif _contains "$(mktemp 2>&1)" "-t prefix" && mktemp -t "$PROJECT_NAME" 2>/dev/null; then
1513 #for Mac osx
1514 return 0
1515 fi
1516 fi
1517 if [ -d "/tmp" ]; then
1518 echo "/tmp/${PROJECT_NAME}wefADf24sf.$(_time).tmp"
1519 return 0
1520 elif [ "$LE_TEMP_DIR" ] && mkdir -p "$LE_TEMP_DIR"; then
1521 echo "/$LE_TEMP_DIR/wefADf24sf.$(_time).tmp"
1522 return 0
1523 fi
1524 _err "Can not create temp file."
1525 }
1526
1527 _inithttp() {
1528
1529 if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
1530 HTTP_HEADER="$(_mktemp)"
1531 _debug2 HTTP_HEADER "$HTTP_HEADER"
1532 fi
1533
1534 if [ "$__HTTP_INITIALIZED" ]; then
1535 if [ "$_ACME_CURL$_ACME_WGET" ]; then
1536 _debug2 "Http already initialized."
1537 return 0
1538 fi
1539 fi
1540
1541 if [ -z "$_ACME_CURL" ] && _exists "curl"; then
1542 _ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
1543 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1544 _CURL_DUMP="$(_mktemp)"
1545 _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
1546 fi
1547
1548 if [ "$CA_PATH" ]; then
1549 _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
1550 elif [ "$CA_BUNDLE" ]; then
1551 _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
1552 fi
1553
1554 fi
1555
1556 if [ -z "$_ACME_WGET" ] && _exists "wget"; then
1557 _ACME_WGET="wget -q"
1558 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1559 _ACME_WGET="$_ACME_WGET -d "
1560 fi
1561 if [ "$CA_PATH" ]; then
1562 _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
1563 elif [ "$CA_BUNDLE" ]; then
1564 _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
1565 fi
1566 fi
1567
1568 #from wget 1.14: do not skip body on 404 error
1569 if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then
1570 _ACME_WGET="$_ACME_WGET --content-on-error "
1571 fi
1572
1573 __HTTP_INITIALIZED=1
1574
1575 }
1576
1577 # body url [needbase64] [POST|PUT]
1578 _post() {
1579 body="$1"
1580 url="$2"
1581 needbase64="$3"
1582 httpmethod="$4"
1583
1584 if [ -z "$httpmethod" ]; then
1585 httpmethod="POST"
1586 fi
1587 _debug $httpmethod
1588 _debug "url" "$url"
1589 _debug2 "body" "$body"
1590
1591 _inithttp
1592
1593 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1594 _CURL="$_ACME_CURL"
1595 if [ "$HTTPS_INSECURE" ]; then
1596 _CURL="$_CURL --insecure "
1597 fi
1598 _debug "_CURL" "$_CURL"
1599 if [ "$needbase64" ]; then
1600 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)"
1601 else
1602 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")"
1603 fi
1604 _ret="$?"
1605 if [ "$_ret" != "0" ]; then
1606 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
1607 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1608 _err "Here is the curl dump log:"
1609 _err "$(cat "$_CURL_DUMP")"
1610 fi
1611 fi
1612 elif [ "$_ACME_WGET" ]; then
1613 _WGET="$_ACME_WGET"
1614 if [ "$HTTPS_INSECURE" ]; then
1615 _WGET="$_WGET --no-check-certificate "
1616 fi
1617 _debug "_WGET" "$_WGET"
1618 if [ "$needbase64" ]; then
1619 if [ "$httpmethod" = "POST" ]; then
1620 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
1621 else
1622 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
1623 fi
1624 else
1625 if [ "$httpmethod" = "POST" ]; then
1626 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")"
1627 else
1628 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")"
1629 fi
1630 fi
1631 _ret="$?"
1632 if [ "$_ret" = "8" ]; then
1633 _ret=0
1634 _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
1635 fi
1636 if [ "$_ret" != "0" ]; then
1637 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
1638 fi
1639 _sed_i "s/^ *//g" "$HTTP_HEADER"
1640 else
1641 _ret="$?"
1642 _err "Neither curl nor wget is found, can not do $httpmethod."
1643 fi
1644 _debug "_ret" "$_ret"
1645 printf "%s" "$response"
1646 return $_ret
1647 }
1648
1649 # url getheader timeout
1650 _get() {
1651 _debug GET
1652 url="$1"
1653 onlyheader="$2"
1654 t="$3"
1655 _debug url "$url"
1656 _debug "timeout" "$t"
1657
1658 _inithttp
1659
1660 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1661 _CURL="$_ACME_CURL"
1662 if [ "$HTTPS_INSECURE" ]; then
1663 _CURL="$_CURL --insecure "
1664 fi
1665 if [ "$t" ]; then
1666 _CURL="$_CURL --connect-timeout $t"
1667 fi
1668 _debug "_CURL" "$_CURL"
1669 if [ "$onlyheader" ]; then
1670 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
1671 else
1672 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
1673 fi
1674 ret=$?
1675 if [ "$ret" != "0" ]; then
1676 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret"
1677 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1678 _err "Here is the curl dump log:"
1679 _err "$(cat "$_CURL_DUMP")"
1680 fi
1681 fi
1682 elif [ "$_ACME_WGET" ]; then
1683 _WGET="$_ACME_WGET"
1684 if [ "$HTTPS_INSECURE" ]; then
1685 _WGET="$_WGET --no-check-certificate "
1686 fi
1687 if [ "$t" ]; then
1688 _WGET="$_WGET --timeout=$t"
1689 fi
1690 _debug "_WGET" "$_WGET"
1691 if [ "$onlyheader" ]; then
1692 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null "$url" 2>&1 | sed 's/^[ ]*//g'
1693 else
1694 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
1695 fi
1696 ret=$?
1697 if [ "$ret" = "8" ]; then
1698 ret=0
1699 _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
1700 fi
1701 if [ "$ret" != "0" ]; then
1702 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
1703 fi
1704 else
1705 ret=$?
1706 _err "Neither curl nor wget is found, can not do GET."
1707 fi
1708 _debug "ret" "$ret"
1709 return $ret
1710 }
1711
1712 _head_n() {
1713 head -n "$1"
1714 }
1715
1716 _tail_n() {
1717 if ! tail -n "$1" 2>/dev/null; then
1718 #fix for solaris
1719 tail -"$1"
1720 fi
1721 }
1722
1723 # url payload needbase64 keyfile
1724 _send_signed_request() {
1725 url=$1
1726 payload=$2
1727 needbase64=$3
1728 keyfile=$4
1729 if [ -z "$keyfile" ]; then
1730 keyfile="$ACCOUNT_KEY_PATH"
1731 fi
1732 _debug url "$url"
1733 _debug payload "$payload"
1734
1735 if ! _calcjwk "$keyfile"; then
1736 return 1
1737 fi
1738
1739 payload64=$(printf "%s" "$payload" | _base64 | _url_replace)
1740 _debug3 payload64 "$payload64"
1741
1742 MAX_REQUEST_RETRY_TIMES=5
1743 _request_retry_times=0
1744 while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
1745 _debug3 _request_retry_times "$_request_retry_times"
1746 if [ -z "$_CACHED_NONCE" ]; then
1747 _headers=""
1748 if [ "$ACME_NEW_NONCE" ]; then
1749 _debug2 "Get nonce. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
1750 nonceurl="$ACME_NEW_NONCE"
1751 if _post "" "$nonceurl" "" "HEAD"; then
1752 _headers="$(cat "$HTTP_HEADER")"
1753 fi
1754 fi
1755 if [ -z "$_headers" ]; then
1756 _debug2 "Get nonce. ACME_DIRECTORY" "$ACME_DIRECTORY"
1757 nonceurl="$ACME_DIRECTORY"
1758 _headers="$(_get "$nonceurl" "onlyheader")"
1759 fi
1760
1761 if [ "$?" != "0" ]; then
1762 _err "Can not connect to $nonceurl to get nonce."
1763 return 1
1764 fi
1765
1766 _debug2 _headers "$_headers"
1767
1768 _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
1769 _debug2 _CACHED_NONCE "$_CACHED_NONCE"
1770 else
1771 _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
1772 fi
1773 nonce="$_CACHED_NONCE"
1774 _debug2 nonce "$nonce"
1775
1776 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2"
1777 _debug3 protected "$protected"
1778
1779 protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
1780 _debug3 protected64 "$protected64"
1781
1782 if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
1783 _err "Sign request failed."
1784 return 1
1785 fi
1786 _debug3 _sig_t "$_sig_t"
1787
1788 sig="$(printf "%s" "$_sig_t" | _url_replace)"
1789 _debug3 sig "$sig"
1790
1791 body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
1792 _debug3 body "$body"
1793
1794 response="$(_post "$body" "$url" "$needbase64")"
1795 _CACHED_NONCE=""
1796
1797 if [ "$?" != "0" ]; then
1798 _err "Can not post to $url"
1799 return 1
1800 fi
1801 _debug2 original "$response"
1802 response="$(echo "$response" | _normalizeJson)"
1803
1804 responseHeaders="$(cat "$HTTP_HEADER")"
1805
1806 _debug2 responseHeaders "$responseHeaders"
1807 _debug2 response "$response"
1808 code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
1809 _debug code "$code"
1810
1811 _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
1812
1813 if _contains "$response" "JWS has invalid anti-replay nonce"; then
1814 _info "It seems the CA server is busy now, let's wait and retry."
1815 _request_retry_times=$(_math "$_request_retry_times" + 1)
1816 _sleep 5
1817 continue
1818 fi
1819 break
1820 done
1821
1822 }
1823
1824 #setopt "file" "opt" "=" "value" [";"]
1825 _setopt() {
1826 __conf="$1"
1827 __opt="$2"
1828 __sep="$3"
1829 __val="$4"
1830 __end="$5"
1831 if [ -z "$__opt" ]; then
1832 _usage usage: _setopt '"file" "opt" "=" "value" [";"]'
1833 return
1834 fi
1835 if [ ! -f "$__conf" ]; then
1836 touch "$__conf"
1837 fi
1838
1839 if grep -n "^$__opt$__sep" "$__conf" >/dev/null; then
1840 _debug3 OK
1841 if _contains "$__val" "&"; then
1842 __val="$(echo "$__val" | sed 's/&/\\&/g')"
1843 fi
1844 text="$(cat "$__conf")"
1845 printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
1846
1847 elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then
1848 if _contains "$__val" "&"; then
1849 __val="$(echo "$__val" | sed 's/&/\\&/g')"
1850 fi
1851 text="$(cat "$__conf")"
1852 printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
1853
1854 else
1855 _debug3 APP
1856 echo "$__opt$__sep$__val$__end" >>"$__conf"
1857 fi
1858 _debug3 "$(grep -n "^$__opt$__sep" "$__conf")"
1859 }
1860
1861 #_save_conf file key value
1862 #save to conf
1863 _save_conf() {
1864 _s_c_f="$1"
1865 _sdkey="$2"
1866 _sdvalue="$3"
1867 if [ "$_s_c_f" ]; then
1868 _setopt "$_s_c_f" "$_sdkey" "=" "'$_sdvalue'"
1869 else
1870 _err "config file is empty, can not save $_sdkey=$_sdvalue"
1871 fi
1872 }
1873
1874 #_clear_conf file key
1875 _clear_conf() {
1876 _c_c_f="$1"
1877 _sdkey="$2"
1878 if [ "$_c_c_f" ]; then
1879 _conf_data="$(cat "$_c_c_f")"
1880 echo "$_conf_data" | sed "s/^$_sdkey *=.*$//" >"$_c_c_f"
1881 else
1882 _err "config file is empty, can not clear"
1883 fi
1884 }
1885
1886 #_read_conf file key
1887 _read_conf() {
1888 _r_c_f="$1"
1889 _sdkey="$2"
1890 if [ -f "$_r_c_f" ]; then
1891 (
1892 eval "$(grep "^$_sdkey *=" "$_r_c_f")"
1893 eval "printf \"%s\" \"\$$_sdkey\""
1894 )
1895 else
1896 _debug "config file is empty, can not read $_sdkey"
1897 fi
1898 }
1899
1900 #_savedomainconf key value
1901 #save to domain.conf
1902 _savedomainconf() {
1903 _save_conf "$DOMAIN_CONF" "$1" "$2"
1904 }
1905
1906 #_cleardomainconf key
1907 _cleardomainconf() {
1908 _clear_conf "$DOMAIN_CONF" "$1"
1909 }
1910
1911 #_readdomainconf key
1912 _readdomainconf() {
1913 _read_conf "$DOMAIN_CONF" "$1"
1914 }
1915
1916 #_saveaccountconf key value
1917 _saveaccountconf() {
1918 _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2"
1919 }
1920
1921 #key value
1922 _saveaccountconf_mutable() {
1923 _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2"
1924 #remove later
1925 _clearaccountconf "$1"
1926 }
1927
1928 #key
1929 _readaccountconf() {
1930 _read_conf "$ACCOUNT_CONF_PATH" "$1"
1931 }
1932
1933 #key
1934 _readaccountconf_mutable() {
1935 _rac_key="$1"
1936 _readaccountconf "SAVED_$_rac_key"
1937 }
1938
1939 #_clearaccountconf key
1940 _clearaccountconf() {
1941 _clear_conf "$ACCOUNT_CONF_PATH" "$1"
1942 }
1943
1944 #_savecaconf key value
1945 _savecaconf() {
1946 _save_conf "$CA_CONF" "$1" "$2"
1947 }
1948
1949 #_readcaconf key
1950 _readcaconf() {
1951 _read_conf "$CA_CONF" "$1"
1952 }
1953
1954 #_clearaccountconf key
1955 _clearcaconf() {
1956 _clear_conf "$CA_CONF" "$1"
1957 }
1958
1959 # content localaddress
1960 _startserver() {
1961 content="$1"
1962 ncaddr="$2"
1963 _debug "ncaddr" "$ncaddr"
1964
1965 _debug "startserver: $$"
1966 nchelp="$(nc -h 2>&1)"
1967
1968 _debug Le_HTTPPort "$Le_HTTPPort"
1969 _debug Le_Listen_V4 "$Le_Listen_V4"
1970 _debug Le_Listen_V6 "$Le_Listen_V6"
1971 _NC="nc"
1972
1973 if [ "$Le_Listen_V4" ]; then
1974 _NC="$_NC -4"
1975 elif [ "$Le_Listen_V6" ]; then
1976 _NC="$_NC -6"
1977 fi
1978
1979 if [ "$Le_Listen_V4$Le_Listen_V6$ncaddr" ]; then
1980 if ! _contains "$nchelp" "-4"; then
1981 _err "The nc doesn't support '-4', '-6' or local-address, please install 'netcat-openbsd' and try again."
1982 _err "See $(__green $_PREPARE_LINK)"
1983 return 1
1984 fi
1985 fi
1986
1987 if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then
1988 _NC="$_NC -q 1 -l $ncaddr"
1989 else
1990 if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null; then
1991 _NC="$_NC -c -l $ncaddr"
1992 elif echo "$nchelp" | grep "\-N" | grep "Shutdown the network socket after EOF on stdin" >/dev/null; then
1993 _NC="$_NC -N -l $ncaddr"
1994 else
1995 _NC="$_NC -l $ncaddr"
1996 fi
1997 fi
1998
1999 _debug "_NC" "$_NC"
2000
2001 #for centos ncat
2002 if _contains "$nchelp" "nmap.org"; then
2003 _debug "Using ncat: nmap.org"
2004 if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"; then
2005 _exec_err
2006 return 1
2007 fi
2008 if [ "$DEBUG" ]; then
2009 _exec_err
2010 fi
2011 return
2012 fi
2013
2014 # while true ; do
2015 if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC -p \"$Le_HTTPPort\" >&2"; then
2016 _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"
2017 fi
2018
2019 if [ "$?" != "0" ]; then
2020 _err "nc listen error."
2021 _exec_err
2022 exit 1
2023 fi
2024 if [ "$DEBUG" ]; then
2025 _exec_err
2026 fi
2027 # done
2028 }
2029
2030 _stopserver() {
2031 pid="$1"
2032 _debug "pid" "$pid"
2033 if [ -z "$pid" ]; then
2034 return
2035 fi
2036
2037 _debug2 "Le_HTTPPort" "$Le_HTTPPort"
2038 if [ "$Le_HTTPPort" ]; then
2039 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
2040 _get "http://localhost:$Le_HTTPPort" "" 1
2041 else
2042 _get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
2043 fi
2044 fi
2045
2046 _debug2 "Le_TLSPort" "$Le_TLSPort"
2047 if [ "$Le_TLSPort" ]; then
2048 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
2049 _get "https://localhost:$Le_TLSPort" "" 1
2050 _get "https://localhost:$Le_TLSPort" "" 1
2051 else
2052 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
2053 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
2054 fi
2055 fi
2056 }
2057
2058 # sleep sec
2059 _sleep() {
2060 _sleep_sec="$1"
2061 if [ "$__INTERACTIVE" ]; then
2062 _sleep_c="$_sleep_sec"
2063 while [ "$_sleep_c" -ge "0" ]; do
2064 printf "\r \r"
2065 __green "$_sleep_c"
2066 _sleep_c="$(_math "$_sleep_c" - 1)"
2067 sleep 1
2068 done
2069 printf "\r"
2070 else
2071 sleep "$_sleep_sec"
2072 fi
2073 }
2074
2075 # _starttlsserver san_a san_b port content _ncaddr
2076 _starttlsserver() {
2077 _info "Starting tls server."
2078 san_a="$1"
2079 san_b="$2"
2080 port="$3"
2081 content="$4"
2082 opaddr="$5"
2083
2084 _debug san_a "$san_a"
2085 _debug san_b "$san_b"
2086 _debug port "$port"
2087
2088 #create key TLS_KEY
2089 if ! _createkey "2048" "$TLS_KEY"; then
2090 _err "Create tls validation key error."
2091 return 1
2092 fi
2093
2094 #create csr
2095 alt="$san_a"
2096 if [ "$san_b" ]; then
2097 alt="$alt,$san_b"
2098 fi
2099 if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF"; then
2100 _err "Create tls validation csr error."
2101 return 1
2102 fi
2103
2104 #self signed
2105 if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT"; then
2106 _err "Create tls validation cert error."
2107 return 1
2108 fi
2109
2110 __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -cert $TLS_CERT -key $TLS_KEY "
2111 if [ "$opaddr" ]; then
2112 __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
2113 else
2114 __S_OPENSSL="$__S_OPENSSL -accept $port"
2115 fi
2116
2117 _debug Le_Listen_V4 "$Le_Listen_V4"
2118 _debug Le_Listen_V6 "$Le_Listen_V6"
2119 if [ "$Le_Listen_V4" ]; then
2120 __S_OPENSSL="$__S_OPENSSL -4"
2121 elif [ "$Le_Listen_V6" ]; then
2122 __S_OPENSSL="$__S_OPENSSL -6"
2123 fi
2124
2125 _debug "$__S_OPENSSL"
2126 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
2127 (printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL -tlsextdebug) &
2128 else
2129 (printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL >/dev/null 2>&1) &
2130 fi
2131
2132 serverproc="$!"
2133 sleep 1
2134 _debug serverproc "$serverproc"
2135 }
2136
2137 #file
2138 _readlink() {
2139 _rf="$1"
2140 if ! readlink -f "$_rf" 2>/dev/null; then
2141 if _startswith "$_rf" "/"; then
2142 echo "$_rf"
2143 return 0
2144 fi
2145 echo "$(pwd)/$_rf" | _conapath
2146 fi
2147 }
2148
2149 _conapath() {
2150 sed "s#/\./#/#g"
2151 }
2152
2153 __initHome() {
2154 if [ -z "$_SCRIPT_HOME" ]; then
2155 if _exists readlink && _exists dirname; then
2156 _debug "Lets find script dir."
2157 _debug "_SCRIPT_" "$_SCRIPT_"
2158 _script="$(_readlink "$_SCRIPT_")"
2159 _debug "_script" "$_script"
2160 _script_home="$(dirname "$_script")"
2161 _debug "_script_home" "$_script_home"
2162 if [ -d "$_script_home" ]; then
2163 _SCRIPT_HOME="$_script_home"
2164 else
2165 _err "It seems the script home is not correct:$_script_home"
2166 fi
2167 fi
2168 fi
2169
2170 # if [ -z "$LE_WORKING_DIR" ]; then
2171 # if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ]; then
2172 # _debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
2173 # LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
2174 # else
2175 # LE_WORKING_DIR="$_SCRIPT_HOME"
2176 # fi
2177 # fi
2178
2179 if [ -z "$LE_WORKING_DIR" ]; then
2180 _debug "Using default home:$DEFAULT_INSTALL_HOME"
2181 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
2182 fi
2183 export LE_WORKING_DIR
2184
2185 if [ -z "$LE_CONFIG_HOME" ]; then
2186 LE_CONFIG_HOME="$LE_WORKING_DIR"
2187 fi
2188 _debug "Using config home:$LE_CONFIG_HOME"
2189 export LE_CONFIG_HOME
2190
2191 _DEFAULT_ACCOUNT_CONF_PATH="$LE_CONFIG_HOME/account.conf"
2192
2193 if [ -z "$ACCOUNT_CONF_PATH" ]; then
2194 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]; then
2195 . "$_DEFAULT_ACCOUNT_CONF_PATH"
2196 fi
2197 fi
2198
2199 if [ -z "$ACCOUNT_CONF_PATH" ]; then
2200 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
2201 fi
2202
2203 DEFAULT_LOG_FILE="$LE_CONFIG_HOME/$PROJECT_NAME.log"
2204
2205 DEFAULT_CA_HOME="$LE_CONFIG_HOME/ca"
2206
2207 if [ -z "$LE_TEMP_DIR" ]; then
2208 LE_TEMP_DIR="$LE_CONFIG_HOME/tmp"
2209 fi
2210 }
2211
2212 #server
2213 _initAPI() {
2214 _api_server="${1:-$ACME_DIRECTORY}"
2215 _debug "_init api for server: $_api_server"
2216
2217 if [ "$_api_server" = "$DEFAULT_CA" ]; then
2218 #just for performance, hardcode the default entry points
2219 export ACME_KEY_CHANGE="https://acme-v01.api.letsencrypt.org/acme/key-change"
2220 export ACME_NEW_AUTHZ="https://acme-v01.api.letsencrypt.org/acme/new-authz"
2221 export ACME_NEW_ORDER="https://acme-v01.api.letsencrypt.org/acme/new-cert"
2222 export ACME_NEW_ORDER_RES="new-cert"
2223 export ACME_NEW_ACCOUNT="https://acme-v01.api.letsencrypt.org/acme/new-reg"
2224 export ACME_NEW_ACCOUNT_RES="new-reg"
2225 export ACME_REVOKE_CERT="https://acme-v01.api.letsencrypt.org/acme/revoke-cert"
2226 fi
2227
2228 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2229 response=$(_get "$_api_server")
2230 if [ "$?" != "0" ]; then
2231 _debug2 "response" "$response"
2232 _err "Can not init api."
2233 return 1
2234 fi
2235 _debug2 "response" "$response"
2236
2237 ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3)
2238 export ACME_KEY_CHANGE
2239
2240 ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3)
2241 export ACME_NEW_AUTHZ
2242
2243 ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
2244 ACME_NEW_ORDER_RES="new-cert"
2245 if [ -z "$ACME_NEW_ORDER" ]; then
2246 ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3)
2247 ACME_NEW_ORDER_RES="new-order"
2248 fi
2249 export ACME_NEW_ORDER
2250 export ACME_NEW_ORDER_RES
2251
2252 ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-reg" *: *"[^"]*"' | cut -d '"' -f 3)
2253 ACME_NEW_ACCOUNT_RES="new-reg"
2254 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2255 ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3)
2256 ACME_NEW_ACCOUNT_RES="new-account"
2257 fi
2258 export ACME_NEW_ACCOUNT
2259 export ACME_NEW_ACCOUNT_RES
2260
2261 ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3)
2262 export ACME_REVOKE_CERT
2263
2264 ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
2265 export ACME_NEW_NONCE
2266
2267 fi
2268
2269 _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
2270 _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
2271 _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
2272 _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
2273 _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
2274 }
2275
2276 #[domain] [keylength or isEcc flag]
2277 _initpath() {
2278
2279 __initHome
2280
2281 if [ -f "$ACCOUNT_CONF_PATH" ]; then
2282 . "$ACCOUNT_CONF_PATH"
2283 fi
2284
2285 if [ "$IN_CRON" ]; then
2286 if [ ! "$_USER_PATH_EXPORTED" ]; then
2287 _USER_PATH_EXPORTED=1
2288 export PATH="$USER_PATH:$PATH"
2289 fi
2290 fi
2291
2292 if [ -z "$CA_HOME" ]; then
2293 CA_HOME="$DEFAULT_CA_HOME"
2294 fi
2295
2296 if [ -z "$ACME_DIRECTORY" ]; then
2297 if [ -z "$STAGE" ]; then
2298 ACME_DIRECTORY="$DEFAULT_CA"
2299 else
2300 ACME_DIRECTORY="$STAGE_CA"
2301 _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY"
2302 fi
2303 fi
2304
2305 _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
2306 _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
2307
2308 CA_DIR="$CA_HOME/$_ACME_SERVER_HOST"
2309
2310 _DEFAULT_CA_CONF="$CA_DIR/ca.conf"
2311
2312 if [ -z "$CA_CONF" ]; then
2313 CA_CONF="$_DEFAULT_CA_CONF"
2314 fi
2315 _debug3 CA_CONF "$CA_CONF"
2316
2317 if [ -f "$CA_CONF" ]; then
2318 . "$CA_CONF"
2319 fi
2320
2321 if [ -z "$ACME_DIR" ]; then
2322 ACME_DIR="/home/.acme"
2323 fi
2324
2325 if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then
2326 APACHE_CONF_BACKUP_DIR="$LE_CONFIG_HOME"
2327 fi
2328
2329 if [ -z "$USER_AGENT" ]; then
2330 USER_AGENT="$DEFAULT_USER_AGENT"
2331 fi
2332
2333 if [ -z "$HTTP_HEADER" ]; then
2334 HTTP_HEADER="$LE_CONFIG_HOME/http.header"
2335 fi
2336
2337 _OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key"
2338 _OLD_ACCOUNT_JSON="$LE_WORKING_DIR/account.json"
2339
2340 _DEFAULT_ACCOUNT_KEY_PATH="$CA_DIR/account.key"
2341 _DEFAULT_ACCOUNT_JSON_PATH="$CA_DIR/account.json"
2342 if [ -z "$ACCOUNT_KEY_PATH" ]; then
2343 ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
2344 fi
2345
2346 if [ -z "$ACCOUNT_JSON_PATH" ]; then
2347 ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
2348 fi
2349
2350 _DEFAULT_CERT_HOME="$LE_CONFIG_HOME"
2351 if [ -z "$CERT_HOME" ]; then
2352 CERT_HOME="$_DEFAULT_CERT_HOME"
2353 fi
2354
2355 if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ]; then
2356 ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
2357 fi
2358
2359 if [ -z "$1" ]; then
2360 return 0
2361 fi
2362
2363 domain="$1"
2364 _ilength="$2"
2365
2366 if [ -z "$DOMAIN_PATH" ]; then
2367 domainhome="$CERT_HOME/$domain"
2368 domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
2369
2370 DOMAIN_PATH="$domainhome"
2371
2372 if _isEccKey "$_ilength"; then
2373 DOMAIN_PATH="$domainhomeecc"
2374 else
2375 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then
2376 _info "The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
2377 fi
2378 fi
2379 _debug DOMAIN_PATH "$DOMAIN_PATH"
2380 fi
2381
2382 if [ -z "$DOMAIN_BACKUP_PATH" ]; then
2383 DOMAIN_BACKUP_PATH="$DOMAIN_PATH/backup"
2384 fi
2385
2386 if [ -z "$DOMAIN_CONF" ]; then
2387 DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
2388 fi
2389
2390 if [ -z "$DOMAIN_SSL_CONF" ]; then
2391 DOMAIN_SSL_CONF="$DOMAIN_PATH/$domain.csr.conf"
2392 fi
2393
2394 if [ -z "$CSR_PATH" ]; then
2395 CSR_PATH="$DOMAIN_PATH/$domain.csr"
2396 fi
2397 if [ -z "$CERT_KEY_PATH" ]; then
2398 CERT_KEY_PATH="$DOMAIN_PATH/$domain.key"
2399 fi
2400 if [ -z "$CERT_PATH" ]; then
2401 CERT_PATH="$DOMAIN_PATH/$domain.cer"
2402 fi
2403 if [ -z "$CA_CERT_PATH" ]; then
2404 CA_CERT_PATH="$DOMAIN_PATH/ca.cer"
2405 fi
2406 if [ -z "$CERT_FULLCHAIN_PATH" ]; then
2407 CERT_FULLCHAIN_PATH="$DOMAIN_PATH/fullchain.cer"
2408 fi
2409 if [ -z "$CERT_PFX_PATH" ]; then
2410 CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
2411 fi
2412 if [ -z "$CERT_PKCS8_PATH" ]; then
2413 CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8"
2414 fi
2415
2416 if [ -z "$TLS_CONF" ]; then
2417 TLS_CONF="$DOMAIN_PATH/tls.validation.conf"
2418 fi
2419 if [ -z "$TLS_CERT" ]; then
2420 TLS_CERT="$DOMAIN_PATH/tls.validation.cert"
2421 fi
2422 if [ -z "$TLS_KEY" ]; then
2423 TLS_KEY="$DOMAIN_PATH/tls.validation.key"
2424 fi
2425 if [ -z "$TLS_CSR" ]; then
2426 TLS_CSR="$DOMAIN_PATH/tls.validation.csr"
2427 fi
2428
2429 }
2430
2431 _exec() {
2432 if [ -z "$_EXEC_TEMP_ERR" ]; then
2433 _EXEC_TEMP_ERR="$(_mktemp)"
2434 fi
2435
2436 if [ "$_EXEC_TEMP_ERR" ]; then
2437 eval "$@ 2>>$_EXEC_TEMP_ERR"
2438 else
2439 eval "$@"
2440 fi
2441 }
2442
2443 _exec_err() {
2444 [ "$_EXEC_TEMP_ERR" ] && _err "$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
2445 }
2446
2447 _apachePath() {
2448 _APACHECTL="apachectl"
2449 if ! _exists apachectl; then
2450 if _exists apache2ctl; then
2451 _APACHECTL="apache2ctl"
2452 else
2453 _err "'apachectl not found. It seems that apache is not installed, or you are not root user.'"
2454 _err "Please use webroot mode to try again."
2455 return 1
2456 fi
2457 fi
2458
2459 if ! _exec $_APACHECTL -V >/dev/null; then
2460 _exec_err
2461 return 1
2462 fi
2463
2464 if [ "$APACHE_HTTPD_CONF" ]; then
2465 _saveaccountconf APACHE_HTTPD_CONF "$APACHE_HTTPD_CONF"
2466 httpdconf="$APACHE_HTTPD_CONF"
2467 httpdconfname="$(basename "$httpdconfname")"
2468 else
2469 httpdconfname="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"')"
2470 _debug httpdconfname "$httpdconfname"
2471
2472 if [ -z "$httpdconfname" ]; then
2473 _err "Can not read apache config file."
2474 return 1
2475 fi
2476
2477 if _startswith "$httpdconfname" '/'; then
2478 httpdconf="$httpdconfname"
2479 httpdconfname="$(basename "$httpdconfname")"
2480 else
2481 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"')"
2482 _debug httpdroot "$httpdroot"
2483 httpdconf="$httpdroot/$httpdconfname"
2484 httpdconfname="$(basename "$httpdconfname")"
2485 fi
2486 fi
2487 _debug httpdconf "$httpdconf"
2488 _debug httpdconfname "$httpdconfname"
2489 if [ ! -f "$httpdconf" ]; then
2490 _err "Apache Config file not found" "$httpdconf"
2491 return 1
2492 fi
2493 return 0
2494 }
2495
2496 _restoreApache() {
2497 if [ -z "$usingApache" ]; then
2498 return 0
2499 fi
2500 _initpath
2501 if ! _apachePath; then
2502 return 1
2503 fi
2504
2505 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]; then
2506 _debug "No config file to restore."
2507 return 0
2508 fi
2509
2510 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
2511 _debug "Restored: $httpdconf."
2512 if ! _exec $_APACHECTL -t; then
2513 _exec_err
2514 _err "Sorry, restore apache config error, please contact me."
2515 return 1
2516 fi
2517 _debug "Restored successfully."
2518 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
2519 return 0
2520 }
2521
2522 _setApache() {
2523 _initpath
2524 if ! _apachePath; then
2525 return 1
2526 fi
2527
2528 #test the conf first
2529 _info "Checking if there is an error in the apache config file before starting."
2530
2531 if ! _exec "$_APACHECTL" -t >/dev/null; then
2532 _exec_err
2533 _err "The apache config file has error, please fix it first, then try again."
2534 _err "Don't worry, there is nothing changed to your system."
2535 return 1
2536 else
2537 _info "OK"
2538 fi
2539
2540 #backup the conf
2541 _debug "Backup apache config file" "$httpdconf"
2542 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then
2543 _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
2544 _err "This might be a bug of $PROJECT_NAME , please report issue: $PROJECT"
2545 return 1
2546 fi
2547 _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
2548 _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
2549 _info "The backup file will be deleted on success, just forget it."
2550
2551 #add alias
2552
2553 apacheVer="$($_APACHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2)"
2554 _debug "apacheVer" "$apacheVer"
2555 apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)"
2556 apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
2557
2558 if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ]; then
2559 echo "
2560 Alias /.well-known/acme-challenge $ACME_DIR
2561
2562 <Directory $ACME_DIR >
2563 Require all granted
2564 </Directory>
2565 " >>"$httpdconf"
2566 else
2567 echo "
2568 Alias /.well-known/acme-challenge $ACME_DIR
2569
2570 <Directory $ACME_DIR >
2571 Order allow,deny
2572 Allow from all
2573 </Directory>
2574 " >>"$httpdconf"
2575 fi
2576
2577 _msg="$($_APACHECTL -t 2>&1)"
2578 if [ "$?" != "0" ]; then
2579 _err "Sorry, apache config error"
2580 if _restoreApache; then
2581 _err "The apache config file is restored."
2582 else
2583 _err "Sorry, The apache config file can not be restored, please report bug."
2584 fi
2585 return 1
2586 fi
2587
2588 if [ ! -d "$ACME_DIR" ]; then
2589 mkdir -p "$ACME_DIR"
2590 chmod 755 "$ACME_DIR"
2591 fi
2592
2593 if ! _exec "$_APACHECTL" graceful; then
2594 _exec_err
2595 _err "$_APACHECTL graceful error, please contact me."
2596 _restoreApache
2597 return 1
2598 fi
2599 usingApache="1"
2600 return 0
2601 }
2602
2603 #find the real nginx conf file
2604 #backup
2605 #set the nginx conf
2606 #returns the real nginx conf file
2607 _setNginx() {
2608 _d="$1"
2609 _croot="$2"
2610 _thumbpt="$3"
2611 if ! _exists "nginx"; then
2612 _err "nginx command is not found."
2613 return 1
2614 fi
2615 FOUND_REAL_NGINX_CONF=""
2616 FOUND_REAL_NGINX_CONF_LN=""
2617 BACKUP_NGINX_CONF=""
2618 _debug _croot "$_croot"
2619 _start_f="$(echo "$_croot" | cut -d : -f 2)"
2620 _debug _start_f "$_start_f"
2621 if [ -z "$_start_f" ]; then
2622 _debug "find start conf from nginx command"
2623 if [ -z "$NGINX_CONF" ]; then
2624 NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
2625 _debug NGINX_CONF "$NGINX_CONF"
2626 NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
2627 _debug NGINX_CONF "$NGINX_CONF"
2628 if [ ! -f "$NGINX_CONF" ]; then
2629 _err "'$NGINX_CONF' doesn't exist."
2630 NGINX_CONF=""
2631 return 1
2632 fi
2633 _debug "Found nginx conf file:$NGINX_CONF"
2634 fi
2635 _start_f="$NGINX_CONF"
2636 fi
2637 _debug "Start detect nginx conf for $_d from:$_start_f"
2638 if ! _checkConf "$_d" "$_start_f"; then
2639 _err "Can not find conf file for domain $d"
2640 return 1
2641 fi
2642 _info "Found conf file: $FOUND_REAL_NGINX_CONF"
2643
2644 _ln=$FOUND_REAL_NGINX_CONF_LN
2645 _debug "_ln" "$_ln"
2646
2647 _lnn=$(_math $_ln + 1)
2648 _debug _lnn "$_lnn"
2649 _start_tag="$(sed -n "$_lnn,${_lnn}p" "$FOUND_REAL_NGINX_CONF")"
2650 _debug "_start_tag" "$_start_tag"
2651 if [ "$_start_tag" = "$NGINX_START" ]; then
2652 _info "The domain $_d is already configured, skip"
2653 FOUND_REAL_NGINX_CONF=""
2654 return 0
2655 fi
2656
2657 mkdir -p "$DOMAIN_BACKUP_PATH"
2658 _backup_conf="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
2659 _debug _backup_conf "$_backup_conf"
2660 BACKUP_NGINX_CONF="$_backup_conf"
2661 _info "Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
2662 if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
2663 _err "backup error."
2664 FOUND_REAL_NGINX_CONF=""
2665 return 1
2666 fi
2667
2668 _info "Check the nginx conf before setting up."
2669 if ! _exec "nginx -t" >/dev/null; then
2670 _exec_err
2671 return 1
2672 fi
2673
2674 _info "OK, Set up nginx config file"
2675
2676 if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
2677 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2678 _err "write nginx conf error, but don't worry, the file is restored to the original version."
2679 return 1
2680 fi
2681
2682 echo "$NGINX_START
2683 location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
2684 default_type text/plain;
2685 return 200 \"\$1.$_thumbpt\";
2686 }
2687 #NGINX_START
2688 " >>"$FOUND_REAL_NGINX_CONF"
2689
2690 if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
2691 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2692 _err "write nginx conf error, but don't worry, the file is restored."
2693 return 1
2694 fi
2695 _debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
2696 _info "nginx conf is done, let's check it again."
2697 if ! _exec "nginx -t" >/dev/null; then
2698 _exec_err
2699 _err "It seems that nginx conf was broken, let's restore."
2700 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2701 return 1
2702 fi
2703
2704 _info "Reload nginx"
2705 if ! _exec "nginx -s reload" >/dev/null; then
2706 _exec_err
2707 _err "It seems that nginx reload error, let's restore."
2708 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2709 return 1
2710 fi
2711
2712 return 0
2713 }
2714
2715 #d , conf
2716 _checkConf() {
2717 _d="$1"
2718 _c_file="$2"
2719 _debug "Start _checkConf from:$_c_file"
2720 if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
2721 _debug "wildcard"
2722 for _w_f in $2; do
2723 if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then
2724 return 0
2725 fi
2726 done
2727 #not found
2728 return 1
2729 elif [ -f "$2" ]; then
2730 _debug "single"
2731 if _isRealNginxConf "$1" "$2"; then
2732 _debug "$2 is found."
2733 FOUND_REAL_NGINX_CONF="$2"
2734 return 0
2735 fi
2736 if cat "$2" | tr "\t" " " | grep "^ *include *.*;" >/dev/null; then
2737 _debug "Try include files"
2738 for included in $(cat "$2" | tr "\t" " " | grep "^ *include *.*;" | sed "s/include //" | tr -d " ;"); do
2739 _debug "check included $included"
2740 if _checkConf "$1" "$included"; then
2741 return 0
2742 fi
2743 done
2744 fi
2745 return 1
2746 else
2747 _debug "$2 not found."
2748 return 1
2749 fi
2750 return 1
2751 }
2752
2753 #d , conf
2754 _isRealNginxConf() {
2755 _debug "_isRealNginxConf $1 $2"
2756 if [ -f "$2" ]; then
2757 for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
2758 _debug _fln "$_fln"
2759 if [ "$_fln" ]; then
2760 _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1)
2761 _debug "_start" "$_start"
2762 _start_n=$(echo "$_start" | cut -d : -f 1)
2763 _start_nn=$(_math $_start_n + 1)
2764 _debug "_start_n" "$_start_n"
2765 _debug "_start_nn" "$_start_nn"
2766
2767 _left="$(sed -n "${_start_nn},99999p" "$2")"
2768 _debug2 _left "$_left"
2769 if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then
2770 _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1)
2771 _debug "_end" "$_end"
2772 _end_n=$(echo "$_end" | cut -d : -f 1)
2773 _debug "_end_n" "$_end_n"
2774 _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
2775 else
2776 _seg_n="$_left"
2777 fi
2778
2779 _debug "_seg_n" "$_seg_n"
2780
2781 if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \
2782 || [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then
2783 _debug "ssl on, skip"
2784 else
2785 FOUND_REAL_NGINX_CONF_LN=$_fln
2786 _debug3 "found FOUND_REAL_NGINX_CONF_LN" "$FOUND_REAL_NGINX_CONF_LN"
2787 return 0
2788 fi
2789 fi
2790 done
2791 fi
2792 return 1
2793 }
2794
2795 #restore all the nginx conf
2796 _restoreNginx() {
2797 if [ -z "$NGINX_RESTORE_VLIST" ]; then
2798 _debug "No need to restore nginx, skip."
2799 return
2800 fi
2801 _debug "_restoreNginx"
2802 _debug "NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
2803
2804 for ng_entry in $(echo "$NGINX_RESTORE_VLIST" | tr "$dvsep" ' '); do
2805 _debug "ng_entry" "$ng_entry"
2806 _nd=$(echo "$ng_entry" | cut -d "$sep" -f 1)
2807 _ngconf=$(echo "$ng_entry" | cut -d "$sep" -f 2)
2808 _ngbackupconf=$(echo "$ng_entry" | cut -d "$sep" -f 3)
2809 _info "Restoring from $_ngbackupconf to $_ngconf"
2810 cat "$_ngbackupconf" >"$_ngconf"
2811 done
2812
2813 _info "Reload nginx"
2814 if ! _exec "nginx -s reload" >/dev/null; then
2815 _exec_err
2816 _err "It seems that nginx reload error, please report bug."
2817 return 1
2818 fi
2819 return 0
2820 }
2821
2822 _clearup() {
2823 _stopserver "$serverproc"
2824 serverproc=""
2825 _restoreApache
2826 _restoreNginx
2827 _clearupdns
2828 if [ -z "$DEBUG" ]; then
2829 rm -f "$TLS_CONF"
2830 rm -f "$TLS_CERT"
2831 rm -f "$TLS_KEY"
2832 rm -f "$TLS_CSR"
2833 fi
2834 }
2835
2836 _clearupdns() {
2837 _debug "_clearupdns"
2838 if [ "$dnsadded" != 1 ] || [ -z "$vlist" ]; then
2839 _debug "skip dns."
2840 return
2841 fi
2842
2843 ventries=$(echo "$vlist" | tr ',' ' ')
2844 for ventry in $ventries; do
2845 d=$(echo "$ventry" | cut -d "$sep" -f 1)
2846 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
2847 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
2848 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
2849 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
2850 _debug txt "$txt"
2851 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
2852 _debug "$d is already verified, skip $vtype."
2853 continue
2854 fi
2855
2856 if [ "$vtype" != "$VTYPE_DNS" ]; then
2857 _info "Skip $d for $vtype"
2858 continue
2859 fi
2860
2861 d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
2862 _debug d_api "$d_api"
2863
2864 if [ -z "$d_api" ]; then
2865 _info "Not Found domain api file: $d_api"
2866 continue
2867 fi
2868
2869 (
2870 if ! . "$d_api"; then
2871 _err "Load file $d_api error. Please check your api file and try again."
2872 return 1
2873 fi
2874
2875 rmcommand="${_currentRoot}_rm"
2876 if ! _exists "$rmcommand"; then
2877 _err "It seems that your api file doesn't define $rmcommand"
2878 return 1
2879 fi
2880
2881 txtdomain="_acme-challenge.$d"
2882
2883 if ! $rmcommand "$txtdomain" "$txt"; then
2884 _err "Error removing txt for domain:$txtdomain"
2885 return 1
2886 fi
2887 )
2888
2889 done
2890 }
2891
2892 # webroot removelevel tokenfile
2893 _clearupwebbroot() {
2894 __webroot="$1"
2895 if [ -z "$__webroot" ]; then
2896 _debug "no webroot specified, skip"
2897 return 0
2898 fi
2899
2900 _rmpath=""
2901 if [ "$2" = '1' ]; then
2902 _rmpath="$__webroot/.well-known"
2903 elif [ "$2" = '2' ]; then
2904 _rmpath="$__webroot/.well-known/acme-challenge"
2905 elif [ "$2" = '3' ]; then
2906 _rmpath="$__webroot/.well-known/acme-challenge/$3"
2907 else
2908 _debug "Skip for removelevel:$2"
2909 fi
2910
2911 if [ "$_rmpath" ]; then
2912 if [ "$DEBUG" ]; then
2913 _debug "Debugging, skip removing: $_rmpath"
2914 else
2915 rm -rf "$_rmpath"
2916 fi
2917 fi
2918
2919 return 0
2920
2921 }
2922
2923 _on_before_issue() {
2924 _chk_web_roots="$1"
2925 _chk_main_domain="$2"
2926 _chk_alt_domains="$3"
2927 _chk_pre_hook="$4"
2928 _chk_local_addr="$5"
2929 _debug _on_before_issue
2930 #run pre hook
2931 if [ "$_chk_pre_hook" ]; then
2932 _info "Run pre hook:'$_chk_pre_hook'"
2933 if ! (
2934 cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
2935 ); then
2936 _err "Error when run pre hook."
2937 return 1
2938 fi
2939 fi
2940
2941 if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
2942 if ! _exists "nc"; then
2943 _err "Please install netcat(nc) tools first."
2944 return 1
2945 fi
2946 fi
2947
2948 _debug Le_LocalAddress "$_chk_local_addr"
2949
2950 alldomains=$(echo "$_chk_main_domain,$_chk_alt_domains" | tr ',' ' ')
2951 _index=1
2952 _currentRoot=""
2953 _addrIndex=1
2954 for d in $alldomains; do
2955 _debug "Check for domain" "$d"
2956 _currentRoot="$(_getfield "$_chk_web_roots" $_index)"
2957 _debug "_currentRoot" "$_currentRoot"
2958 _index=$(_math $_index + 1)
2959 _checkport=""
2960 if [ "$_currentRoot" = "$NO_VALUE" ]; then
2961 _info "Standalone mode."
2962 if [ -z "$Le_HTTPPort" ]; then
2963 Le_HTTPPort=80
2964 else
2965 _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
2966 fi
2967 _checkport="$Le_HTTPPort"
2968 elif [ "$_currentRoot" = "$W_TLS" ]; then
2969 _info "Standalone tls mode."
2970 if [ -z "$Le_TLSPort" ]; then
2971 Le_TLSPort=443
2972 else
2973 _savedomainconf "Le_TLSPort" "$Le_TLSPort"
2974 fi
2975 _checkport="$Le_TLSPort"
2976 fi
2977
2978 if [ "$_checkport" ]; then
2979 _debug _checkport "$_checkport"
2980 _checkaddr="$(_getfield "$_chk_local_addr" $_addrIndex)"
2981 _debug _checkaddr "$_checkaddr"
2982
2983 _addrIndex="$(_math $_addrIndex + 1)"
2984
2985 _netprc="$(_ss "$_checkport" | grep "$_checkport")"
2986 netprc="$(echo "$_netprc" | grep "$_checkaddr")"
2987 if [ -z "$netprc" ]; then
2988 netprc="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
2989 fi
2990 if [ "$netprc" ]; then
2991 _err "$netprc"
2992 _err "tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
2993 _err "Please stop it first"
2994 return 1
2995 fi
2996 fi
2997 done
2998
2999 if _hasfield "$_chk_web_roots" "apache"; then
3000 if ! _setApache; then
3001 _err "set up apache error. Report error to me."
3002 return 1
3003 fi
3004 else
3005 usingApache=""
3006 fi
3007
3008 }
3009
3010 _on_issue_err() {
3011 _chk_post_hook="$1"
3012 _chk_vlist="$2"
3013 _debug _on_issue_err
3014 if [ "$LOG_FILE" ]; then
3015 _err "Please check log file for more details: $LOG_FILE"
3016 else
3017 _err "Please add '--debug' or '--log' to check more details."
3018 _err "See: $_DEBUG_WIKI"
3019 fi
3020
3021 #run the post hook
3022 if [ "$_chk_post_hook" ]; then
3023 _info "Run post hook:'$_chk_post_hook'"
3024 if ! (
3025 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3026 ); then
3027 _err "Error when run post hook."
3028 return 1
3029 fi
3030 fi
3031
3032 #trigger the validation to flush the pending authz
3033 _debug2 "_chk_vlist" "$_chk_vlist"
3034 if [ "$_chk_vlist" ]; then
3035 (
3036 _debug2 "start to deactivate authz"
3037 ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ')
3038 for ventry in $ventries; do
3039 d=$(echo "$ventry" | cut -d "$sep" -f 1)
3040 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
3041 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
3042 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
3043 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
3044 __trigger_validation "$uri" "$keyauthorization"
3045 done
3046 )
3047 fi
3048
3049 if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
3050 _debug "$(_dlg_versions)"
3051 fi
3052
3053 }
3054
3055 _on_issue_success() {
3056 _chk_post_hook="$1"
3057 _chk_renew_hook="$2"
3058 _debug _on_issue_success
3059 #run the post hook
3060 if [ "$_chk_post_hook" ]; then
3061 _info "Run post hook:'$_chk_post_hook'"
3062 if ! (
3063 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3064 ); then
3065 _err "Error when run post hook."
3066 return 1
3067 fi
3068 fi
3069
3070 #run renew hook
3071 if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
3072 _info "Run renew hook:'$_chk_renew_hook'"
3073 if ! (
3074 cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
3075 ); then
3076 _err "Error when run renew hook."
3077 return 1
3078 fi
3079 fi
3080
3081 }
3082
3083 updateaccount() {
3084 _initpath
3085 _regAccount
3086 }
3087
3088 registeraccount() {
3089 _reg_length="$1"
3090 _initpath
3091 _regAccount "$_reg_length"
3092 }
3093
3094 __calcAccountKeyHash() {
3095 [ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256 <"$ACCOUNT_KEY_PATH"
3096 }
3097
3098 __calc_account_thumbprint() {
3099 printf "%s" "$jwk" | tr -d ' ' | _digest "sha256" | _url_replace
3100 }
3101
3102 #keylength
3103 _regAccount() {
3104 _initpath
3105 _reg_length="$1"
3106
3107 mkdir -p "$CA_DIR"
3108 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3109 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3110 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3111 fi
3112
3113 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3114 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3115 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3116 fi
3117
3118 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3119 if ! _create_account_key "$_reg_length"; then
3120 _err "Create account key error."
3121 return 1
3122 fi
3123 fi
3124
3125 if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
3126 return 1
3127 fi
3128 _initAPI
3129 _updateTos=""
3130 _reg_res="$ACME_NEW_ACCOUNT_RES"
3131 while true; do
3132 _debug AGREEMENT "$AGREEMENT"
3133
3134 regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}'
3135
3136 if [ "$ACCOUNT_EMAIL" ]; then
3137 regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
3138 fi
3139
3140 if [ -z "$_updateTos" ]; then
3141 _info "Registering account"
3142
3143 if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
3144 _err "Register account Error: $response"
3145 return 1
3146 fi
3147
3148 if [ "$code" = "" ] || [ "$code" = '201' ]; then
3149 echo "$response" >"$ACCOUNT_JSON_PATH"
3150 _info "Registered"
3151 elif [ "$code" = '409' ]; then
3152 _info "Already registered"
3153 else
3154 _err "Register account Error: $response"
3155 return 1
3156 fi
3157
3158 _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
3159 _debug "_accUri" "$_accUri"
3160 _savecaconf "ACCOUNT_URL" "$_accUri"
3161 _tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')"
3162 _debug "_tos" "$_tos"
3163 if [ -z "$_tos" ]; then
3164 _debug "Use default tos: $DEFAULT_AGREEMENT"
3165 _tos="$DEFAULT_AGREEMENT"
3166 fi
3167 if [ "$_tos" != "$AGREEMENT" ]; then
3168 _updateTos=1
3169 AGREEMENT="$_tos"
3170 _reg_res="reg"
3171 continue
3172 fi
3173
3174 else
3175 _debug "Update tos: $_tos"
3176 if ! _send_signed_request "$_accUri" "$regjson"; then
3177 _err "Update tos error."
3178 return 1
3179 fi
3180 if [ "$code" = '202' ]; then
3181 _info "Update account tos info success."
3182
3183 CA_KEY_HASH="$(__calcAccountKeyHash)"
3184 _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
3185 _savecaconf CA_KEY_HASH "$CA_KEY_HASH"
3186 elif [ "$code" = '403' ]; then
3187 _err "It seems that the account key is already deactivated, please use a new account key."
3188 return 1
3189 else
3190 _err "Update account error."
3191 return 1
3192 fi
3193 fi
3194 ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
3195 _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
3196 return 0
3197 done
3198
3199 }
3200
3201 #Implement deactivate account
3202 deactivateaccount() {
3203 _initpath
3204
3205 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3206 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3207 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3208 fi
3209
3210 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3211 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3212 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3213 fi
3214
3215 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3216 _err "Account key is not found at: $ACCOUNT_KEY_PATH"
3217 return 1
3218 fi
3219
3220 _accUri=$(_readcaconf "ACCOUNT_URL")
3221 _debug _accUri "$_accUri"
3222
3223 if [ -z "$_accUri" ]; then
3224 _err "The account url is empty, please run '--update-account' first to update the account info first,"
3225 _err "Then try again."
3226 return 1
3227 fi
3228
3229 if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
3230 return 1
3231 fi
3232 _initAPI
3233
3234 if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
3235 _info "Deactivate account success for $_accUri."
3236 _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
3237 elif [ "$code" = "403" ]; then
3238 _info "The account is already deactivated."
3239 _accid=$(_getfield "$_accUri" "999" "/")
3240 else
3241 _err "Deactivate: account failed for $_accUri."
3242 return 1
3243 fi
3244
3245 _debug "Account id: $_accid"
3246 if [ "$_accid" ]; then
3247 _deactivated_account_path="$CA_DIR/deactivated/$_accid"
3248 _debug _deactivated_account_path "$_deactivated_account_path"
3249 if mkdir -p "$_deactivated_account_path"; then
3250 _info "Moving deactivated account info to $_deactivated_account_path/"
3251 mv "$CA_CONF" "$_deactivated_account_path/"
3252 mv "$ACCOUNT_JSON_PATH" "$_deactivated_account_path/"
3253 mv "$ACCOUNT_KEY_PATH" "$_deactivated_account_path/"
3254 else
3255 _err "Can not create dir: $_deactivated_account_path, try to remove the deactivated account key."
3256 rm -f "$CA_CONF"
3257 rm -f "$ACCOUNT_JSON_PATH"
3258 rm -f "$ACCOUNT_KEY_PATH"
3259 fi
3260 fi
3261 }
3262
3263 # domain folder file
3264 _findHook() {
3265 _hookdomain="$1"
3266 _hookcat="$2"
3267 _hookname="$3"
3268
3269 if [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname" ]; then
3270 d_api="$_SCRIPT_HOME/$_hookcat/$_hookname"
3271 elif [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname.sh" ]; then
3272 d_api="$_SCRIPT_HOME/$_hookcat/$_hookname.sh"
3273 elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then
3274 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname"
3275 elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then
3276 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
3277 elif [ -f "$LE_WORKING_DIR/$_hookname" ]; then
3278 d_api="$LE_WORKING_DIR/$_hookname"
3279 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ]; then
3280 d_api="$LE_WORKING_DIR/$_hookname.sh"
3281 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ]; then
3282 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname"
3283 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ]; then
3284 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
3285 fi
3286
3287 printf "%s" "$d_api"
3288 }
3289
3290 #domain
3291 __get_domain_new_authz() {
3292 _gdnd="$1"
3293 _info "Getting new-authz for domain" "$_gdnd"
3294 _initAPI
3295 _Max_new_authz_retry_times=5
3296 _authz_i=0
3297 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do
3298 _debug "Try new-authz for the $_authz_i time."
3299 if ! _send_signed_request "${ACME_NEW_AUTHZ}" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then
3300 _err "Can not get domain new authz."
3301 return 1
3302 fi
3303 if _contains "$response" "No registration exists matching provided key"; then
3304 _err "It seems there is an error, but it's recovered now, please try again."
3305 _err "If you see this message for a second time, please report bug: $(__green "$PROJECT")"
3306 _clearcaconf "CA_KEY_HASH"
3307 break
3308 fi
3309 if ! _contains "$response" "An error occurred while processing your request"; then
3310 _info "The new-authz request is ok."
3311 break
3312 fi
3313 _authz_i="$(_math "$_authz_i" + 1)"
3314 _info "The server is busy, Sleep $_authz_i to retry."
3315 _sleep "$_authz_i"
3316 done
3317
3318 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ]; then
3319 _err "new-authz retry reach the max $_Max_new_authz_retry_times times."
3320 fi
3321
3322 if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then
3323 _err "new-authz error: $response"
3324 return 1
3325 fi
3326
3327 }
3328
3329 #uri keyAuthorization
3330 __trigger_validation() {
3331 _debug2 "tigger domain validation."
3332 _t_url="$1"
3333 _debug2 _t_url "$_t_url"
3334 _t_key_authz="$2"
3335 _debug2 _t_key_authz "$_t_key_authz"
3336 _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
3337 }
3338
3339 #webroot, domain domainlist keylength
3340 issue() {
3341 if [ -z "$2" ]; then
3342 _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
3343 return 1
3344 fi
3345 if [ -z "$1" ]; then
3346 _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
3347 return 1
3348 fi
3349 _web_roots="$1"
3350 _main_domain="$2"
3351 _alt_domains="$3"
3352 if _contains "$_main_domain" ","; then
3353 _main_domain=$(echo "$2,$3" | cut -d , -f 1)
3354 _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
3355 fi
3356 _key_length="$4"
3357 _real_cert="$5"
3358 _real_key="$6"
3359 _real_ca="$7"
3360 _reload_cmd="$8"
3361 _real_fullchain="$9"
3362 _pre_hook="${10}"
3363 _post_hook="${11}"
3364 _renew_hook="${12}"
3365 _local_addr="${13}"
3366
3367 #remove these later.
3368 if [ "$_web_roots" = "dns-cf" ]; then
3369 _web_roots="dns_cf"
3370 fi
3371 if [ "$_web_roots" = "dns-dp" ]; then
3372 _web_roots="dns_dp"
3373 fi
3374 if [ "$_web_roots" = "dns-cx" ]; then
3375 _web_roots="dns_cx"
3376 fi
3377
3378 if [ ! "$IS_RENEW" ]; then
3379 _initpath "$_main_domain" "$_key_length"
3380 mkdir -p "$DOMAIN_PATH"
3381 fi
3382
3383 _debug "Using ACME_DIRECTORY: $ACME_DIRECTORY"
3384
3385 _initAPI
3386
3387 if [ -f "$DOMAIN_CONF" ]; then
3388 Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
3389 _debug Le_NextRenewTime "$Le_NextRenewTime"
3390 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
3391 _saved_domain=$(_readdomainconf Le_Domain)
3392 _debug _saved_domain "$_saved_domain"
3393 _saved_alt=$(_readdomainconf Le_Alt)
3394 _debug _saved_alt "$_saved_alt"
3395 if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
3396 _info "Domains not changed."
3397 _info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
3398 _info "Add '$(__red '--force')' to force to renew."
3399 return $RENEW_SKIP
3400 else
3401 _info "Domains have changed."
3402 fi
3403 fi
3404 fi
3405
3406 _savedomainconf "Le_Domain" "$_main_domain"
3407 _savedomainconf "Le_Alt" "$_alt_domains"
3408 _savedomainconf "Le_Webroot" "$_web_roots"
3409
3410 _savedomainconf "Le_PreHook" "$_pre_hook"
3411 _savedomainconf "Le_PostHook" "$_post_hook"
3412 _savedomainconf "Le_RenewHook" "$_renew_hook"
3413
3414 if [ "$_local_addr" ]; then
3415 _savedomainconf "Le_LocalAddress" "$_local_addr"
3416 else
3417 _cleardomainconf "Le_LocalAddress"
3418 fi
3419
3420 Le_API="$ACME_DIRECTORY"
3421 _savedomainconf "Le_API" "$Le_API"
3422
3423 if [ "$_alt_domains" = "$NO_VALUE" ]; then
3424 _alt_domains=""
3425 fi
3426
3427 if [ "$_key_length" = "$NO_VALUE" ]; then
3428 _key_length=""
3429 fi
3430
3431 if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
3432 _err "_on_before_issue."
3433 return 1
3434 fi
3435
3436 _saved_account_key_hash="$(_readcaconf "CA_KEY_HASH")"
3437 _debug2 _saved_account_key_hash "$_saved_account_key_hash"
3438
3439 if [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
3440 if ! _regAccount "$_accountkeylength"; then
3441 _on_issue_err "$_post_hook"
3442 return 1
3443 fi
3444 else
3445 _debug "_saved_account_key_hash is not changed, skip register account."
3446 fi
3447
3448 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
3449 _info "Signing from existing CSR."
3450 else
3451 _key=$(_readdomainconf Le_Keylength)
3452 _debug "Read key length:$_key"
3453 if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then
3454 if ! createDomainKey "$_main_domain" "$_key_length"; then
3455 _err "Create domain key error."
3456 _clearup
3457 _on_issue_err "$_post_hook"
3458 return 1
3459 fi
3460 fi
3461
3462 if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
3463 _err "Create CSR error."
3464 _clearup
3465 _on_issue_err "$_post_hook"
3466 return 1
3467 fi
3468 fi
3469
3470 _savedomainconf "Le_Keylength" "$_key_length"
3471
3472 vlist="$Le_Vlist"
3473
3474 _info "Getting domain auth token for each domain"
3475 sep='#'
3476 dvsep=','
3477 if [ -z "$vlist" ]; then
3478 alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ')
3479 _index=1
3480 _currentRoot=""
3481 for d in $alldomains; do
3482 _info "Getting webroot for domain" "$d"
3483 _w="$(echo $_web_roots | cut -d , -f $_index)"
3484 _debug _w "$_w"
3485 if [ "$_w" ]; then
3486 _currentRoot="$_w"
3487 fi
3488 _debug "_currentRoot" "$_currentRoot"
3489 _index=$(_math $_index + 1)
3490
3491 vtype="$VTYPE_HTTP"
3492 if _startswith "$_currentRoot" "dns"; then
3493 vtype="$VTYPE_DNS"
3494 fi
3495
3496 if [ "$_currentRoot" = "$W_TLS" ]; then
3497 vtype="$VTYPE_TLS"
3498 fi
3499
3500 if ! __get_domain_new_authz "$d"; then
3501 _clearup
3502 _on_issue_err "$_post_hook"
3503 return 1
3504 fi
3505
3506 if [ -z "$thumbprint" ]; then
3507 thumbprint="$(__calc_account_thumbprint)"
3508 fi
3509
3510 entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
3511 _debug entry "$entry"
3512 if [ -z "$entry" ]; then
3513 _err "Error, can not get domain token $d"
3514 _clearup
3515 _on_issue_err "$_post_hook"
3516 return 1
3517 fi
3518 token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
3519 _debug token "$token"
3520
3521 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
3522 _debug uri "$uri"
3523
3524 keyauthorization="$token.$thumbprint"
3525 _debug keyauthorization "$keyauthorization"
3526
3527 if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
3528 _debug "$d is already verified, skip."
3529 keyauthorization="$STATE_VERIFIED"
3530 _debug keyauthorization "$keyauthorization"
3531 fi
3532
3533 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
3534 _debug dvlist "$dvlist"
3535
3536 vlist="$vlist$dvlist$dvsep"
3537
3538 done
3539 _debug vlist "$vlist"
3540 #add entry
3541 dnsadded=""
3542 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
3543 for ventry in $ventries; do
3544 d=$(echo "$ventry" | cut -d "$sep" -f 1)
3545 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
3546 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
3547 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
3548
3549 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
3550 _debug "$d is already verified, skip $vtype."
3551 continue
3552 fi
3553
3554 if [ "$vtype" = "$VTYPE_DNS" ]; then
3555 dnsadded='0'
3556 txtdomain="_acme-challenge.$d"
3557 _debug txtdomain "$txtdomain"
3558 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
3559 _debug txt "$txt"
3560
3561 d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
3562
3563 _debug d_api "$d_api"
3564
3565 if [ "$d_api" ]; then
3566 _info "Found domain api file: $d_api"
3567 else
3568 _info "$(__red "Add the following TXT record:")"
3569 _info "$(__red "Domain: '$(__green "$txtdomain")'")"
3570 _info "$(__red "TXT value: '$(__green "$txt")'")"
3571 _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
3572 _info "$(__red "so the resulting subdomain will be: $txtdomain")"
3573 continue
3574 fi
3575
3576 (
3577 if ! . "$d_api"; then
3578 _err "Load file $d_api error. Please check your api file and try again."
3579 return 1
3580 fi
3581
3582 addcommand="${_currentRoot}_add"
3583 if ! _exists "$addcommand"; then
3584 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
3585 return 1
3586 fi
3587
3588 if ! $addcommand "$txtdomain" "$txt"; then
3589 _err "Error add txt for domain:$txtdomain"
3590 return 1
3591 fi
3592 )
3593
3594 if [ "$?" != "0" ]; then
3595 _clearup
3596 _on_issue_err "$_post_hook" "$vlist"
3597 return 1
3598 fi
3599 dnsadded='1'
3600 fi
3601 done
3602
3603 if [ "$dnsadded" = '0' ]; then
3604 _savedomainconf "Le_Vlist" "$vlist"
3605 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
3606 _err "Please add the TXT records to the domains, and retry again."
3607 _clearup
3608 _on_issue_err "$_post_hook"
3609 return 1
3610 fi
3611
3612 fi
3613
3614 if [ "$dnsadded" = '1' ]; then
3615 if [ -z "$Le_DNSSleep" ]; then
3616 Le_DNSSleep="$DEFAULT_DNS_SLEEP"
3617 else
3618 _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
3619 fi
3620
3621 _info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
3622 _sleep "$Le_DNSSleep"
3623 fi
3624
3625 NGINX_RESTORE_VLIST=""
3626 _debug "ok, let's start to verify"
3627
3628 _ncIndex=1
3629 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
3630 for ventry in $ventries; do
3631 d=$(echo "$ventry" | cut -d "$sep" -f 1)
3632 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
3633 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
3634 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
3635 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
3636
3637 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
3638 _info "$d is already verified, skip $vtype."
3639 continue
3640 fi
3641
3642 _info "Verifying:$d"
3643 _debug "d" "$d"
3644 _debug "keyauthorization" "$keyauthorization"
3645 _debug "uri" "$uri"
3646 removelevel=""
3647 token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
3648
3649 _debug "_currentRoot" "$_currentRoot"
3650
3651 if [ "$vtype" = "$VTYPE_HTTP" ]; then
3652 if [ "$_currentRoot" = "$NO_VALUE" ]; then
3653 _info "Standalone mode server"
3654 _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
3655 _ncIndex="$(_math $_ncIndex + 1)"
3656 _startserver "$keyauthorization" "$_ncaddr" &
3657 if [ "$?" != "0" ]; then
3658 _clearup
3659 _on_issue_err "$_post_hook" "$vlist"
3660 return 1
3661 fi
3662 serverproc="$!"
3663 sleep 1
3664 _debug serverproc "$serverproc"
3665 elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
3666 _info "Stateless mode for domain:$d"
3667 _sleep 1
3668 elif _startswith "$_currentRoot" "$NGINX"; then
3669 _info "Nginx mode for domain:$d"
3670 #set up nginx server
3671 FOUND_REAL_NGINX_CONF=""
3672 BACKUP_NGINX_CONF=""
3673 if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
3674 _clearup
3675 _on_issue_err "$_post_hook" "$vlist"
3676 return 1
3677 fi
3678
3679 if [ "$FOUND_REAL_NGINX_CONF" ]; then
3680 _realConf="$FOUND_REAL_NGINX_CONF"
3681 _backup="$BACKUP_NGINX_CONF"
3682 _debug _realConf "$_realConf"
3683 NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
3684 fi
3685 _sleep 1
3686 else
3687 if [ "$_currentRoot" = "apache" ]; then
3688 wellknown_path="$ACME_DIR"
3689 else
3690 wellknown_path="$_currentRoot/.well-known/acme-challenge"
3691 if [ ! -d "$_currentRoot/.well-known" ]; then
3692 removelevel='1'
3693 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ]; then
3694 removelevel='2'
3695 else
3696 removelevel='3'
3697 fi
3698 fi
3699
3700 _debug wellknown_path "$wellknown_path"
3701
3702 _debug "writing token:$token to $wellknown_path/$token"
3703
3704 mkdir -p "$wellknown_path"
3705
3706 if ! printf "%s" "$keyauthorization" >"$wellknown_path/$token"; then
3707 _err "$d:Can not write token to file : $wellknown_path/$token"
3708 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3709 _clearup
3710 _on_issue_err "$_post_hook" "$vlist"
3711 return 1
3712 fi
3713
3714 if [ ! "$usingApache" ]; then
3715 if webroot_owner=$(_stat "$_currentRoot"); then
3716 _debug "Changing owner/group of .well-known to $webroot_owner"
3717 if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
3718 _debug "$(cat "$_EXEC_TEMP_ERR")"
3719 _exec_err >/dev/null 2>&1
3720 fi
3721 else
3722 _debug "not changing owner/group of webroot"
3723 fi
3724 fi
3725
3726 fi
3727
3728 elif [ "$vtype" = "$VTYPE_TLS" ]; then
3729 #create A
3730 #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
3731 #_debug2 _hash_A "$_hash_A"
3732 #_x="$(echo $_hash_A | cut -c 1-32)"
3733 #_debug2 _x "$_x"
3734 #_y="$(echo $_hash_A | cut -c 33-64)"
3735 #_debug2 _y "$_y"
3736 #_SAN_A="$_x.$_y.token.acme.invalid"
3737 #_debug2 _SAN_A "$_SAN_A"
3738
3739 #create B
3740 _hash_B="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
3741 _debug2 _hash_B "$_hash_B"
3742 _x="$(echo "$_hash_B" | cut -c 1-32)"
3743 _debug2 _x "$_x"
3744 _y="$(echo "$_hash_B" | cut -c 33-64)"
3745 _debug2 _y "$_y"
3746
3747 #_SAN_B="$_x.$_y.ka.acme.invalid"
3748
3749 _SAN_B="$_x.$_y.acme.invalid"
3750 _debug2 _SAN_B "$_SAN_B"
3751
3752 _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
3753 _ncIndex="$(_math "$_ncIndex" + 1)"
3754 if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
3755 _err "Start tls server error."
3756 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3757 _clearup
3758 _on_issue_err "$_post_hook" "$vlist"
3759 return 1
3760 fi
3761 fi
3762
3763 if ! __trigger_validation "$uri" "$keyauthorization"; then
3764 _err "$d:Can not get challenge: $response"
3765 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3766 _clearup
3767 _on_issue_err "$_post_hook" "$vlist"
3768 return 1
3769 fi
3770
3771 if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then
3772 _err "$d:Challenge error: $response"
3773 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3774 _clearup
3775 _on_issue_err "$_post_hook" "$vlist"
3776 return 1
3777 fi
3778
3779 waittimes=0
3780 if [ -z "$MAX_RETRY_TIMES" ]; then
3781 MAX_RETRY_TIMES=30
3782 fi
3783
3784 while true; do
3785 waittimes=$(_math "$waittimes" + 1)
3786 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
3787 _err "$d:Timeout"
3788 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3789 _clearup
3790 _on_issue_err "$_post_hook" "$vlist"
3791 return 1
3792 fi
3793
3794 _debug "sleep 2 secs to verify"
3795 sleep 2
3796 _debug "checking"
3797 response="$(_get "$uri")"
3798 if [ "$?" != "0" ]; then
3799 _err "$d:Verify error:$response"
3800 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3801 _clearup
3802 _on_issue_err "$_post_hook" "$vlist"
3803 return 1
3804 fi
3805 _debug2 original "$response"
3806
3807 response="$(echo "$response" | _normalizeJson)"
3808 _debug2 response "$response"
3809
3810 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
3811 if [ "$status" = "valid" ]; then
3812 _info "$(__green Success)"
3813 _stopserver "$serverproc"
3814 serverproc=""
3815 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3816 break
3817 fi
3818
3819 if [ "$status" = "invalid" ]; then
3820 error="$(echo "$response" | tr -d "\r\n" | _egrep_o '"error":\{[^\}]*')"
3821 _debug2 error "$error"
3822 errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
3823 _debug2 errordetail "$errordetail"
3824 if [ "$errordetail" ]; then
3825 _err "$d:Verify error:$errordetail"
3826 else
3827 _err "$d:Verify error:$error"
3828 fi
3829 if [ "$DEBUG" ]; then
3830 if [ "$vtype" = "$VTYPE_HTTP" ]; then
3831 _debug "Debug: get token url."
3832 _get "http://$d/.well-known/acme-challenge/$token" "" 1
3833 fi
3834 fi
3835 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3836 _clearup
3837 _on_issue_err "$_post_hook" "$vlist"
3838 return 1
3839 fi
3840
3841 if [ "$status" = "pending" ]; then
3842 _info "Pending"
3843 else
3844 _err "$d:Verify error:$response"
3845 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
3846 _clearup
3847 _on_issue_err "$_post_hook" "$vlist"
3848 return 1
3849 fi
3850
3851 done
3852
3853 done
3854
3855 _clearup
3856 _info "Verify finished, start to sign."
3857 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
3858
3859 if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
3860 _err "Sign failed."
3861 _on_issue_err "$_post_hook"
3862 return 1
3863 fi
3864
3865 _rcert="$response"
3866 Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
3867 _debug "Le_LinkCert" "$Le_LinkCert"
3868 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
3869
3870 if [ "$Le_LinkCert" ]; then
3871 echo "$BEGIN_CERT" >"$CERT_PATH"
3872
3873 #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
3874 # _debug "Get cert failed. Let's try last response."
3875 # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
3876 #fi
3877
3878 if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
3879 _debug "Try cert link."
3880 _get "$Le_LinkCert" | _base64 "multiline" >>"$CERT_PATH"
3881 fi
3882
3883 echo "$END_CERT" >>"$CERT_PATH"
3884 _info "$(__green "Cert success.")"
3885 cat "$CERT_PATH"
3886
3887 _info "Your cert is in $(__green " $CERT_PATH ")"
3888
3889 if [ -f "$CERT_KEY_PATH" ]; then
3890 _info "Your cert key is in $(__green " $CERT_KEY_PATH ")"
3891 fi
3892
3893 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
3894
3895 if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then
3896 USER_PATH="$PATH"
3897 _saveaccountconf "USER_PATH" "$USER_PATH"
3898 fi
3899 fi
3900
3901 if [ -z "$Le_LinkCert" ]; then
3902 response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)"
3903 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
3904 _on_issue_err "$_post_hook"
3905 return 1
3906 fi
3907
3908 _cleardomainconf "Le_Vlist"
3909
3910 Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
3911 if ! _contains "$Le_LinkIssuer" ":"; then
3912 _info "$(__red "Relative issuer link found.")"
3913 Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
3914 fi
3915 _debug Le_LinkIssuer "$Le_LinkIssuer"
3916 _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
3917
3918 if [ "$Le_LinkIssuer" ]; then
3919 _link_issuer_retry=0
3920 _MAX_ISSUER_RETRY=5
3921 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
3922 _debug _link_issuer_retry "$_link_issuer_retry"
3923 if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
3924 echo "$BEGIN_CERT" >"$CA_CERT_PATH"
3925 _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
3926 echo "$END_CERT" >>"$CA_CERT_PATH"
3927
3928 _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
3929 cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
3930 _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
3931
3932 rm -f "$CA_CERT_PATH.der"
3933 break
3934 fi
3935 _link_issuer_retry=$(_math $_link_issuer_retry + 1)
3936 _sleep "$_link_issuer_retry"
3937 done
3938 if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
3939 _err "Max retry for issuer ca cert is reached."
3940 fi
3941 else
3942 _debug "No Le_LinkIssuer header found."
3943 fi
3944
3945 Le_CertCreateTime=$(_time)
3946 _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
3947
3948 Le_CertCreateTimeStr=$(date -u)
3949 _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
3950
3951 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ]; then
3952 Le_RenewalDays="$MAX_RENEW"
3953 else
3954 _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
3955 fi
3956
3957 if [ "$CA_BUNDLE" ]; then
3958 _saveaccountconf CA_BUNDLE "$CA_BUNDLE"
3959 else
3960 _clearaccountconf "CA_BUNDLE"
3961 fi
3962
3963 if [ "$CA_PATH" ]; then
3964 _saveaccountconf CA_PATH "$CA_PATH"
3965 else
3966 _clearaccountconf "CA_PATH"
3967 fi
3968
3969 if [ "$HTTPS_INSECURE" ]; then
3970 _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
3971 else
3972 _clearaccountconf "HTTPS_INSECURE"
3973 fi
3974
3975 if [ "$Le_Listen_V4" ]; then
3976 _savedomainconf "Le_Listen_V4" "$Le_Listen_V4"
3977 _cleardomainconf Le_Listen_V6
3978 elif [ "$Le_Listen_V6" ]; then
3979 _savedomainconf "Le_Listen_V6" "$Le_Listen_V6"
3980 _cleardomainconf Le_Listen_V4
3981 fi
3982
3983 if [ "$Le_ForceNewDomainKey" = "1" ]; then
3984 _savedomainconf "Le_ForceNewDomainKey" "$Le_ForceNewDomainKey"
3985 else
3986 _cleardomainconf Le_ForceNewDomainKey
3987 fi
3988
3989 Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
3990
3991 Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
3992 _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
3993
3994 Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
3995 _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
3996
3997 _on_issue_success "$_post_hook" "$_renew_hook"
3998
3999 if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
4000 _savedomainconf "Le_RealCertPath" "$_real_cert"
4001 _savedomainconf "Le_RealCACertPath" "$_real_ca"
4002 _savedomainconf "Le_RealKeyPath" "$_real_key"
4003 _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
4004 _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
4005 _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
4006 fi
4007
4008 }
4009
4010 #domain [isEcc]
4011 renew() {
4012 Le_Domain="$1"
4013 if [ -z "$Le_Domain" ]; then
4014 _usage "Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
4015 return 1
4016 fi
4017
4018 _isEcc="$2"
4019
4020 _initpath "$Le_Domain" "$_isEcc"
4021
4022 _info "$(__green "Renew: '$Le_Domain'")"
4023 if [ ! -f "$DOMAIN_CONF" ]; then
4024 _info "'$Le_Domain' is not a issued domain, skip."
4025 return 0
4026 fi
4027
4028 if [ "$Le_RenewalDays" ]; then
4029 _savedomainconf Le_RenewalDays "$Le_RenewalDays"
4030 fi
4031
4032 . "$DOMAIN_CONF"
4033
4034 if [ "$Le_API" ]; then
4035 if [ "$_OLD_CA_HOST" = "$Le_API" ]; then
4036 export Le_API="$DEFAULT_CA"
4037 _savedomainconf Le_API "$Le_API"
4038 fi
4039 if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then
4040 export Le_API="$STAGE_CA"
4041 _savedomainconf Le_API "$Le_API"
4042 fi
4043 export ACME_DIRECTORY="$Le_API"
4044 #reload ca configs
4045 ACCOUNT_KEY_PATH=""
4046 ACCOUNT_JSON_PATH=""
4047 CA_CONF=""
4048 _debug3 "initpath again."
4049 _initpath "$Le_Domain" "$_isEcc"
4050 fi
4051
4052 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
4053 _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
4054 _info "Add '$(__red '--force')' to force to renew."
4055 return "$RENEW_SKIP"
4056 fi
4057
4058 if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
4059 _info "Skip invalid cert for: $Le_Domain"
4060 return 0
4061 fi
4062
4063 IS_RENEW="1"
4064 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress"
4065 res="$?"
4066 if [ "$res" != "0" ]; then
4067 return "$res"
4068 fi
4069
4070 if [ "$Le_DeployHook" ]; then
4071 _deploy "$Le_Domain" "$Le_DeployHook"
4072 res="$?"
4073 fi
4074
4075 IS_RENEW=""
4076
4077 return "$res"
4078 }
4079
4080 #renewAll [stopRenewOnError]
4081 renewAll() {
4082 _initpath
4083 _stopRenewOnError="$1"
4084 _debug "_stopRenewOnError" "$_stopRenewOnError"
4085 _ret="0"
4086
4087 for di in "${CERT_HOME}"/*.*/; do
4088 _debug di "$di"
4089 if ! [ -d "$di" ]; then
4090 _debug "Not directory, skip: $di"
4091 continue
4092 fi
4093 d=$(basename "$di")
4094 _debug d "$d"
4095 (
4096 if _endswith "$d" "$ECC_SUFFIX"; then
4097 _isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2)
4098 d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
4099 fi
4100 renew "$d" "$_isEcc"
4101 )
4102 rc="$?"
4103 _debug "Return code: $rc"
4104 if [ "$rc" != "0" ]; then
4105 if [ "$rc" = "$RENEW_SKIP" ]; then
4106 _info "Skipped $d"
4107 elif [ "$_stopRenewOnError" ]; then
4108 _err "Error renew $d, stop now."
4109 return "$rc"
4110 else
4111 _ret="$rc"
4112 _err "Error renew $d."
4113 fi
4114 fi
4115 done
4116 return "$_ret"
4117 }
4118
4119 #csr webroot
4120 signcsr() {
4121 _csrfile="$1"
4122 _csrW="$2"
4123 if [ -z "$_csrfile" ] || [ -z "$_csrW" ]; then
4124 _usage "Usage: $PROJECT_ENTRY --signcsr --csr mycsr.csr -w /path/to/webroot/a.com/ "
4125 return 1
4126 fi
4127
4128 _initpath
4129
4130 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
4131 if [ "$?" != "0" ]; then
4132 _err "Can not read subject from csr: $_csrfile"
4133 return 1
4134 fi
4135 _debug _csrsubj "$_csrsubj"
4136 if _contains "$_csrsubj" ' ' || ! _contains "$_csrsubj" '.'; then
4137 _info "It seems that the subject: $_csrsubj is not a valid domain name. Drop it."
4138 _csrsubj=""
4139 fi
4140
4141 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
4142 if [ "$?" != "0" ]; then
4143 _err "Can not read domain list from csr: $_csrfile"
4144 return 1
4145 fi
4146 _debug "_csrdomainlist" "$_csrdomainlist"
4147
4148 if [ -z "$_csrsubj" ]; then
4149 _csrsubj="$(_getfield "$_csrdomainlist" 1)"
4150 _debug _csrsubj "$_csrsubj"
4151 _csrdomainlist="$(echo "$_csrdomainlist" | cut -d , -f 2-)"
4152 _debug "_csrdomainlist" "$_csrdomainlist"
4153 fi
4154
4155 if [ -z "$_csrsubj" ]; then
4156 _err "Can not read subject from csr: $_csrfile"
4157 return 1
4158 fi
4159
4160 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
4161 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ]; then
4162 _err "Can not read key length from csr: $_csrfile"
4163 return 1
4164 fi
4165
4166 _initpath "$_csrsubj" "$_csrkeylength"
4167 mkdir -p "$DOMAIN_PATH"
4168
4169 _info "Copy csr to: $CSR_PATH"
4170 cp "$_csrfile" "$CSR_PATH"
4171
4172 issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength"
4173
4174 }
4175
4176 showcsr() {
4177 _csrfile="$1"
4178 _csrd="$2"
4179 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
4180 _usage "Usage: $PROJECT_ENTRY --showcsr --csr mycsr.csr"
4181 return 1
4182 fi
4183
4184 _initpath
4185
4186 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
4187 if [ "$?" != "0" ] || [ -z "$_csrsubj" ]; then
4188 _err "Can not read subject from csr: $_csrfile"
4189 return 1
4190 fi
4191
4192 _info "Subject=$_csrsubj"
4193
4194 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
4195 if [ "$?" != "0" ]; then
4196 _err "Can not read domain list from csr: $_csrfile"
4197 return 1
4198 fi
4199 _debug "_csrdomainlist" "$_csrdomainlist"
4200
4201 _info "SubjectAltNames=$_csrdomainlist"
4202
4203 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
4204 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ]; then
4205 _err "Can not read key length from csr: $_csrfile"
4206 return 1
4207 fi
4208 _info "KeyLength=$_csrkeylength"
4209 }
4210
4211 list() {
4212 _raw="$1"
4213 _initpath
4214
4215 _sep="|"
4216 if [ "$_raw" ]; then
4217 printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew"
4218 for di in "${CERT_HOME}"/*.*/; do
4219 if ! [ -d "$di" ]; then
4220 _debug "Not directory, skip: $di"
4221 continue
4222 fi
4223 d=$(basename "$di")
4224 _debug d "$d"
4225 (
4226 if _endswith "$d" "$ECC_SUFFIX"; then
4227 _isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2)
4228 d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
4229 fi
4230 _initpath "$d" "$_isEcc"
4231 if [ -f "$DOMAIN_CONF" ]; then
4232 . "$DOMAIN_CONF"
4233 printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
4234 fi
4235 )
4236 done
4237 else
4238 if _exists column; then
4239 list "raw" | column -t -s "$_sep"
4240 else
4241 list "raw" | tr "$_sep" '\t'
4242 fi
4243 fi
4244
4245 }
4246
4247 _deploy() {
4248 _d="$1"
4249 _hooks="$2"
4250
4251 for _d_api in $(echo "$_hooks" | tr ',' " "); do
4252 _deployApi="$(_findHook "$_d" deploy "$_d_api")"
4253 if [ -z "$_deployApi" ]; then
4254 _err "The deploy hook $_d_api is not found."
4255 return 1
4256 fi
4257 _debug _deployApi "$_deployApi"
4258
4259 if ! (
4260 if ! . "$_deployApi"; then
4261 _err "Load file $_deployApi error. Please check your api file and try again."
4262 return 1
4263 fi
4264
4265 d_command="${_d_api}_deploy"
4266 if ! _exists "$d_command"; then
4267 _err "It seems that your api file is not correct, it must have a function named: $d_command"
4268 return 1
4269 fi
4270
4271 if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
4272 _err "Error deploy for domain:$_d"
4273 return 1
4274 fi
4275 ); then
4276 _err "Deploy error."
4277 return 1
4278 else
4279 _info "$(__green Success)"
4280 fi
4281 done
4282 }
4283
4284 #domain hooks
4285 deploy() {
4286 _d="$1"
4287 _hooks="$2"
4288 _isEcc="$3"
4289 if [ -z "$_hooks" ]; then
4290 _usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
4291 return 1
4292 fi
4293
4294 _initpath "$_d" "$_isEcc"
4295 if [ ! -d "$DOMAIN_PATH" ]; then
4296 _err "Domain is not valid:'$_d'"
4297 return 1
4298 fi
4299
4300 . "$DOMAIN_CONF"
4301
4302 _savedomainconf Le_DeployHook "$_hooks"
4303
4304 _deploy "$_d" "$_hooks"
4305 }
4306
4307 installcert() {
4308 _main_domain="$1"
4309 if [ -z "$_main_domain" ]; then
4310 _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--cert-file cert-file-path] [--key-file key-file-path] [--ca-file ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchain-file fullchain-path]"
4311 return 1
4312 fi
4313
4314 _real_cert="$2"
4315 _real_key="$3"
4316 _real_ca="$4"
4317 _reload_cmd="$5"
4318 _real_fullchain="$6"
4319 _isEcc="$7"
4320
4321 _initpath "$_main_domain" "$_isEcc"
4322 if [ ! -d "$DOMAIN_PATH" ]; then
4323 _err "Domain is not valid:'$_main_domain'"
4324 return 1
4325 fi
4326
4327 _savedomainconf "Le_RealCertPath" "$_real_cert"
4328 _savedomainconf "Le_RealCACertPath" "$_real_ca"
4329 _savedomainconf "Le_RealKeyPath" "$_real_key"
4330 _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
4331 _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
4332
4333 _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
4334 }
4335
4336 #domain cert key ca fullchain reloadcmd backup-prefix
4337 _installcert() {
4338 _main_domain="$1"
4339 _real_cert="$2"
4340 _real_key="$3"
4341 _real_ca="$4"
4342 _real_fullchain="$5"
4343 _reload_cmd="$6"
4344 _backup_prefix="$7"
4345
4346 if [ "$_real_cert" = "$NO_VALUE" ]; then
4347 _real_cert=""
4348 fi
4349 if [ "$_real_key" = "$NO_VALUE" ]; then
4350 _real_key=""
4351 fi
4352 if [ "$_real_ca" = "$NO_VALUE" ]; then
4353 _real_ca=""
4354 fi
4355 if [ "$_reload_cmd" = "$NO_VALUE" ]; then
4356 _reload_cmd=""
4357 fi
4358 if [ "$_real_fullchain" = "$NO_VALUE" ]; then
4359 _real_fullchain=""
4360 fi
4361
4362 _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
4363 mkdir -p "$_backup_path"
4364
4365 if [ "$_real_cert" ]; then
4366 _info "Installing cert to:$_real_cert"
4367 if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then
4368 cp "$_real_cert" "$_backup_path/cert.bak"
4369 fi
4370 cat "$CERT_PATH" >"$_real_cert"
4371 fi
4372
4373 if [ "$_real_ca" ]; then
4374 _info "Installing CA to:$_real_ca"
4375 if [ "$_real_ca" = "$_real_cert" ]; then
4376 echo "" >>"$_real_ca"
4377 cat "$CA_CERT_PATH" >>"$_real_ca"
4378 else
4379 if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then
4380 cp "$_real_ca" "$_backup_path/ca.bak"
4381 fi
4382 cat "$CA_CERT_PATH" >"$_real_ca"
4383 fi
4384 fi
4385
4386 if [ "$_real_key" ]; then
4387 _info "Installing key to:$_real_key"
4388 if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
4389 cp "$_real_key" "$_backup_path/key.bak"
4390 fi
4391 cat "$CERT_KEY_PATH" >"$_real_key"
4392 fi
4393
4394 if [ "$_real_fullchain" ]; then
4395 _info "Installing full chain to:$_real_fullchain"
4396 if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then
4397 cp "$_real_fullchain" "$_backup_path/fullchain.bak"
4398 fi
4399 cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
4400 fi
4401
4402 if [ "$_reload_cmd" ]; then
4403 _info "Run reload cmd: $_reload_cmd"
4404 if (
4405 export CERT_PATH
4406 export CERT_KEY_PATH
4407 export CA_CERT_PATH
4408 export CERT_FULLCHAIN_PATH
4409 export Le_Domain
4410 cd "$DOMAIN_PATH" && eval "$_reload_cmd"
4411 ); then
4412 _info "$(__green "Reload success")"
4413 else
4414 _err "Reload error for :$Le_Domain"
4415 fi
4416 fi
4417
4418 }
4419
4420 #confighome
4421 installcronjob() {
4422 _c_home="$1"
4423 _initpath
4424 _CRONTAB="crontab"
4425 if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
4426 _CRONTAB="fcrontab"
4427 fi
4428 if ! _exists "$_CRONTAB"; then
4429 _err "crontab/fcrontab doesn't exist, so, we can not install cron jobs."
4430 _err "All your certs will not be renewed automatically."
4431 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
4432 return 1
4433 fi
4434
4435 _info "Installing cron job"
4436 if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
4437 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
4438 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
4439 else
4440 _err "Can not install cronjob, $PROJECT_ENTRY not found."
4441 return 1
4442 fi
4443
4444 if [ "$_c_home" ]; then
4445 _c_entry="--config-home \"$_c_home\" "
4446 fi
4447 _t=$(_time)
4448 random_minute=$(_math $_t % 60)
4449 if _exists uname && uname -a | grep SunOS >/dev/null; then
4450 $_CRONTAB -l | {
4451 cat
4452 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
4453 } | $_CRONTAB --
4454 else
4455 $_CRONTAB -l | {
4456 cat
4457 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
4458 } | $_CRONTAB -
4459 fi
4460 fi
4461 if [ "$?" != "0" ]; then
4462 _err "Install cron job failed. You need to manually renew your certs."
4463 _err "Or you can add cronjob by yourself:"
4464 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
4465 return 1
4466 fi
4467 }
4468
4469 uninstallcronjob() {
4470 _CRONTAB="crontab"
4471 if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
4472 _CRONTAB="fcrontab"
4473 fi
4474
4475 if ! _exists "$_CRONTAB"; then
4476 return
4477 fi
4478 _info "Removing cron job"
4479 cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
4480 if [ "$cr" ]; then
4481 if _exists uname && uname -a | grep solaris >/dev/null; then
4482 $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB --
4483 else
4484 $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -
4485 fi
4486 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
4487 _info LE_WORKING_DIR "$LE_WORKING_DIR"
4488 if _contains "$cr" "--config-home"; then
4489 LE_CONFIG_HOME="$(echo "$cr" | cut -d ' ' -f 11 | tr -d '"')"
4490 _debug LE_CONFIG_HOME "$LE_CONFIG_HOME"
4491 fi
4492 fi
4493 _initpath
4494
4495 }
4496
4497 revoke() {
4498 Le_Domain="$1"
4499 if [ -z "$Le_Domain" ]; then
4500 _usage "Usage: $PROJECT_ENTRY --revoke -d domain.com [--ecc]"
4501 return 1
4502 fi
4503
4504 _isEcc="$2"
4505
4506 _initpath "$Le_Domain" "$_isEcc"
4507 if [ ! -f "$DOMAIN_CONF" ]; then
4508 _err "$Le_Domain is not a issued domain, skip."
4509 return 1
4510 fi
4511
4512 if [ ! -f "$CERT_PATH" ]; then
4513 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
4514 return 1
4515 fi
4516
4517 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
4518
4519 if [ -z "$cert" ]; then
4520 _err "Cert for $Le_Domain is empty found, skip."
4521 return 1
4522 fi
4523
4524 _initAPI
4525
4526 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
4527 uri="${ACME_REVOKE_CERT}"
4528
4529 if [ -f "$CERT_KEY_PATH" ]; then
4530 _info "Try domain key first."
4531 if _send_signed_request "$uri" "$data" "" "$CERT_KEY_PATH"; then
4532 if [ -z "$response" ]; then
4533 _info "Revoke success."
4534 rm -f "$CERT_PATH"
4535 return 0
4536 else
4537 _err "Revoke error by domain key."
4538 _err "$response"
4539 fi
4540 fi
4541 else
4542 _info "Domain key file doesn't exists."
4543 fi
4544
4545 _info "Try account key."
4546
4547 if _send_signed_request "$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then
4548 if [ -z "$response" ]; then
4549 _info "Revoke success."
4550 rm -f "$CERT_PATH"
4551 return 0
4552 else
4553 _err "Revoke error."
4554 _debug "$response"
4555 fi
4556 fi
4557 return 1
4558 }
4559
4560 #domain ecc
4561 remove() {
4562 Le_Domain="$1"
4563 if [ -z "$Le_Domain" ]; then
4564 _usage "Usage: $PROJECT_ENTRY --remove -d domain.com [--ecc]"
4565 return 1
4566 fi
4567
4568 _isEcc="$2"
4569
4570 _initpath "$Le_Domain" "$_isEcc"
4571 _removed_conf="$DOMAIN_CONF.removed"
4572 if [ ! -f "$DOMAIN_CONF" ]; then
4573 if [ -f "$_removed_conf" ]; then
4574 _err "$Le_Domain is already removed, You can remove the folder by yourself: $DOMAIN_PATH"
4575 else
4576 _err "$Le_Domain is not a issued domain, skip."
4577 fi
4578 return 1
4579 fi
4580
4581 if mv "$DOMAIN_CONF" "$_removed_conf"; then
4582 _info "$Le_Domain is removed, the key and cert files are in $(__green $DOMAIN_PATH)"
4583 _info "You can remove them by yourself."
4584 return 0
4585 else
4586 _err "Remove $Le_Domain failed."
4587 return 1
4588 fi
4589 }
4590
4591 #domain vtype
4592 _deactivate() {
4593 _d_domain="$1"
4594 _d_type="$2"
4595 _initpath
4596
4597 if ! __get_domain_new_authz "$_d_domain"; then
4598 _err "Can not get domain new authz token."
4599 return 1
4600 fi
4601
4602 authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
4603 _debug "authzUri" "$authzUri"
4604
4605 if [ "$code" ] && [ ! "$code" = '201' ]; then
4606 _err "new-authz error: $response"
4607 return 1
4608 fi
4609
4610 entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')"
4611 if [ -z "$entries" ]; then
4612 _info "No valid entries found."
4613 if [ -z "$thumbprint" ]; then
4614 thumbprint="$(__calc_account_thumbprint)"
4615 fi
4616 _debug "Trigger validation."
4617 vtype="$VTYPE_HTTP"
4618 entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
4619 _debug entry "$entry"
4620 if [ -z "$entry" ]; then
4621 _err "Error, can not get domain token $d"
4622 return 1
4623 fi
4624 token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
4625 _debug token "$token"
4626
4627 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
4628 _debug uri "$uri"
4629
4630 keyauthorization="$token.$thumbprint"
4631 _debug keyauthorization "$keyauthorization"
4632 __trigger_validation "$uri" "$keyauthorization"
4633
4634 fi
4635
4636 _d_i=0
4637 _d_max_retry=$(echo "$entries" | wc -l)
4638 while [ "$_d_i" -lt "$_d_max_retry" ]; do
4639 _info "Deactivate: $_d_domain"
4640 _d_i="$(_math $_d_i + 1)"
4641 entry="$(echo "$entries" | sed -n "${_d_i}p")"
4642 _debug entry "$entry"
4643
4644 if [ -z "$entry" ]; then
4645 _info "No more valid entry found."
4646 break
4647 fi
4648
4649 _vtype="$(printf "%s\n" "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')"
4650 _debug _vtype "$_vtype"
4651 _info "Found $_vtype"
4652
4653 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
4654 _debug uri "$uri"
4655
4656 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
4657 _info "Skip $_vtype"
4658 continue
4659 fi
4660
4661 _info "Deactivate: $_vtype"
4662
4663 if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
4664 _info "Deactivate: $_vtype success."
4665 else
4666 _err "Can not deactivate $_vtype."
4667 break
4668 fi
4669
4670 done
4671 _debug "$_d_i"
4672 if [ "$_d_i" -eq "$_d_max_retry" ]; then
4673 _info "Deactivated success!"
4674 else
4675 _err "Deactivate failed."
4676 fi
4677
4678 }
4679
4680 deactivate() {
4681 _d_domain_list="$1"
4682 _d_type="$2"
4683 _initpath
4684 _initAPI
4685 _debug _d_domain_list "$_d_domain_list"
4686 if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then
4687 _usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
4688 return 1
4689 fi
4690 for _d_dm in $(echo "$_d_domain_list" | tr ',' ' '); do
4691 if [ -z "$_d_dm" ] || [ "$_d_dm" = "$NO_VALUE" ]; then
4692 continue
4693 fi
4694 if ! _deactivate "$_d_dm" "$_d_type"; then
4695 return 1
4696 fi
4697 done
4698 }
4699
4700 # Detect profile file if not specified as environment variable
4701 _detect_profile() {
4702 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
4703 echo "$PROFILE"
4704 return
4705 fi
4706
4707 DETECTED_PROFILE=''
4708 SHELLTYPE="$(basename "/$SHELL")"
4709
4710 if [ "$SHELLTYPE" = "bash" ]; then
4711 if [ -f "$HOME/.bashrc" ]; then
4712 DETECTED_PROFILE="$HOME/.bashrc"
4713 elif [ -f "$HOME/.bash_profile" ]; then
4714 DETECTED_PROFILE="$HOME/.bash_profile"
4715 fi
4716 elif [ "$SHELLTYPE" = "zsh" ]; then
4717 DETECTED_PROFILE="$HOME/.zshrc"
4718 fi
4719
4720 if [ -z "$DETECTED_PROFILE" ]; then
4721 if [ -f "$HOME/.profile" ]; then
4722 DETECTED_PROFILE="$HOME/.profile"
4723 elif [ -f "$HOME/.bashrc" ]; then
4724 DETECTED_PROFILE="$HOME/.bashrc"
4725 elif [ -f "$HOME/.bash_profile" ]; then
4726 DETECTED_PROFILE="$HOME/.bash_profile"
4727 elif [ -f "$HOME/.zshrc" ]; then
4728 DETECTED_PROFILE="$HOME/.zshrc"
4729 fi
4730 fi
4731
4732 echo "$DETECTED_PROFILE"
4733 }
4734
4735 _initconf() {
4736 _initpath
4737 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
4738 echo "
4739
4740 #LOG_FILE=\"$DEFAULT_LOG_FILE\"
4741 #LOG_LEVEL=1
4742
4743 #AUTO_UPGRADE=\"1\"
4744
4745 #NO_TIMESTAMP=1
4746
4747 " >"$ACCOUNT_CONF_PATH"
4748 fi
4749 }
4750
4751 # nocron
4752 _precheck() {
4753 _nocron="$1"
4754
4755 if ! _exists "curl" && ! _exists "wget"; then
4756 _err "Please install curl or wget first, we need to access http resources."
4757 return 1
4758 fi
4759
4760 if [ -z "$_nocron" ]; then
4761 if ! _exists "crontab" && ! _exists "fcrontab"; then
4762 _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
4763 _err "We need to set cron job to renew the certs automatically."
4764 _err "Otherwise, your certs will not be able to be renewed automatically."
4765 if [ -z "$FORCE" ]; then
4766 _err "Please add '--force' and try install again to go without crontab."
4767 _err "./$PROJECT_ENTRY --install --force"
4768 return 1
4769 fi
4770 fi
4771 fi
4772
4773 if ! _exists "${ACME_OPENSSL_BIN:-openssl}"; then
4774 _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
4775 _err "We need openssl to generate keys."
4776 return 1
4777 fi
4778
4779 if ! _exists "nc"; then
4780 _err "It is recommended to install nc first, try to install 'nc' or 'netcat'."
4781 _err "We use nc for standalone server if you use standalone mode."
4782 _err "If you don't use standalone mode, just ignore this warning."
4783 fi
4784
4785 return 0
4786 }
4787
4788 _setShebang() {
4789 _file="$1"
4790 _shebang="$2"
4791 if [ -z "$_shebang" ]; then
4792 _usage "Usage: file shebang"
4793 return 1
4794 fi
4795 cp "$_file" "$_file.tmp"
4796 echo "$_shebang" >"$_file"
4797 sed -n 2,99999p "$_file.tmp" >>"$_file"
4798 rm -f "$_file.tmp"
4799 }
4800
4801 #confighome
4802 _installalias() {
4803 _c_home="$1"
4804 _initpath
4805
4806 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
4807 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ]; then
4808 echo "$(cat "$_envfile")" | sed "s|^LE_WORKING_DIR.*$||" >"$_envfile"
4809 echo "$(cat "$_envfile")" | sed "s|^alias le.*$||" >"$_envfile"
4810 echo "$(cat "$_envfile")" | sed "s|^alias le.sh.*$||" >"$_envfile"
4811 fi
4812
4813 if [ "$_c_home" ]; then
4814 _c_entry=" --config-home '$_c_home'"
4815 fi
4816
4817 _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
4818 if [ "$_c_home" ]; then
4819 _setopt "$_envfile" "export LE_CONFIG_HOME" "=" "\"$LE_CONFIG_HOME\""
4820 else
4821 _sed_i "/^export LE_CONFIG_HOME/d" "$_envfile"
4822 fi
4823 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
4824
4825 _profile="$(_detect_profile)"
4826 if [ "$_profile" ]; then
4827 _debug "Found profile: $_profile"
4828 _info "Installing alias to '$_profile'"
4829 _setopt "$_profile" ". \"$_envfile\""
4830 _info "OK, Close and reopen your terminal to start using $PROJECT_NAME"
4831 else
4832 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
4833 fi
4834
4835 #for csh
4836 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
4837 _csh_profile="$HOME/.cshrc"
4838 if [ -f "$_csh_profile" ]; then
4839 _info "Installing alias to '$_csh_profile'"
4840 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
4841 if [ "$_c_home" ]; then
4842 _setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
4843 else
4844 _sed_i "/^setenv LE_CONFIG_HOME/d" "$_cshfile"
4845 fi
4846 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
4847 _setopt "$_csh_profile" "source \"$_cshfile\""
4848 fi
4849
4850 #for tcsh
4851 _tcsh_profile="$HOME/.tcshrc"
4852 if [ -f "$_tcsh_profile" ]; then
4853 _info "Installing alias to '$_tcsh_profile'"
4854 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
4855 if [ "$_c_home" ]; then
4856 _setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
4857 fi
4858 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
4859 _setopt "$_tcsh_profile" "source \"$_cshfile\""
4860 fi
4861
4862 }
4863
4864 # nocron confighome
4865 install() {
4866
4867 if [ -z "$LE_WORKING_DIR" ]; then
4868 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
4869 fi
4870
4871 _nocron="$1"
4872 _c_home="$2"
4873 if ! _initpath; then
4874 _err "Install failed."
4875 return 1
4876 fi
4877 if [ "$_nocron" ]; then
4878 _debug "Skip install cron job"
4879 fi
4880
4881 if ! _precheck "$_nocron"; then
4882 _err "Pre-check failed, can not install."
4883 return 1
4884 fi
4885
4886 if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
4887 _info "Using config home: $LE_CONFIG_HOME"
4888 _c_home="$LE_CONFIG_HOME"
4889 fi
4890
4891 #convert from le
4892 if [ -d "$HOME/.le" ]; then
4893 for envfile in "le.env" "le.sh.env"; do
4894 if [ -f "$HOME/.le/$envfile" ]; then
4895 if grep "le.sh" "$HOME/.le/$envfile" >/dev/null; then
4896 _upgrading="1"
4897 _info "You are upgrading from le.sh"
4898 _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
4899 mv "$HOME/.le" "$LE_WORKING_DIR"
4900 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
4901 break
4902 fi
4903 fi
4904 done
4905 fi
4906
4907 _info "Installing to $LE_WORKING_DIR"
4908
4909 if [ ! -d "$LE_WORKING_DIR" ]; then
4910 if ! mkdir -p "$LE_WORKING_DIR"; then
4911 _err "Can not create working dir: $LE_WORKING_DIR"
4912 return 1
4913 fi
4914
4915 chmod 700 "$LE_WORKING_DIR"
4916 fi
4917
4918 if [ ! -d "$LE_CONFIG_HOME" ]; then
4919 if ! mkdir -p "$LE_CONFIG_HOME"; then
4920 _err "Can not create config dir: $LE_CONFIG_HOME"
4921 return 1
4922 fi
4923
4924 chmod 700 "$LE_CONFIG_HOME"
4925 fi
4926
4927 cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
4928
4929 if [ "$?" != "0" ]; then
4930 _err "Install failed, can not copy $PROJECT_ENTRY"
4931 return 1
4932 fi
4933
4934 _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
4935
4936 _installalias "$_c_home"
4937
4938 for subf in $_SUB_FOLDERS; do
4939 if [ -d "$subf" ]; then
4940 mkdir -p "$LE_WORKING_DIR/$subf"
4941 cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
4942 fi
4943 done
4944
4945 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
4946 _initconf
4947 fi
4948
4949 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ]; then
4950 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
4951 fi
4952
4953 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]; then
4954 _saveaccountconf "CERT_HOME" "$CERT_HOME"
4955 fi
4956
4957 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]; then
4958 _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
4959 fi
4960
4961 if [ -z "$_nocron" ]; then
4962 installcronjob "$_c_home"
4963 fi
4964
4965 if [ -z "$NO_DETECT_SH" ]; then
4966 #Modify shebang
4967 if _exists bash; then
4968 _info "Good, bash is found, so change the shebang to use bash as preferred."
4969 _shebang='#!'"$(env bash -c "command -v bash")"
4970 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
4971 for subf in $_SUB_FOLDERS; do
4972 if [ -d "$LE_WORKING_DIR/$subf" ]; then
4973 for _apifile in "$LE_WORKING_DIR/$subf/"*.sh; do
4974 _setShebang "$_apifile" "$_shebang"
4975 done
4976 fi
4977 done
4978 fi
4979 fi
4980
4981 _info OK
4982 }
4983
4984 # nocron
4985 uninstall() {
4986 _nocron="$1"
4987 if [ -z "$_nocron" ]; then
4988 uninstallcronjob
4989 fi
4990 _initpath
4991
4992 _uninstallalias
4993
4994 rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
4995 _info "The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\", you can remove them by yourself."
4996
4997 }
4998
4999 _uninstallalias() {
5000 _initpath
5001
5002 _profile="$(_detect_profile)"
5003 if [ "$_profile" ]; then
5004 _info "Uninstalling alias from: '$_profile'"
5005 text="$(cat "$_profile")"
5006 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" >"$_profile"
5007 fi
5008
5009 _csh_profile="$HOME/.cshrc"
5010 if [ -f "$_csh_profile" ]; then
5011 _info "Uninstalling alias from: '$_csh_profile'"
5012 text="$(cat "$_csh_profile")"
5013 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_csh_profile"
5014 fi
5015
5016 _tcsh_profile="$HOME/.tcshrc"
5017 if [ -f "$_tcsh_profile" ]; then
5018 _info "Uninstalling alias from: '$_csh_profile'"
5019 text="$(cat "$_tcsh_profile")"
5020 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_tcsh_profile"
5021 fi
5022
5023 }
5024
5025 cron() {
5026 IN_CRON=1
5027 _initpath
5028 _info "$(__green "===Starting cron===")"
5029 if [ "$AUTO_UPGRADE" = "1" ]; then
5030 export LE_WORKING_DIR
5031 (
5032 if ! upgrade; then
5033 _err "Cron:Upgrade failed!"
5034 return 1
5035 fi
5036 )
5037 . "$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev/null
5038
5039 if [ -t 1 ]; then
5040 __INTERACTIVE="1"
5041 fi
5042
5043 _info "Auto upgraded to: $VER"
5044 fi
5045 renewAll
5046 _ret="$?"
5047 IN_CRON=""
5048 _info "$(__green "===End cron===")"
5049 exit $_ret
5050 }
5051
5052 version() {
5053 echo "$PROJECT"
5054 echo "v$VER"
5055 }
5056
5057 showhelp() {
5058 _initpath
5059 version
5060 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
5061 Commands:
5062 --help, -h Show this help message.
5063 --version, -v Show version info.
5064 --install Install $PROJECT_NAME to your system.
5065 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
5066 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
5067 --issue Issue a cert.
5068 --signcsr Issue a cert from an existing csr.
5069 --deploy Deploy the cert to your server.
5070 --install-cert Install the issued cert to apache/nginx or any other server.
5071 --renew, -r Renew a cert.
5072 --renew-all Renew all the certs.
5073 --revoke Revoke a cert.
5074 --remove Remove the cert from $PROJECT
5075 --list List all the certs.
5076 --showcsr Show the content of a csr.
5077 --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
5078 --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
5079 --cron Run cron job to renew all the certs.
5080 --toPkcs Export the certificate and key to a pfx file.
5081 --toPkcs8 Convert to pkcs8 format.
5082 --update-account Update account info.
5083 --register-account Register account key.
5084 --deactivate-account Deactivate the account.
5085 --create-account-key Create an account private key, professional use.
5086 --create-domain-key Create an domain private key, professional use.
5087 --createCSR, -ccsr Create CSR , professional use.
5088 --deactivate Deactivate the domain authz, professional use.
5089
5090 Parameters:
5091 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
5092 --force, -f Used to force to install or force to renew a cert immediately.
5093 --staging, --test Use staging server, just for test.
5094 --debug Output debug info.
5095 --output-insecure Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure.
5096 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
5097 --standalone Use standalone mode.
5098 --stateless Use stateless mode, see: $_STATELESS_WIKI
5099 --tls Use standalone tls mode.
5100 --apache Use apache mode.
5101 --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
5102 --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
5103
5104 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
5105 --accountkeylength, -ak [2048] Specifies the account key length.
5106 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
5107 --log-level 1|2 Specifies the log level, default is 1.
5108 --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
5109
5110 These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
5111
5112 --cert-file After issue/renew, the cert will be copied to this path.
5113 --key-file After issue/renew, the key will be copied to this path.
5114 --ca-file After issue/renew, the intermediate cert will be copied to this path.
5115 --fullchain-file After issue/renew, the fullchain cert will be copied to this path.
5116
5117 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
5118
5119 --server SERVER ACME Directory Resource URI. (default: https://acme-v01.api.letsencrypt.org/directory)
5120 --accountconf Specifies a customized account config file.
5121 --home Specifies the home dir for $PROJECT_NAME .
5122 --cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
5123 --config-home Specifies the home dir to save all the configurations.
5124 --useragent Specifies the user agent string. it will be saved for future use too.
5125 --accountemail Specifies the account email for registering, Only valid for the '--install' command.
5126 --accountkey Specifies the account key path, Only valid for the '--install' command.
5127 --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
5128 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
5129 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
5130 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
5131 --listraw Only used for '--list' command, list the certs in raw format.
5132 --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal.
5133 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
5134 --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate.
5135 --ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl.
5136 --nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
5137 --no-color Do not output color text.
5138 --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
5139 --csr Specifies the input csr.
5140 --pre-hook Command to be run before obtaining any certificates.
5141 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed.
5142 --renew-hook Command to be run once for each successfully renewed certificate.
5143 --deploy-hook The hook file to deploy cert
5144 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
5145 --always-force-new-domain-key Generate new domain key when renewal. Otherwise, the domain key is not changed by default.
5146 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
5147 --listen-v4 Force standalone/tls server to listen at ipv4.
5148 --listen-v6 Force standalone/tls server to listen at ipv6.
5149 --openssl-bin Specifies a custom openssl bin location.
5150 --use-wget Force to use wget, if you have both curl and wget installed.
5151 "
5152 }
5153
5154 # nocron
5155 _installOnline() {
5156 _info "Installing from online archive."
5157 _nocron="$1"
5158 if [ ! "$BRANCH" ]; then
5159 BRANCH="master"
5160 fi
5161
5162 target="$PROJECT/archive/$BRANCH.tar.gz"
5163 _info "Downloading $target"
5164 localname="$BRANCH.tar.gz"
5165 if ! _get "$target" >$localname; then
5166 _err "Download error."
5167 return 1
5168 fi
5169 (
5170 _info "Extracting $localname"
5171 if ! (tar xzf $localname || gtar xzf $localname); then
5172 _err "Extraction error."
5173 exit 1
5174 fi
5175
5176 cd "$PROJECT_NAME-$BRANCH"
5177 chmod +x $PROJECT_ENTRY
5178 if ./$PROJECT_ENTRY install "$_nocron"; then
5179 _info "Install success!"
5180 fi
5181
5182 cd ..
5183
5184 rm -rf "$PROJECT_NAME-$BRANCH"
5185 rm -f "$localname"
5186 )
5187 }
5188
5189 upgrade() {
5190 if (
5191 _initpath
5192 export LE_WORKING_DIR
5193 cd "$LE_WORKING_DIR"
5194 _installOnline "nocron"
5195 ); then
5196 _info "Upgrade success!"
5197 exit 0
5198 else
5199 _err "Upgrade failed!"
5200 exit 1
5201 fi
5202 }
5203
5204 _processAccountConf() {
5205 if [ "$_useragent" ]; then
5206 _saveaccountconf "USER_AGENT" "$_useragent"
5207 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ]; then
5208 _saveaccountconf "USER_AGENT" "$USER_AGENT"
5209 fi
5210
5211 if [ "$_accountemail" ]; then
5212 _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
5213 elif [ "$ACCOUNT_EMAIL" ] && [ "$ACCOUNT_EMAIL" != "$DEFAULT_ACCOUNT_EMAIL" ]; then
5214 _saveaccountconf "ACCOUNT_EMAIL" "$ACCOUNT_EMAIL"
5215 fi
5216
5217 if [ "$_openssl_bin" ]; then
5218 _saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin"
5219 elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
5220 _saveaccountconf "ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
5221 fi
5222
5223 if [ "$_auto_upgrade" ]; then
5224 _saveaccountconf "AUTO_UPGRADE" "$_auto_upgrade"
5225 elif [ "$AUTO_UPGRADE" ]; then
5226 _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
5227 fi
5228
5229 if [ "$_use_wget" ]; then
5230 _saveaccountconf "ACME_USE_WGET" "$_use_wget"
5231 elif [ "$ACME_USE_WGET" ]; then
5232 _saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET"
5233 fi
5234
5235 }
5236
5237 _process() {
5238 _CMD=""
5239 _domain=""
5240 _altdomains="$NO_VALUE"
5241 _webroot=""
5242 _keylength=""
5243 _accountkeylength=""
5244 _cert_file=""
5245 _key_file=""
5246 _ca_file=""
5247 _fullchain_file=""
5248 _reloadcmd=""
5249 _password=""
5250 _accountconf=""
5251 _useragent=""
5252 _accountemail=""
5253 _accountkey=""
5254 _certhome=""
5255 _confighome=""
5256 _httpport=""
5257 _tlsport=""
5258 _dnssleep=""
5259 _listraw=""
5260 _stopRenewOnError=""
5261 #_insecure=""
5262 _ca_bundle=""
5263 _ca_path=""
5264 _nocron=""
5265 _ecc=""
5266 _csr=""
5267 _pre_hook=""
5268 _post_hook=""
5269 _renew_hook=""
5270 _deploy_hook=""
5271 _logfile=""
5272 _log=""
5273 _local_address=""
5274 _log_level=""
5275 _auto_upgrade=""
5276 _listen_v4=""
5277 _listen_v6=""
5278 _openssl_bin=""
5279 _syslog=""
5280 _use_wget=""
5281 _server=""
5282 while [ ${#} -gt 0 ]; do
5283 case "${1}" in
5284
5285 --help | -h)
5286 showhelp
5287 return
5288 ;;
5289 --version | -v)
5290 version
5291 return
5292 ;;
5293 --install)
5294 _CMD="install"
5295 ;;
5296 --uninstall)
5297 _CMD="uninstall"
5298 ;;
5299 --upgrade)
5300 _CMD="upgrade"
5301 ;;
5302 --issue)
5303 _CMD="issue"
5304 ;;
5305 --deploy)
5306 _CMD="deploy"
5307 ;;
5308 --signcsr)
5309 _CMD="signcsr"
5310 ;;
5311 --showcsr)
5312 _CMD="showcsr"
5313 ;;
5314 --installcert | -i | --install-cert)
5315 _CMD="installcert"
5316 ;;
5317 --renew | -r)
5318 _CMD="renew"
5319 ;;
5320 --renewAll | --renewall | --renew-all)
5321 _CMD="renewAll"
5322 ;;
5323 --revoke)
5324 _CMD="revoke"
5325 ;;
5326 --remove)
5327 _CMD="remove"
5328 ;;
5329 --list)
5330 _CMD="list"
5331 ;;
5332 --installcronjob | --install-cronjob)
5333 _CMD="installcronjob"
5334 ;;
5335 --uninstallcronjob | --uninstall-cronjob)
5336 _CMD="uninstallcronjob"
5337 ;;
5338 --cron)
5339 _CMD="cron"
5340 ;;
5341 --toPkcs)
5342 _CMD="toPkcs"
5343 ;;
5344 --toPkcs8)
5345 _CMD="toPkcs8"
5346 ;;
5347 --createAccountKey | --createaccountkey | -cak | --create-account-key)
5348 _CMD="createAccountKey"
5349 ;;
5350 --createDomainKey | --createdomainkey | -cdk | --create-domain-key)
5351 _CMD="createDomainKey"
5352 ;;
5353 --createCSR | --createcsr | -ccr)
5354 _CMD="createCSR"
5355 ;;
5356 --deactivate)
5357 _CMD="deactivate"
5358 ;;
5359 --updateaccount | --update-account)
5360 _CMD="updateaccount"
5361 ;;
5362 --registeraccount | --register-account)
5363 _CMD="registeraccount"
5364 ;;
5365 --deactivate-account)
5366 _CMD="deactivateaccount"
5367 ;;
5368 --domain | -d)
5369 _dvalue="$2"
5370
5371 if [ "$_dvalue" ]; then
5372 if _startswith "$_dvalue" "-"; then
5373 _err "'$_dvalue' is not a valid domain for parameter '$1'"
5374 return 1
5375 fi
5376 if _is_idn "$_dvalue" && ! _exists idn; then
5377 _err "It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first."
5378 return 1
5379 fi
5380
5381 if [ -z "$_domain" ]; then
5382 _domain="$_dvalue"
5383 else
5384 if [ "$_altdomains" = "$NO_VALUE" ]; then
5385 _altdomains="$_dvalue"
5386 else
5387 _altdomains="$_altdomains,$_dvalue"
5388 fi
5389 fi
5390 fi
5391
5392 shift
5393 ;;
5394
5395 --force | -f)
5396 FORCE="1"
5397 ;;
5398 --staging | --test)
5399 STAGE="1"
5400 ;;
5401 --server)
5402 ACME_DIRECTORY="$2"
5403 _server="$ACME_DIRECTORY"
5404 export ACME_DIRECTORY
5405 shift
5406 ;;
5407 --debug)
5408 if [ -z "$2" ] || _startswith "$2" "-"; then
5409 DEBUG="$DEBUG_LEVEL_DEFAULT"
5410 else
5411 DEBUG="$2"
5412 shift
5413 fi
5414 ;;
5415 --output-insecure)
5416 export OUTPUT_INSECURE=1
5417 ;;
5418 --webroot | -w)
5419 wvalue="$2"
5420 if [ -z "$_webroot" ]; then
5421 _webroot="$wvalue"
5422 else
5423 _webroot="$_webroot,$wvalue"
5424 fi
5425 shift
5426 ;;
5427 --standalone)
5428 wvalue="$NO_VALUE"
5429 if [ -z "$_webroot" ]; then
5430 _webroot="$wvalue"
5431 else
5432 _webroot="$_webroot,$wvalue"
5433 fi
5434 ;;
5435 --stateless)
5436 wvalue="$MODE_STATELESS"
5437 if [ -z "$_webroot" ]; then
5438 _webroot="$wvalue"
5439 else
5440 _webroot="$_webroot,$wvalue"
5441 fi
5442 ;;
5443 --local-address)
5444 lvalue="$2"
5445 _local_address="$_local_address$lvalue,"
5446 shift
5447 ;;
5448 --apache)
5449 wvalue="apache"
5450 if [ -z "$_webroot" ]; then
5451 _webroot="$wvalue"
5452 else
5453 _webroot="$_webroot,$wvalue"
5454 fi
5455 ;;
5456 --nginx)
5457 wvalue="$NGINX"
5458 if [ -z "$_webroot" ]; then
5459 _webroot="$wvalue"
5460 else
5461 _webroot="$_webroot,$wvalue"
5462 fi
5463 ;;
5464 --tls)
5465 wvalue="$W_TLS"
5466 if [ -z "$_webroot" ]; then
5467 _webroot="$wvalue"
5468 else
5469 _webroot="$_webroot,$wvalue"
5470 fi
5471 ;;
5472 --dns)
5473 wvalue="dns"
5474 if [ "$2" ] && ! _startswith "$2" "-"; then
5475 wvalue="$2"
5476 shift
5477 fi
5478 if [ -z "$_webroot" ]; then
5479 _webroot="$wvalue"
5480 else
5481 _webroot="$_webroot,$wvalue"
5482 fi
5483 ;;
5484 --dnssleep)
5485 _dnssleep="$2"
5486 Le_DNSSleep="$_dnssleep"
5487 shift
5488 ;;
5489
5490 --keylength | -k)
5491 _keylength="$2"
5492 shift
5493 ;;
5494 --accountkeylength | -ak)
5495 _accountkeylength="$2"
5496 shift
5497 ;;
5498
5499 --cert-file | --certpath)
5500 _cert_file="$2"
5501 shift
5502 ;;
5503 --key-file | --keypath)
5504 _key_file="$2"
5505 shift
5506 ;;
5507 --ca-file | --capath)
5508 _ca_file="$2"
5509 shift
5510 ;;
5511 --fullchain-file | --fullchainpath)
5512 _fullchain_file="$2"
5513 shift
5514 ;;
5515 --reloadcmd | --reloadCmd)
5516 _reloadcmd="$2"
5517 shift
5518 ;;
5519 --password)
5520 _password="$2"
5521 shift
5522 ;;
5523 --accountconf)
5524 _accountconf="$2"
5525 ACCOUNT_CONF_PATH="$_accountconf"
5526 shift
5527 ;;
5528 --home)
5529 LE_WORKING_DIR="$2"
5530 shift
5531 ;;
5532 --certhome | --cert-home)
5533 _certhome="$2"
5534 CERT_HOME="$_certhome"
5535 shift
5536 ;;
5537 --config-home)
5538 _confighome="$2"
5539 LE_CONFIG_HOME="$_confighome"
5540 shift
5541 ;;
5542 --useragent)
5543 _useragent="$2"
5544 USER_AGENT="$_useragent"
5545 shift
5546 ;;
5547 --accountemail)
5548 _accountemail="$2"
5549 ACCOUNT_EMAIL="$_accountemail"
5550 shift
5551 ;;
5552 --accountkey)
5553 _accountkey="$2"
5554 ACCOUNT_KEY_PATH="$_accountkey"
5555 shift
5556 ;;
5557 --days)
5558 _days="$2"
5559 Le_RenewalDays="$_days"
5560 shift
5561 ;;
5562 --httpport)
5563 _httpport="$2"
5564 Le_HTTPPort="$_httpport"
5565 shift
5566 ;;
5567 --tlsport)
5568 _tlsport="$2"
5569 Le_TLSPort="$_tlsport"
5570 shift
5571 ;;
5572
5573 --listraw)
5574 _listraw="raw"
5575 ;;
5576 --stopRenewOnError | --stoprenewonerror | -se)
5577 _stopRenewOnError="1"
5578 ;;
5579 --insecure)
5580 #_insecure="1"
5581 HTTPS_INSECURE="1"
5582 ;;
5583 --ca-bundle)
5584 _ca_bundle="$(_readlink -f "$2")"
5585 CA_BUNDLE="$_ca_bundle"
5586 shift
5587 ;;
5588 --ca-path)
5589 _ca_path="$2"
5590 CA_PATH="$_ca_path"
5591 shift
5592 ;;
5593 --nocron)
5594 _nocron="1"
5595 ;;
5596 --no-color)
5597 export ACME_NO_COLOR=1
5598 ;;
5599 --ecc)
5600 _ecc="isEcc"
5601 ;;
5602 --csr)
5603 _csr="$2"
5604 shift
5605 ;;
5606 --pre-hook)
5607 _pre_hook="$2"
5608 shift
5609 ;;
5610 --post-hook)
5611 _post_hook="$2"
5612 shift
5613 ;;
5614 --renew-hook)
5615 _renew_hook="$2"
5616 shift
5617 ;;
5618 --deploy-hook)
5619 if [ -z "$2" ] || _startswith "$2" "-"; then
5620 _usage "Please specify a value for '--deploy-hook'"
5621 return 1
5622 fi
5623 _deploy_hook="$_deploy_hook$2,"
5624 shift
5625 ;;
5626 --ocsp-must-staple | --ocsp)
5627 Le_OCSP_Staple="1"
5628 ;;
5629 --always-force-new-domain-key)
5630 if [ -z "$2" ] || _startswith "$2" "-"; then
5631 Le_ForceNewDomainKey=1
5632 else
5633 Le_ForceNewDomainKey="$2"
5634 shift
5635 fi
5636 ;;
5637 --log | --logfile)
5638 _log="1"
5639 _logfile="$2"
5640 if _startswith "$_logfile" '-'; then
5641 _logfile=""
5642 else
5643 shift
5644 fi
5645 LOG_FILE="$_logfile"
5646 if [ -z "$LOG_LEVEL" ]; then
5647 LOG_LEVEL="$DEFAULT_LOG_LEVEL"
5648 fi
5649 ;;
5650 --log-level)
5651 _log_level="$2"
5652 LOG_LEVEL="$_log_level"
5653 shift
5654 ;;
5655 --syslog)
5656 if ! _startswith "$2" '-'; then
5657 _syslog="$2"
5658 shift
5659 fi
5660 if [ -z "$_syslog" ]; then
5661 _syslog="$SYSLOG_LEVEL_DEFAULT"
5662 fi
5663 ;;
5664 --auto-upgrade)
5665 _auto_upgrade="$2"
5666 if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-'; then
5667 _auto_upgrade="1"
5668 else
5669 shift
5670 fi
5671 AUTO_UPGRADE="$_auto_upgrade"
5672 ;;
5673 --listen-v4)
5674 _listen_v4="1"
5675 Le_Listen_V4="$_listen_v4"
5676 ;;
5677 --listen-v6)
5678 _listen_v6="1"
5679 Le_Listen_V6="$_listen_v6"
5680 ;;
5681 --openssl-bin)
5682 _openssl_bin="$2"
5683 ACME_OPENSSL_BIN="$_openssl_bin"
5684 shift
5685 ;;
5686 --use-wget)
5687 _use_wget="1"
5688 ACME_USE_WGET="1"
5689 ;;
5690 *)
5691 _err "Unknown parameter : $1"
5692 return 1
5693 ;;
5694 esac
5695
5696 shift 1
5697 done
5698
5699 if [ "${_CMD}" != "install" ]; then
5700 __initHome
5701 if [ "$_log" ]; then
5702 if [ -z "$_logfile" ]; then
5703 _logfile="$DEFAULT_LOG_FILE"
5704 fi
5705 fi
5706 if [ "$_logfile" ]; then
5707 _saveaccountconf "LOG_FILE" "$_logfile"
5708 LOG_FILE="$_logfile"
5709 fi
5710
5711 if [ "$_log_level" ]; then
5712 _saveaccountconf "LOG_LEVEL" "$_log_level"
5713 LOG_LEVEL="$_log_level"
5714 fi
5715
5716 if [ "$_syslog" ]; then
5717 if _exists logger; then
5718 if [ "$_syslog" = "0" ]; then
5719 _clearaccountconf "SYS_LOG"
5720 else
5721 _saveaccountconf "SYS_LOG" "$_syslog"
5722 fi
5723 SYS_LOG="$_syslog"
5724 else
5725 _err "The 'logger' command is not found, can not enable syslog."
5726 _clearaccountconf "SYS_LOG"
5727 SYS_LOG=""
5728 fi
5729 fi
5730
5731 _processAccountConf
5732 fi
5733
5734 _debug2 LE_WORKING_DIR "$LE_WORKING_DIR"
5735
5736 if [ "$DEBUG" ]; then
5737 version
5738 if [ "$_server" ]; then
5739 _debug "Using server: $_server"
5740 fi
5741 fi
5742
5743 case "${_CMD}" in
5744 install) install "$_nocron" "$_confighome" ;;
5745 uninstall) uninstall "$_nocron" ;;
5746 upgrade) upgrade ;;
5747 issue)
5748 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address"
5749 ;;
5750 deploy)
5751 deploy "$_domain" "$_deploy_hook" "$_ecc"
5752 ;;
5753 signcsr)
5754 signcsr "$_csr" "$_webroot"
5755 ;;
5756 showcsr)
5757 showcsr "$_csr" "$_domain"
5758 ;;
5759 installcert)
5760 installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
5761 ;;
5762 renew)
5763 renew "$_domain" "$_ecc"
5764 ;;
5765 renewAll)
5766 renewAll "$_stopRenewOnError"
5767 ;;
5768 revoke)
5769 revoke "$_domain" "$_ecc"
5770 ;;
5771 remove)
5772 remove "$_domain" "$_ecc"
5773 ;;
5774 deactivate)
5775 deactivate "$_domain,$_altdomains"
5776 ;;
5777 registeraccount)
5778 registeraccount "$_accountkeylength"
5779 ;;
5780 updateaccount)
5781 updateaccount
5782 ;;
5783 deactivateaccount)
5784 deactivateaccount
5785 ;;
5786 list)
5787 list "$_listraw"
5788 ;;
5789 installcronjob) installcronjob "$_confighome" ;;
5790 uninstallcronjob) uninstallcronjob ;;
5791 cron) cron ;;
5792 toPkcs)
5793 toPkcs "$_domain" "$_password" "$_ecc"
5794 ;;
5795 toPkcs8)
5796 toPkcs8 "$_domain" "$_ecc"
5797 ;;
5798 createAccountKey)
5799 createAccountKey "$_accountkeylength"
5800 ;;
5801 createDomainKey)
5802 createDomainKey "$_domain" "$_keylength"
5803 ;;
5804 createCSR)
5805 createCSR "$_domain" "$_altdomains" "$_ecc"
5806 ;;
5807
5808 *)
5809 if [ "$_CMD" ]; then
5810 _err "Invalid command: $_CMD"
5811 fi
5812 showhelp
5813 return 1
5814 ;;
5815 esac
5816 _ret="$?"
5817 if [ "$_ret" != "0" ]; then
5818 return $_ret
5819 fi
5820
5821 if [ "${_CMD}" = "install" ]; then
5822 if [ "$_log" ]; then
5823 if [ -z "$LOG_FILE" ]; then
5824 LOG_FILE="$DEFAULT_LOG_FILE"
5825 fi
5826 _saveaccountconf "LOG_FILE" "$LOG_FILE"
5827 fi
5828
5829 if [ "$_log_level" ]; then
5830 _saveaccountconf "LOG_LEVEL" "$_log_level"
5831 fi
5832
5833 if [ "$_syslog" ]; then
5834 if _exists logger; then
5835 if [ "$_syslog" = "0" ]; then
5836 _clearaccountconf "SYS_LOG"
5837 else
5838 _saveaccountconf "SYS_LOG" "$_syslog"
5839 fi
5840 else
5841 _err "The 'logger' command is not found, can not enable syslog."
5842 _clearaccountconf "SYS_LOG"
5843 SYS_LOG=""
5844 fi
5845 fi
5846
5847 _processAccountConf
5848 fi
5849
5850 }
5851
5852 if [ "$INSTALLONLINE" ]; then
5853 INSTALLONLINE=""
5854 _installOnline
5855 exit
5856 fi
5857
5858 main() {
5859 [ -z "$1" ] && showhelp && return
5860 if _startswith "$1" '-'; then _process "$@"; else "$@"; fi
5861 }
5862
5863 main "$@"