]> git.proxmox.com Git - mirror_acme.sh.git/blob - acme.sh
rename JWK_HEADER
[mirror_acme.sh.git] / acme.sh
1 #!/usr/bin/env sh
2
3 VER=2.6.2
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 DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
17 DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
18
19 DEFAULT_USER_AGENT="$PROJECT_ENTRY client v$VER : $PROJECT"
20 DEFAULT_ACCOUNT_EMAIL=""
21
22 STAGE_CA="https://acme-staging.api.letsencrypt.org"
23
24 VTYPE_HTTP="http-01"
25 VTYPE_DNS="dns-01"
26 VTYPE_TLS="tls-sni-01"
27 VTYPE_TLS2="tls-sni-02"
28
29 LOCAL_ANY_ADDRESS="0.0.0.0"
30
31 MAX_RENEW=60
32
33 DEFAULT_DNS_SLEEP=120
34
35 NO_VALUE="no"
36
37 W_TLS="tls"
38
39 STATE_VERIFIED="verified_ok"
40
41 BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
42 END_CSR="-----END CERTIFICATE REQUEST-----"
43
44 BEGIN_CERT="-----BEGIN CERTIFICATE-----"
45 END_CERT="-----END CERTIFICATE-----"
46
47 RENEW_SKIP=2
48
49 ECC_SEP="_"
50 ECC_SUFFIX="${ECC_SEP}ecc"
51
52 LOG_LEVEL_1=1
53 LOG_LEVEL_2=2
54 LOG_LEVEL_3=3
55 DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
56
57 _DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh"
58
59 __INTERACTIVE=""
60 if [ -t 1 ] ; then
61 __INTERACTIVE="1"
62 fi
63
64 __green() {
65 if [ "$__INTERACTIVE" ] ; then
66 printf '\033[1;31;32m'
67 fi
68 printf -- "$1"
69 if [ "$__INTERACTIVE" ] ; then
70 printf '\033[0m'
71 fi
72 }
73
74 __red() {
75 if [ "$__INTERACTIVE" ] ; then
76 printf '\033[1;31;40m'
77 fi
78 printf -- "$1"
79 if [ "$__INTERACTIVE" ] ; then
80 printf '\033[0m'
81 fi
82 }
83
84
85 _printargs() {
86 if [ -z "$2" ] ; then
87 printf -- "[$(date)] $1"
88 else
89 printf -- "[$(date)] $1='$2'"
90 fi
91 printf "\n"
92 }
93
94
95 _log() {
96 [ -z "$LOG_FILE" ] && return
97 _printargs "$@" >> $LOG_FILE
98 }
99
100 _info() {
101 _log "$@"
102 _printargs "$@"
103 }
104
105
106 _err() {
107 _log "$@"
108 printf -- "[$(date)] " >&2
109 if [ -z "$2" ] ; then
110 __red "$1" >&2
111 else
112 __red "$1='$2'" >&2
113 fi
114 printf "\n" >&2
115 return 1
116 }
117
118 _usage() {
119 __red "$@" >&2
120 printf "\n" >&2
121 }
122
123
124 _debug() {
125 if [ -z "$LOG_LEVEL" ] || [ "$LOG_LEVEL" -ge "$LOG_LEVEL_1" ] ; then
126 _log "$@"
127 fi
128 if [ -z "$DEBUG" ] ; then
129 return
130 fi
131 _printargs "$@" >&2
132 }
133
134 _debug2() {
135 if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_2" ] ; then
136 _log "$@"
137 fi
138 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
139 _debug "$@"
140 fi
141 }
142
143 _debug3() {
144 if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_3" ] ; then
145 _log "$@"
146 fi
147 if [ "$DEBUG" ] && [ "$DEBUG" -ge "3" ] ; then
148 _debug "$@"
149 fi
150 }
151
152 _startswith(){
153 _str="$1"
154 _sub="$2"
155 echo "$_str" | grep "^$_sub" >/dev/null 2>&1
156 }
157
158 _endswith(){
159 _str="$1"
160 _sub="$2"
161 echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
162 }
163
164 _contains(){
165 _str="$1"
166 _sub="$2"
167 echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
168 }
169
170 _hasfield() {
171 _str="$1"
172 _field="$2"
173 _sep="$3"
174 if [ -z "$_field" ] ; then
175 _usage "Usage: str field [sep]"
176 return 1
177 fi
178
179 if [ -z "$_sep" ] ; then
180 _sep=","
181 fi
182
183 for f in $(echo "$_str" | tr ',' ' ') ; do
184 if [ "$f" = "$_field" ] ; then
185 _debug2 "'$_str' contains '$_field'"
186 return 0 #contains ok
187 fi
188 done
189 _debug2 "'$_str' does not contain '$_field'"
190 return 1 #not contains
191 }
192
193 _getfield(){
194 _str="$1"
195 _findex="$2"
196 _sep="$3"
197
198 if [ -z "$_findex" ] ; then
199 _usage "Usage: str field [sep]"
200 return 1
201 fi
202
203 if [ -z "$_sep" ] ; then
204 _sep=","
205 fi
206
207 _ffi=$_findex
208 while [ "$_ffi" -gt "0" ]
209 do
210 _fv="$(echo "$_str" | cut -d $_sep -f $_ffi)"
211 if [ "$_fv" ] ; then
212 printf -- "%s" "$_fv"
213 return 0
214 fi
215 _ffi="$(_math $_ffi - 1)"
216 done
217
218 printf -- "%s" "$_str"
219
220 }
221
222 _exists(){
223 cmd="$1"
224 if [ -z "$cmd" ] ; then
225 _usage "Usage: _exists cmd"
226 return 1
227 fi
228 if type command >/dev/null 2>&1 ; then
229 command -v "$cmd" >/dev/null 2>&1
230 else
231 type "$cmd" >/dev/null 2>&1
232 fi
233 ret="$?"
234 _debug3 "$cmd exists=$ret"
235 return $ret
236 }
237
238 #a + b
239 _math(){
240 expr "$@"
241 }
242
243 _h_char_2_dec() {
244 _ch=$1
245 case "${_ch}" in
246 a|A)
247 printf "10"
248 ;;
249 b|B)
250 printf "11"
251 ;;
252 c|C)
253 printf "12"
254 ;;
255 d|D)
256 printf "13"
257 ;;
258 e|E)
259 printf "14"
260 ;;
261 f|F)
262 printf "15"
263 ;;
264 *)
265 printf "%s" "$_ch"
266 ;;
267 esac
268
269 }
270
271
272 _URGLY_PRINTF=""
273 if [ "$(printf '\x41')" != 'A' ] ; then
274 _URGLY_PRINTF=1
275 fi
276
277 _h2b() {
278 hex=$(cat)
279 i=1
280 j=2
281 if _exists let ; then
282 uselet="1"
283 fi
284 _debug3 uselet "$uselet"
285 _debug3 _URGLY_PRINTF "$_URGLY_PRINTF"
286 while true ; do
287 if [ -z "$_URGLY_PRINTF" ] ; then
288 h="$(printf $hex | cut -c $i-$j)"
289 if [ -z "$h" ] ; then
290 break;
291 fi
292 printf "\x$h"
293 else
294 ic="$(printf $hex | cut -c $i)"
295 jc="$(printf $hex | cut -c $j)"
296 if [ -z "$ic$jc" ] ; then
297 break;
298 fi
299 ic="$(_h_char_2_dec "$ic")"
300 jc="$(_h_char_2_dec "$jc")"
301 printf '\'"$(printf %o "$(_math $ic \* 16 + $jc)")"
302 fi
303 if [ "$uselet" ] ; then
304 let "i+=2" >/dev/null
305 let "j+=2" >/dev/null
306 else
307 i="$(_math $i + 2)"
308 j="$(_math $j + 2)"
309 fi
310 done
311 }
312
313 #options file
314 _sed_i() {
315 options="$1"
316 filename="$2"
317 if [ -z "$filename" ] ; then
318 _usage "Usage:_sed_i options filename"
319 return 1
320 fi
321 _debug2 options "$options"
322 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
323 _debug "Using sed -i"
324 sed -i "$options" "$filename"
325 else
326 _debug "No -i support in sed"
327 text="$(cat "$filename")"
328 echo "$text" | sed "$options" > "$filename"
329 fi
330 }
331
332 _egrep_o() {
333 if _contains "$(egrep -o 2>&1)" "egrep: illegal option -- o" ; then
334 sed -n 's/.*\('"$1"'\).*/\1/p'
335 else
336 egrep -o "$1"
337 fi
338 }
339
340 #Usage: file startline endline
341 _getfile() {
342 filename="$1"
343 startline="$2"
344 endline="$3"
345 if [ -z "$endline" ] ; then
346 _usage "Usage: file startline endline"
347 return 1
348 fi
349
350 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
351 if [ -z "$i" ] ; then
352 _err "Can not find start line: $startline"
353 return 1
354 fi
355 i="$(_math "$i" + 1)"
356 _debug i "$i"
357
358 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
359 if [ -z "$j" ] ; then
360 _err "Can not find end line: $endline"
361 return 1
362 fi
363 j="$(_math "$j" - 1)"
364 _debug j "$j"
365
366 sed -n "$i,${j}p" "$filename"
367
368 }
369
370 #Usage: multiline
371 _base64() {
372 if [ "$1" ] ; then
373 openssl base64 -e
374 else
375 openssl base64 -e | tr -d '\r\n'
376 fi
377 }
378
379 #Usage: multiline
380 _dbase64() {
381 if [ "$1" ] ; then
382 openssl base64 -d -A
383 else
384 openssl base64 -d
385 fi
386 }
387
388 #Usage: hashalg [outputhex]
389 #Output Base64-encoded digest
390 _digest() {
391 alg="$1"
392 if [ -z "$alg" ] ; then
393 _usage "Usage: _digest hashalg"
394 return 1
395 fi
396
397 outputhex="$2"
398
399 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
400 if [ "$outputhex" ] ; then
401 openssl dgst -$alg -hex | cut -d = -f 2 | tr -d ' '
402 else
403 openssl dgst -$alg -binary | _base64
404 fi
405 else
406 _err "$alg is not supported yet"
407 return 1
408 fi
409
410 }
411
412 #Usage: keyfile hashalg
413 #Output: Base64-encoded signature value
414 _sign() {
415 keyfile="$1"
416 alg="$2"
417 if [ -z "$alg" ] ; then
418 _usage "Usage: _sign keyfile hashalg"
419 return 1
420 fi
421
422 _sign_openssl="openssl dgst -sign $keyfile "
423 if [ "$alg" = "sha256" ] ; then
424 _sign_openssl="$_sign_openssl -$alg"
425 else
426 _err "$alg is not supported yet"
427 return 1
428 fi
429
430 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
431 $_sign_openssl | _base64
432 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
433 _signedECText="$($_sign_openssl | openssl asn1parse -inform DER)"
434 _debug3 "_signedECText" "$_signedECText"
435 _ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
436 _debug3 "_ec_r" "$_ec_r"
437 _ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
438 _debug3 "_ec_s" "$_ec_s"
439 printf "%s" "$_ec_r$_ec_s" | _h2b | _base64
440 else
441 _err "Unknown key file format."
442 return 1
443 fi
444
445 }
446
447 #keylength
448 _isEccKey() {
449 _length="$1"
450
451 if [ -z "$_length" ] ;then
452 return 1
453 fi
454
455 [ "$_length" != "1024" ] \
456 && [ "$_length" != "2048" ] \
457 && [ "$_length" != "3072" ] \
458 && [ "$_length" != "4096" ] \
459 && [ "$_length" != "8192" ]
460 }
461
462 # _createkey 2048|ec-256 file
463 _createkey() {
464 length="$1"
465 f="$2"
466 eccname="$length"
467 if _startswith "$length" "ec-" ; then
468 length=$(printf $length | cut -d '-' -f 2-100)
469
470 if [ "$length" = "256" ] ; then
471 eccname="prime256v1"
472 fi
473 if [ "$length" = "384" ] ; then
474 eccname="secp384r1"
475 fi
476 if [ "$length" = "521" ] ; then
477 eccname="secp521r1"
478 fi
479
480 fi
481
482 if [ -z "$length" ] ; then
483 length=2048
484 fi
485
486 _debug "Use length $length"
487
488 if _isEccKey "$length" ; then
489 _debug "Using ec name: $eccname"
490 openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
491 else
492 _debug "Using RSA: $length"
493 openssl genrsa $length 2>/dev/null > "$f"
494 fi
495
496 if [ "$?" != "0" ] ; then
497 _err "Create key error."
498 return 1
499 fi
500 }
501
502
503 #domain
504 _is_idn() {
505 _is_idn_d="$1"
506 _debug2 _is_idn_d "$_is_idn_d"
507 _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d "[0-9a-zA-Z.,-]")
508 _debug2 _idn_temp "$_idn_temp"
509 [ "$_idn_temp" ]
510 }
511
512 #aa.com
513 #aa.com,bb.com,cc.com
514 _idn() {
515 __idn_d="$1"
516 if ! _is_idn "$__idn_d" ; then
517 printf "%s" "$__idn_d"
518 return 0
519 fi
520
521 if _exists idn ; then
522 if _contains "$__idn_d" ',' ; then
523 _i_first="1"
524 for f in $(echo "$__idn_d" | tr ',' ' ') ; do
525 [ -z "$f" ] && continue
526 if [ -z "$_i_first" ] ; then
527 printf "%s" ","
528 else
529 _i_first=""
530 fi
531 idn "$f" | tr -d "\r\n"
532 done
533 else
534 idn "$__idn_d" | tr -d "\r\n"
535 fi
536 else
537 _err "Please install idn to process IDN names."
538 fi
539 }
540
541 #_createcsr cn san_list keyfile csrfile conf
542 _createcsr() {
543 _debug _createcsr
544 domain="$1"
545 domainlist="$2"
546 csrkey="$3"
547 csr="$4"
548 csrconf="$5"
549 _debug2 domain "$domain"
550 _debug2 domainlist "$domainlist"
551 _debug2 csrkey "$csrkey"
552 _debug2 csr "$csr"
553 _debug2 csrconf "$csrconf"
554
555 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" > "$csrconf"
556
557 if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
558 #single domain
559 _info "Single domain" "$domain"
560 else
561 domainlist="$(_idn $domainlist)"
562 _debug2 domainlist "$domainlist"
563 if _contains "$domainlist" "," ; then
564 alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
565 else
566 alt="DNS:$domainlist"
567 fi
568 #multi
569 _info "Multi domain" "$alt"
570 printf -- "\nsubjectAltName=$alt" >> "$csrconf"
571 fi
572 if [ "$Le_OCSP_Stable" ] ; then
573 _savedomainconf Le_OCSP_Stable "$Le_OCSP_Stable"
574 printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >> "$csrconf"
575 fi
576
577 _csr_cn="$(_idn "$domain")"
578 _debug2 _csr_cn "$_csr_cn"
579 openssl req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
580 }
581
582 #_signcsr key csr conf cert
583 _signcsr() {
584 key="$1"
585 csr="$2"
586 conf="$3"
587 cert="$4"
588 _debug "_signcsr"
589
590 _msg="$(openssl x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
591 _ret="$?"
592 _debug "$_msg"
593 return $_ret
594 }
595
596 #_csrfile
597 _readSubjectFromCSR() {
598 _csrfile="$1"
599 if [ -z "$_csrfile" ] ; then
600 _usage "_readSubjectFromCSR mycsr.csr"
601 return 1
602 fi
603 openssl req -noout -in "$_csrfile" -subject | _egrep_o "CN=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n'
604 }
605
606 #_csrfile
607 #echo comma separated domain list
608 _readSubjectAltNamesFromCSR() {
609 _csrfile="$1"
610 if [ -z "$_csrfile" ] ; then
611 _usage "_readSubjectAltNamesFromCSR mycsr.csr"
612 return 1
613 fi
614
615 _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
616 _debug _csrsubj "$_csrsubj"
617
618 _dnsAltnames="$(openssl req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
619 _debug _dnsAltnames "$_dnsAltnames"
620
621 if _contains "$_dnsAltnames," "DNS:$_csrsubj," ; then
622 _debug "AltNames contains subject"
623 _dnsAltnames="$(printf "%s" "$_dnsAltnames," | sed "s/DNS:$_csrsubj,//g")"
624 else
625 _debug "AltNames doesn't contain subject"
626 fi
627
628 printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
629 }
630
631 #_csrfile
632 _readKeyLengthFromCSR() {
633 _csrfile="$1"
634 if [ -z "$_csrfile" ] ; then
635 _usage "_readKeyLengthFromCSR mycsr.csr"
636 return 1
637 fi
638
639 _outcsr="$(openssl req -noout -text -in "$_csrfile")"
640 if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey" ; then
641 _debug "ECC CSR"
642 echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
643 else
644 _debug "RSA CSR"
645 echo "$_outcsr" | _egrep_o "^ *Public-Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
646 fi
647 }
648
649
650 _ss() {
651 _port="$1"
652
653 if _exists "ss" ; then
654 _debug "Using: ss"
655 ss -ntpl | grep ":$_port "
656 return 0
657 fi
658
659 if _exists "netstat" ; then
660 _debug "Using: netstat"
661 if netstat -h 2>&1 | grep "\-p proto" >/dev/null ; then
662 #for windows version netstat tool
663 netstat -an -p tcp | grep "LISTENING" | grep ":$_port "
664 else
665 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null ; then
666 netstat -an -p tcp | grep LISTEN | grep ":$_port "
667 elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null ; then
668 #for solaris
669 netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
670 else
671 netstat -ntpl | grep ":$_port "
672 fi
673 fi
674 return 0
675 fi
676
677 return 1
678 }
679
680 #domain [password] [isEcc]
681 toPkcs() {
682 domain="$1"
683 pfxPassword="$2"
684 if [ -z "$domain" ] ; then
685 _usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
686 return 1
687 fi
688
689 _isEcc="$3"
690
691 _initpath "$domain" "$_isEcc"
692
693 if [ "$pfxPassword" ] ; then
694 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
695 else
696 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
697 fi
698
699 if [ "$?" = "0" ] ; then
700 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
701 fi
702
703 }
704
705 #[2048]
706 createAccountKey() {
707 _info "Creating account key"
708 if [ -z "$1" ] ; then
709 _usage "Usage: $PROJECT_ENTRY --createAccountKey --accountkeylength 2048"
710 return
711 fi
712
713 length=$1
714
715 if [ -z "$length" ] || [ "$length" = "$NO_VALUE" ] ; then
716 _debug "Use default length 2048"
717 length=2048
718 fi
719 _debug length "$length"
720 _initpath
721
722 if [ -f "$ACCOUNT_KEY_PATH" ] ; then
723 _info "Account key exists, skip"
724 return
725 else
726 #generate account key
727 _createkey "$length" "$ACCOUNT_KEY_PATH"
728 fi
729
730 }
731
732 #domain [length]
733 createDomainKey() {
734 _info "Creating domain key"
735 if [ -z "$1" ] ; then
736 _usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
737 return
738 fi
739
740 domain=$1
741 length=$2
742
743 _initpath $domain "$length"
744
745 if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
746 _createkey "$length" "$CERT_KEY_PATH"
747 else
748 if [ "$IS_RENEW" ] ; then
749 _info "Domain key exists, skip"
750 return 0
751 else
752 _err "Domain key exists, do you want to overwrite the key?"
753 _err "Add '--force', and try again."
754 return 1
755 fi
756 fi
757
758 }
759
760 # domain domainlist isEcc
761 createCSR() {
762 _info "Creating csr"
763 if [ -z "$1" ] ; then
764 _usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
765 return
766 fi
767
768 domain="$1"
769 domainlist="$2"
770 _isEcc="$3"
771
772 _initpath "$domain" "$_isEcc"
773
774 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
775 _info "CSR exists, skip"
776 return
777 fi
778
779 if [ ! -f "$CERT_KEY_PATH" ] ; then
780 _err "The key file is not found: $CERT_KEY_PATH"
781 _err "Please create the key file first."
782 return 1
783 fi
784 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
785
786 }
787
788 _urlencode() {
789 __n=$(cat)
790 echo $__n | tr '/+' '_-' | tr -d '= '
791 }
792
793 _time2str() {
794 #BSD
795 if date -u -d@$1 2>/dev/null ; then
796 return
797 fi
798
799 #Linux
800 if date -u -r $1 2>/dev/null ; then
801 return
802 fi
803
804 #Soaris
805 if _exists adb ; then
806 echo $(echo "0t${1}=Y" | adb)
807 fi
808
809 }
810
811 _normalizeJson() {
812 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
813 }
814
815 _stat() {
816 #Linux
817 if stat -c '%U:%G' "$1" 2>/dev/null ; then
818 return
819 fi
820
821 #BSD
822 if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
823 return
824 fi
825
826 return 1; #error, 'stat' not found
827 }
828
829 #keyfile
830 _calcjwk() {
831 keyfile="$1"
832 if [ -z "$keyfile" ] ; then
833 _usage "Usage: _calcjwk keyfile"
834 return 1
835 fi
836 EC_SIGN=""
837 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
838 _debug "RSA key"
839 pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
840 if [ "${#pub_exp}" = "5" ] ; then
841 pub_exp=0$pub_exp
842 fi
843 _debug3 pub_exp "$pub_exp"
844
845 e=$(echo $pub_exp | _h2b | _base64)
846 _debug3 e "$e"
847
848 modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 )
849 _debug3 modulus "$modulus"
850 n="$(printf "%s" "$modulus"| _h2b | _base64 | _urlencode )"
851 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
852 _debug3 jwk "$jwk"
853
854 JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
855 JWK_HEADERPLACE_PART1='{"nonce": "'
856 JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
857 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
858 _debug "EC key"
859 EC_SIGN="1"
860 crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
861 _debug3 crv "$crv"
862
863 pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
864 pubi=$(_math $pubi + 1)
865 _debug3 pubi "$pubi"
866
867 pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
868 pubj=$(_math $pubj - 1)
869 _debug3 pubj "$pubj"
870
871 pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
872 _debug3 pubtext "$pubtext"
873
874 xlen="$(printf "$pubtext" | tr -d ':' | wc -c)"
875 xlen=$(_math $xlen / 4)
876 _debug3 xlen "$xlen"
877
878 xend=$(_math "$xlen" + 1)
879 x="$(printf $pubtext | cut -d : -f 2-$xend)"
880 _debug3 x "$x"
881
882 x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
883 _debug3 x64 "$x64"
884
885 xend=$(_math "$xend" + 1)
886 y="$(printf $pubtext | cut -d : -f $xend-10000)"
887 _debug3 y "$y"
888
889 y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
890 _debug3 y64 "$y64"
891
892 jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
893 _debug3 jwk "$jwk"
894
895 JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}'
896 JWK_HEADERPLACE_PART1='{"nonce": "'
897 JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}'
898 else
899 _err "Only RSA or EC key is supported."
900 return 1
901 fi
902
903 _debug3 JWK_HEADER "$JWK_HEADER"
904 }
905
906 _time() {
907 date -u "+%s"
908 }
909
910 _mktemp() {
911 if _exists mktemp ; then
912 if mktemp 2>/dev/null ; then
913 return
914 elif _contains "$(mktemp 2>&1)" "-t prefix" && mktemp -t "$PROJECT_NAME" 2>/dev/null ; then
915 #for Mac osx
916 return
917 fi
918 fi
919 if [ -d "/tmp" ] ; then
920 echo "/tmp/${PROJECT_NAME}wefADf24sf.$(_time).tmp"
921 return 0
922 fi
923 _err "Can not create temp file."
924 }
925
926 _inithttp() {
927
928 if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER" ; then
929 HTTP_HEADER="$(_mktemp)"
930 _debug2 HTTP_HEADER "$HTTP_HEADER"
931 fi
932
933 if [ -z "$CURL" ] ; then
934 CURL="curl -L --silent --dump-header $HTTP_HEADER "
935 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
936 _CURL_DUMP="$(_mktemp)"
937 CURL="$CURL --trace-ascii $_CURL_DUMP "
938 fi
939
940 if [ "$CA_BUNDLE" ] ; then
941 CURL="$CURL --cacert $CA_BUNDLE "
942 fi
943
944 if [ "$HTTPS_INSECURE" ] ; then
945 CURL="$CURL --insecure "
946 fi
947 fi
948
949 if [ -z "$WGET" ] ; then
950 WGET="wget -q"
951 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
952 WGET="$WGET -d "
953 fi
954 if [ "$CA_BUNDLE" ] ; then
955 WGET="$WGET --ca-certificate $CA_BUNDLE "
956 fi
957 if [ "$HTTPS_INSECURE" ] ; then
958 WGET="$WGET --no-check-certificate "
959 fi
960 fi
961
962 }
963
964
965 # body url [needbase64] [POST|PUT]
966 _post() {
967 body="$1"
968 url="$2"
969 needbase64="$3"
970 httpmethod="$4"
971
972 if [ -z "$httpmethod" ] ; then
973 httpmethod="POST"
974 fi
975 _debug $httpmethod
976 _debug "url" "$url"
977 _debug2 "body" "$body"
978
979 _inithttp
980
981 if _exists "curl" ; then
982 _CURL="$CURL"
983 _debug "_CURL" "$_CURL"
984 if [ "$needbase64" ] ; then
985 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)"
986 else
987 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" )"
988 fi
989 _ret="$?"
990 if [ "$_ret" != "0" ] ; then
991 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
992 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
993 _err "Here is the curl dump log:"
994 _err "$(cat "$_CURL_DUMP")"
995 fi
996 fi
997 elif _exists "wget" ; then
998 _debug "WGET" "$WGET"
999 if [ "$needbase64" ] ; then
1000 if [ "$httpmethod" = "POST" ] ; then
1001 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)"
1002 else
1003 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)"
1004 fi
1005 else
1006 if [ "$httpmethod" = "POST" ] ; then
1007 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")"
1008 else
1009 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")"
1010 fi
1011 fi
1012 _ret="$?"
1013 if [ "$_ret" = "8" ] ; then
1014 _ret=0
1015 _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later."
1016 fi
1017 if [ "$_ret" != "0" ] ; then
1018 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
1019 fi
1020 _sed_i "s/^ *//g" "$HTTP_HEADER"
1021 else
1022 _ret="$?"
1023 _err "Neither curl nor wget is found, can not do $httpmethod."
1024 fi
1025 _debug "_ret" "$_ret"
1026 printf "%s" "$response"
1027 return $_ret
1028 }
1029
1030
1031 # url getheader timeout
1032 _get() {
1033 _debug GET
1034 url="$1"
1035 onlyheader="$2"
1036 t="$3"
1037 _debug url $url
1038 _debug "timeout" "$t"
1039
1040 _inithttp
1041
1042 if _exists "curl" ; then
1043 _CURL="$CURL"
1044 if [ "$t" ] ; then
1045 _CURL="$_CURL --connect-timeout $t"
1046 fi
1047 _debug "_CURL" "$_CURL"
1048 if [ "$onlyheader" ] ; then
1049 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" $url
1050 else
1051 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" $url
1052 fi
1053 ret=$?
1054 if [ "$ret" != "0" ] ; then
1055 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret"
1056 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
1057 _err "Here is the curl dump log:"
1058 _err "$(cat "$_CURL_DUMP")"
1059 fi
1060 fi
1061 elif _exists "wget" ; then
1062 _WGET="$WGET"
1063 if [ "$t" ] ; then
1064 _WGET="$_WGET --timeout=$t"
1065 fi
1066 _debug "_WGET" "$_WGET"
1067 if [ "$onlyheader" ] ; then
1068 $_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'
1069 else
1070 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - $url
1071 fi
1072 ret=$?
1073 if [ "$_ret" = "8" ] ; then
1074 _ret=0
1075 _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later."
1076 fi
1077 if [ "$ret" != "0" ] ; then
1078 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
1079 fi
1080 else
1081 ret=$?
1082 _err "Neither curl nor wget is found, can not do GET."
1083 fi
1084 _debug "ret" "$ret"
1085 return $ret
1086 }
1087
1088 _head_n() {
1089 head -n $1
1090 }
1091
1092 _tail_n() {
1093 if ! tail -n $1 2>/dev/null ; then
1094 #fix for solaris
1095 tail -$1
1096 fi
1097 }
1098
1099 # url payload needbase64 keyfile
1100 _send_signed_request() {
1101 url=$1
1102 payload=$2
1103 needbase64=$3
1104 keyfile=$4
1105 if [ -z "$keyfile" ] ; then
1106 keyfile="$ACCOUNT_KEY_PATH"
1107 fi
1108 _debug url $url
1109 _debug payload "$payload"
1110
1111 if ! _calcjwk "$keyfile" ; then
1112 return 1
1113 fi
1114
1115 payload64=$(printf "%s" "$payload" | _base64 | _urlencode)
1116 _debug3 payload64 $payload64
1117
1118 nonceurl="$API/directory"
1119 _headers="$(_get $nonceurl "onlyheader")"
1120
1121 if [ "$?" != "0" ] ; then
1122 _err "Can not connect to $nonceurl to get nonce."
1123 return 1
1124 fi
1125
1126 _debug3 _headers "$_headers"
1127
1128 nonce="$( echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
1129
1130 _debug3 nonce "$nonce"
1131
1132 protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
1133 _debug3 protected "$protected"
1134
1135 protected64="$(printf "$protected" | _base64 | _urlencode)"
1136 _debug3 protected64 "$protected64"
1137
1138 sig=$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256" | _urlencode)
1139 _debug3 sig "$sig"
1140
1141 body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
1142 _debug3 body "$body"
1143
1144
1145 response="$(_post "$body" $url "$needbase64")"
1146 if [ "$?" != "0" ] ; then
1147 _err "Can not post to $url"
1148 return 1
1149 fi
1150 _debug2 original "$response"
1151
1152 response="$( echo "$response" | _normalizeJson )"
1153
1154 responseHeaders="$(cat $HTTP_HEADER)"
1155
1156 _debug2 responseHeaders "$responseHeaders"
1157 _debug2 response "$response"
1158 code="$(grep "^HTTP" $HTTP_HEADER | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n" )"
1159 _debug code $code
1160
1161 }
1162
1163
1164 #setopt "file" "opt" "=" "value" [";"]
1165 _setopt() {
1166 __conf="$1"
1167 __opt="$2"
1168 __sep="$3"
1169 __val="$4"
1170 __end="$5"
1171 if [ -z "$__opt" ] ; then
1172 _usage usage: _setopt '"file" "opt" "=" "value" [";"]'
1173 return
1174 fi
1175 if [ ! -f "$__conf" ] ; then
1176 touch "$__conf"
1177 fi
1178
1179 if grep -n "^$__opt$__sep" "$__conf" > /dev/null ; then
1180 _debug3 OK
1181 if _contains "$__val" "&" ; then
1182 __val="$(echo $__val | sed 's/&/\\&/g')"
1183 fi
1184 text="$(cat $__conf)"
1185 echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
1186
1187 elif grep -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
1188 if _contains "$__val" "&" ; then
1189 __val="$(echo $__val | sed 's/&/\\&/g')"
1190 fi
1191 text="$(cat $__conf)"
1192 echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
1193
1194 else
1195 _debug3 APP
1196 echo "$__opt$__sep$__val$__end" >> "$__conf"
1197 fi
1198 _debug2 "$(grep -n "^$__opt$__sep" $__conf)"
1199 }
1200
1201 #_savedomainconf key value
1202 #save to domain.conf
1203 _savedomainconf() {
1204 _sdkey="$1"
1205 _sdvalue="$2"
1206 if [ "$DOMAIN_CONF" ] ; then
1207 _setopt "$DOMAIN_CONF" "$_sdkey" "=" "\"$_sdvalue\""
1208 else
1209 _err "DOMAIN_CONF is empty, can not save $_sdkey=$_sdvalue"
1210 fi
1211 }
1212
1213 #_cleardomainconf key
1214 _cleardomainconf() {
1215 _sdkey="$1"
1216 if [ "$DOMAIN_CONF" ] ; then
1217 _sed_i "s/^$_sdkey.*$//" "$DOMAIN_CONF"
1218 else
1219 _err "DOMAIN_CONF is empty, can not save $_sdkey=$value"
1220 fi
1221 }
1222
1223 #_readdomainconf key
1224 _readdomainconf() {
1225 _sdkey="$1"
1226 if [ "$DOMAIN_CONF" ] ; then
1227 (
1228 eval $(grep "^$_sdkey *=" "$DOMAIN_CONF")
1229 eval "printf \"%s\" \"\$$_sdkey\""
1230 )
1231 else
1232 _err "DOMAIN_CONF is empty, can not read $_sdkey"
1233 fi
1234 }
1235
1236 #_saveaccountconf key value
1237 _saveaccountconf() {
1238 _sckey="$1"
1239 _scvalue="$2"
1240 if [ "$ACCOUNT_CONF_PATH" ] ; then
1241 _setopt "$ACCOUNT_CONF_PATH" "$_sckey" "=" "'$_scvalue'"
1242 else
1243 _err "ACCOUNT_CONF_PATH is empty, can not save $_sckey=$_scvalue"
1244 fi
1245 }
1246
1247 #_clearaccountconf key
1248 _clearaccountconf() {
1249 _scvalue="$1"
1250 if [ "$ACCOUNT_CONF_PATH" ] ; then
1251 _sed_i "s/^$_scvalue.*$//" "$ACCOUNT_CONF_PATH"
1252 else
1253 _err "ACCOUNT_CONF_PATH is empty, can not clear $_scvalue"
1254 fi
1255 }
1256
1257 # content localaddress
1258 _startserver() {
1259 content="$1"
1260 ncaddr="$2"
1261 _debug "ncaddr" "$ncaddr"
1262
1263 _debug "startserver: $$"
1264 nchelp="$(nc -h 2>&1)"
1265
1266 _debug Le_HTTPPort "$Le_HTTPPort"
1267 _debug Le_Listen_V4 "$Le_Listen_V4"
1268 _debug Le_Listen_V6 "$Le_Listen_V6"
1269 _NC="nc"
1270
1271 if [ "$Le_Listen_V4" ] ; then
1272 _NC="$_NC -4"
1273 elif [ "$Le_Listen_V6" ] ; then
1274 _NC="$_NC -6"
1275 fi
1276
1277 if echo "$nchelp" | grep "\-q[ ,]" >/dev/null ; then
1278 _NC="$_NC -q 1 -l $ncaddr"
1279 else
1280 if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then
1281 _NC="$_NC -c -l $ncaddr"
1282 elif echo "$nchelp" | grep "\-N" |grep "Shutdown the network socket after EOF on stdin" >/dev/null ; then
1283 _NC="$_NC -N -l $ncaddr"
1284 else
1285 _NC="$_NC -l $ncaddr"
1286 fi
1287 fi
1288
1289
1290 _debug "_NC" "$_NC"
1291
1292 #for centos ncat
1293 if _contains "$nchelp" "nmap.org" ; then
1294 _debug "Using ncat: nmap.org"
1295 if [ "$DEBUG" ] ; then
1296 if printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ; then
1297 return
1298 fi
1299 else
1300 if printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1; then
1301 return
1302 fi
1303 fi
1304 _err "ncat listen error."
1305 fi
1306
1307 # while true ; do
1308 if [ "$DEBUG" ] ; then
1309 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort ; then
1310 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ;
1311 fi
1312 else
1313 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then
1314 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1
1315 fi
1316 fi
1317 if [ "$?" != "0" ] ; then
1318 _err "nc listen error."
1319 exit 1
1320 fi
1321 # done
1322 }
1323
1324 _stopserver(){
1325 pid="$1"
1326 _debug "pid" "$pid"
1327 if [ -z "$pid" ] ; then
1328 return
1329 fi
1330
1331 _debug2 "Le_HTTPPort" "$Le_HTTPPort"
1332 if [ "$Le_HTTPPort" ] ; then
1333 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ] ; then
1334 _get "http://localhost:$Le_HTTPPort" "" 1
1335 else
1336 _get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
1337 fi
1338 fi
1339
1340 _debug2 "Le_TLSPort" "$Le_TLSPort"
1341 if [ "$Le_TLSPort" ] ; then
1342 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ] ; then
1343 _get "https://localhost:$Le_TLSPort" "" 1
1344 _get "https://localhost:$Le_TLSPort" "" 1
1345 else
1346 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
1347 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
1348 fi
1349 fi
1350 }
1351
1352 # sleep sec
1353 _sleep() {
1354 _sleep_sec="$1"
1355 if [ "$__INTERACTIVE" ] ; then
1356 _sleep_c="$_sleep_sec"
1357 while [ "$_sleep_c" -ge "0" ] ;
1358 do
1359 printf "\r \r"
1360 __green "$_sleep_c"
1361 _sleep_c="$(_math $_sleep_c - 1)"
1362 sleep 1
1363 done
1364 printf "\r"
1365 else
1366 sleep "$_sleep_sec"
1367 fi
1368 }
1369
1370 # _starttlsserver san_a san_b port content _ncaddr
1371 _starttlsserver() {
1372 _info "Starting tls server."
1373 san_a="$1"
1374 san_b="$2"
1375 port="$3"
1376 content="$4"
1377 opaddr="$5"
1378
1379 _debug san_a "$san_a"
1380 _debug san_b "$san_b"
1381 _debug port "$port"
1382
1383 #create key TLS_KEY
1384 if ! _createkey "2048" "$TLS_KEY" ; then
1385 _err "Create tls validation key error."
1386 return 1
1387 fi
1388
1389 #create csr
1390 alt="$san_a"
1391 if [ "$san_b" ] ; then
1392 alt="$alt,$san_b"
1393 fi
1394 if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" ; then
1395 _err "Create tls validation csr error."
1396 return 1
1397 fi
1398
1399 #self signed
1400 if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT" ; then
1401 _err "Create tls validation cert error."
1402 return 1
1403 fi
1404
1405 __S_OPENSSL="openssl s_server -cert $TLS_CERT -key $TLS_KEY "
1406 if [ "$opaddr" ] ; then
1407 __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
1408 else
1409 __S_OPENSSL="$__S_OPENSSL -accept $port"
1410 fi
1411
1412 _debug Le_Listen_V4 "$Le_Listen_V4"
1413 _debug Le_Listen_V6 "$Le_Listen_V6"
1414 if [ "$Le_Listen_V4" ] ; then
1415 __S_OPENSSL="$__S_OPENSSL -4"
1416 elif [ "$Le_Listen_V6" ] ; then
1417 __S_OPENSSL="$__S_OPENSSL -6"
1418 fi
1419
1420 #start openssl
1421 _debug "$__S_OPENSSL"
1422 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
1423 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | $__S_OPENSSL -tlsextdebug ) &
1424 else
1425 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | $__S_OPENSSL >/dev/null 2>&1) &
1426 fi
1427
1428 serverproc="$!"
1429 sleep 2
1430 _debug serverproc $serverproc
1431 }
1432
1433 #file
1434 _readlink() {
1435 _rf="$1"
1436 if ! readlink -f "$_rf" 2>/dev/null; then
1437 if _startswith "$_rf" "\./$PROJECT_ENTRY" ; then
1438 printf -- "%s" "$(pwd)/$PROJECT_ENTRY"
1439 return 0
1440 fi
1441 readlink "$_rf"
1442 fi
1443 }
1444
1445 __initHome() {
1446 if [ -z "$_SCRIPT_HOME" ] ; then
1447 if _exists readlink && _exists dirname ; then
1448 _debug "Lets find script dir."
1449 _debug "_SCRIPT_" "$_SCRIPT_"
1450 _script="$(_readlink "$_SCRIPT_")"
1451 _debug "_script" "$_script"
1452 _script_home="$(dirname "$_script")"
1453 _debug "_script_home" "$_script_home"
1454 if [ -d "$_script_home" ] ; then
1455 _SCRIPT_HOME="$_script_home"
1456 else
1457 _err "It seems the script home is not correct:$_script_home"
1458 fi
1459 fi
1460 fi
1461
1462
1463 if [ -z "$LE_WORKING_DIR" ] ; then
1464 if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ] ; then
1465 _debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
1466 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
1467 else
1468 LE_WORKING_DIR="$_SCRIPT_HOME"
1469 fi
1470 fi
1471
1472 if [ -z "$LE_WORKING_DIR" ] ; then
1473 _debug "Using default home:$DEFAULT_INSTALL_HOME"
1474 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
1475 fi
1476 export LE_WORKING_DIR
1477
1478 _DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
1479
1480 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
1481 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ] ; then
1482 . "$_DEFAULT_ACCOUNT_CONF_PATH"
1483 fi
1484 fi
1485
1486 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
1487 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
1488 fi
1489
1490 DEFAULT_LOG_FILE="$LE_WORKING_DIR/$PROJECT_NAME.log"
1491
1492 DEFAULT_CA_HOME="$LE_WORKING_DIR/ca"
1493 }
1494
1495 #[domain] [keylength]
1496 _initpath() {
1497
1498 __initHome
1499
1500 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
1501 . "$ACCOUNT_CONF_PATH"
1502 fi
1503
1504 if [ "$IN_CRON" ] ; then
1505 if [ ! "$_USER_PATH_EXPORTED" ] ; then
1506 _USER_PATH_EXPORTED=1
1507 export PATH="$USER_PATH:$PATH"
1508 fi
1509 fi
1510
1511 if [ -z "$CA_HOME" ] ; then
1512 CA_HOME="$DEFAULT_CA_HOME"
1513 fi
1514
1515 if [ -z "$API" ] ; then
1516 if [ -z "$STAGE" ] ; then
1517 API="$DEFAULT_CA"
1518 else
1519 API="$STAGE_CA"
1520 _info "Using stage api:$API"
1521 fi
1522 fi
1523
1524 _API_HOST="$(echo "$API" | cut -d : -f 2 | tr -d '/')"
1525 CA_DIR="$CA_HOME/$_API_HOST"
1526
1527 _DEFAULT_CA_CONF="$CA_DIR/ca.conf"
1528
1529 if [ -z "$CA_CONF" ] ; then
1530 CA_CONF="$_DEFAULT_CA_CONF"
1531 fi
1532
1533 if [ -f "$CA_CONF" ] ; then
1534 . "$CA_CONF"
1535 fi
1536
1537 if [ -z "$ACME_DIR" ] ; then
1538 ACME_DIR="/home/.acme"
1539 fi
1540
1541 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
1542 APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR"
1543 fi
1544
1545 if [ -z "$USER_AGENT" ] ; then
1546 USER_AGENT="$DEFAULT_USER_AGENT"
1547 fi
1548
1549 if [ -z "$HTTP_HEADER" ] ; then
1550 HTTP_HEADER="$LE_WORKING_DIR/http.header"
1551 fi
1552
1553 _OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key"
1554 _OLD_ACCOUNT_JSON="$LE_WORKING_DIR/account.json"
1555
1556 _DEFAULT_ACCOUNT_KEY_PATH="$CA_DIR/account.key"
1557 _DEFAULT_ACCOUNT_JSON_PATH="$CA_DIR/account.json"
1558 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
1559 ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
1560 fi
1561
1562 if [ -z "$ACCOUNT_JSON_PATH" ] ; then
1563 ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
1564 fi
1565
1566
1567 _DEFAULT_CERT_HOME="$LE_WORKING_DIR"
1568 if [ -z "$CERT_HOME" ] ; then
1569 CERT_HOME="$_DEFAULT_CERT_HOME"
1570 fi
1571
1572 if [ -z "$1" ] ; then
1573 return 0
1574 fi
1575
1576 mkdir -p "$CA_DIR"
1577
1578 domain="$1"
1579 _ilength="$2"
1580
1581 if [ -z "$DOMAIN_PATH" ] ; then
1582 domainhome="$CERT_HOME/$domain"
1583 domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
1584
1585 DOMAIN_PATH="$domainhome"
1586
1587 if _isEccKey "$_ilength" ; then
1588 DOMAIN_PATH="$domainhomeecc"
1589 else
1590 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ] ; then
1591 _info "The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
1592 fi
1593 fi
1594 _debug DOMAIN_PATH "$DOMAIN_PATH"
1595 fi
1596
1597 if [ ! -d "$DOMAIN_PATH" ] ; then
1598 if ! mkdir -p "$DOMAIN_PATH" ; then
1599 _err "Can not create domain path: $DOMAIN_PATH"
1600 return 1
1601 fi
1602 fi
1603
1604 if [ -z "$DOMAIN_CONF" ] ; then
1605 DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
1606 fi
1607
1608 if [ -z "$DOMAIN_SSL_CONF" ] ; then
1609 DOMAIN_SSL_CONF="$DOMAIN_PATH/$domain.csr.conf"
1610 fi
1611
1612 if [ -z "$CSR_PATH" ] ; then
1613 CSR_PATH="$DOMAIN_PATH/$domain.csr"
1614 fi
1615 if [ -z "$CERT_KEY_PATH" ] ; then
1616 CERT_KEY_PATH="$DOMAIN_PATH/$domain.key"
1617 fi
1618 if [ -z "$CERT_PATH" ] ; then
1619 CERT_PATH="$DOMAIN_PATH/$domain.cer"
1620 fi
1621 if [ -z "$CA_CERT_PATH" ] ; then
1622 CA_CERT_PATH="$DOMAIN_PATH/ca.cer"
1623 fi
1624 if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
1625 CERT_FULLCHAIN_PATH="$DOMAIN_PATH/fullchain.cer"
1626 fi
1627 if [ -z "$CERT_PFX_PATH" ] ; then
1628 CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
1629 fi
1630
1631 if [ -z "$TLS_CONF" ] ; then
1632 TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
1633 fi
1634 if [ -z "$TLS_CERT" ] ; then
1635 TLS_CERT="$DOMAIN_PATH/tls.valdation.cert"
1636 fi
1637 if [ -z "$TLS_KEY" ] ; then
1638 TLS_KEY="$DOMAIN_PATH/tls.valdation.key"
1639 fi
1640 if [ -z "$TLS_CSR" ] ; then
1641 TLS_CSR="$DOMAIN_PATH/tls.valdation.csr"
1642 fi
1643
1644 }
1645
1646
1647 _apachePath() {
1648 _APACHECTL="apachectl"
1649 if ! _exists apachectl ; then
1650 if _exists apache2ctl ; then
1651 _APACHECTL="apache2ctl"
1652 else
1653 _err "'apachectl not found. It seems that apache is not installed, or you are not root user.'"
1654 _err "Please use webroot mode to try again."
1655 return 1
1656 fi
1657 fi
1658 httpdconfname="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
1659 _debug httpdconfname "$httpdconfname"
1660 if _startswith "$httpdconfname" '/' ; then
1661 httpdconf="$httpdconfname"
1662 httpdconfname="$(basename $httpdconfname)"
1663 else
1664 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
1665 _debug httpdroot "$httpdroot"
1666 httpdconf="$httpdroot/$httpdconfname"
1667 httpdconfname="$(basename $httpdconfname)"
1668 fi
1669 _debug httpdconf "$httpdconf"
1670 _debug httpdconfname "$httpdconfname"
1671 if [ ! -f "$httpdconf" ] ; then
1672 _err "Apache Config file not found" "$httpdconf"
1673 return 1
1674 fi
1675 return 0
1676 }
1677
1678 _restoreApache() {
1679 if [ -z "$usingApache" ] ; then
1680 return 0
1681 fi
1682 _initpath
1683 if ! _apachePath ; then
1684 return 1
1685 fi
1686
1687 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
1688 _debug "No config file to restore."
1689 return 0
1690 fi
1691
1692 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" > "$httpdconf"
1693 _debug "Restored: $httpdconf."
1694 if ! $_APACHECTL -t >/dev/null 2>&1 ; then
1695 _err "Sorry, restore apache config error, please contact me."
1696 return 1;
1697 fi
1698 _debug "Restored successfully."
1699 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
1700 return 0
1701 }
1702
1703 _setApache() {
1704 _initpath
1705 if ! _apachePath ; then
1706 return 1
1707 fi
1708
1709 #test the conf first
1710 _info "Checking if there is an error in the apache config file before starting."
1711 _msg="$($_APACHECTL -t 2>&1 )"
1712 if [ "$?" != "0" ] ; then
1713 _err "Sorry, apache config file has error, please fix it first, then try again."
1714 _err "Don't worry, there is nothing changed to your system."
1715 _err "$_msg"
1716 return 1;
1717 else
1718 _info "OK"
1719 fi
1720
1721 #backup the conf
1722 _debug "Backup apache config file" "$httpdconf"
1723 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/" ; then
1724 _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
1725 _err "This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT"
1726 return 1
1727 fi
1728 _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
1729 _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
1730 _info "The backup file will be deleted on sucess, just forget it."
1731
1732 #add alias
1733
1734 apacheVer="$($_APACHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )"
1735 _debug "apacheVer" "$apacheVer"
1736 apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)"
1737 apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
1738
1739 if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ] ; then
1740 echo "
1741 Alias /.well-known/acme-challenge $ACME_DIR
1742
1743 <Directory $ACME_DIR >
1744 Require all granted
1745 </Directory>
1746 " >> "$httpdconf"
1747 else
1748 echo "
1749 Alias /.well-known/acme-challenge $ACME_DIR
1750
1751 <Directory $ACME_DIR >
1752 Order allow,deny
1753 Allow from all
1754 </Directory>
1755 " >> "$httpdconf"
1756 fi
1757
1758 _msg="$($_APACHECTL -t 2>&1 )"
1759 if [ "$?" != "0" ] ; then
1760 _err "Sorry, apache config error"
1761 if _restoreApache ; then
1762 _err "The apache config file is restored."
1763 else
1764 _err "Sorry, The apache config file can not be restored, please report bug."
1765 fi
1766 return 1;
1767 fi
1768
1769 if [ ! -d "$ACME_DIR" ] ; then
1770 mkdir -p "$ACME_DIR"
1771 chmod 755 "$ACME_DIR"
1772 fi
1773
1774 if ! $_APACHECTL graceful ; then
1775 _err "Sorry, $_APACHECTL graceful error, please contact me."
1776 _restoreApache
1777 return 1;
1778 fi
1779 usingApache="1"
1780 return 0
1781 }
1782
1783 _clearup() {
1784 _stopserver $serverproc
1785 serverproc=""
1786 _restoreApache
1787 _clearupdns
1788 if [ -z "$DEBUG" ] ; then
1789 rm -f "$TLS_CONF"
1790 rm -f "$TLS_CERT"
1791 rm -f "$TLS_KEY"
1792 rm -f "$TLS_CSR"
1793 fi
1794 }
1795
1796 _clearupdns() {
1797 _debug "_clearupdns"
1798 if [ "$dnsadded" != 1 ] || [ -z "$vlist" ] ; then
1799 _info "Dns not added, skip."
1800 return
1801 fi
1802
1803 ventries=$(echo "$vlist" | tr ',' ' ' )
1804 for ventry in $ventries
1805 do
1806 d=$(echo $ventry | cut -d $sep -f 1)
1807 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
1808 vtype=$(echo $ventry | cut -d $sep -f 4)
1809 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
1810
1811 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
1812 _info "$d is already verified, skip $vtype."
1813 continue
1814 fi
1815
1816 if [ "$vtype" != "$VTYPE_DNS" ] ; then
1817 _info "Skip $d for $vtype"
1818 continue
1819 fi
1820
1821 d_api="$(_findHook $d dnsapi $_currentRoot)"
1822 _debug d_api "$d_api"
1823
1824 if [ -z "$d_api" ] ; then
1825 _info "Not Found domain api file: $d_api"
1826 continue
1827 fi
1828
1829 (
1830 if ! . $d_api ; then
1831 _err "Load file $d_api error. Please check your api file and try again."
1832 return 1
1833 fi
1834
1835 rmcommand="${_currentRoot}_rm"
1836 if ! _exists $rmcommand ; then
1837 _err "It seems that your api file doesn't define $rmcommand"
1838 return 1
1839 fi
1840
1841 txtdomain="_acme-challenge.$d"
1842
1843 if ! $rmcommand $txtdomain ; then
1844 _err "Error removing txt for domain:$txtdomain"
1845 return 1
1846 fi
1847 )
1848
1849 done
1850 }
1851
1852 # webroot removelevel tokenfile
1853 _clearupwebbroot() {
1854 __webroot="$1"
1855 if [ -z "$__webroot" ] ; then
1856 _debug "no webroot specified, skip"
1857 return 0
1858 fi
1859
1860 _rmpath=""
1861 if [ "$2" = '1' ] ; then
1862 _rmpath="$__webroot/.well-known"
1863 elif [ "$2" = '2' ] ; then
1864 _rmpath="$__webroot/.well-known/acme-challenge"
1865 elif [ "$2" = '3' ] ; then
1866 _rmpath="$__webroot/.well-known/acme-challenge/$3"
1867 else
1868 _debug "Skip for removelevel:$2"
1869 fi
1870
1871 if [ "$_rmpath" ] ; then
1872 if [ "$DEBUG" ] ; then
1873 _debug "Debugging, skip removing: $_rmpath"
1874 else
1875 rm -rf "$_rmpath"
1876 fi
1877 fi
1878
1879 return 0
1880
1881 }
1882
1883 _on_before_issue() {
1884 _debug _on_before_issue
1885 if _hasfield "$Le_Webroot" "$NO_VALUE" ; then
1886 if ! _exists "nc" ; then
1887 _err "Please install netcat(nc) tools first."
1888 return 1
1889 fi
1890 elif ! _hasfield "$Le_Webroot" "$W_TLS" ; then
1891 #no need to check anymore
1892 return 0
1893 fi
1894
1895 _debug Le_LocalAddress "$Le_LocalAddress"
1896
1897 alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
1898 _index=1
1899 _currentRoot=""
1900 _addrIndex=1
1901 for d in $alldomains
1902 do
1903 _debug "Check for domain" $d
1904 _currentRoot="$(_getfield "$Le_Webroot" $_index)"
1905 _debug "_currentRoot" "$_currentRoot"
1906 _index=$(_math $_index + 1)
1907 _checkport=""
1908 if [ "$_currentRoot" = "$NO_VALUE" ] ; then
1909 _info "Standalone mode."
1910 if [ -z "$Le_HTTPPort" ] ; then
1911 Le_HTTPPort=80
1912 else
1913 _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
1914 fi
1915 _checkport="$Le_HTTPPort"
1916 elif [ "$_currentRoot" = "$W_TLS" ] ; then
1917 _info "Standalone tls mode."
1918 if [ -z "$Le_TLSPort" ] ; then
1919 Le_TLSPort=443
1920 else
1921 _savedomainconf "Le_TLSPort" "$Le_TLSPort"
1922 fi
1923 _checkport="$Le_TLSPort"
1924 fi
1925
1926 if [ "$_checkport" ] ; then
1927 _debug _checkport "$_checkport"
1928 _checkaddr="$(_getfield "$Le_LocalAddress" $_addrIndex)"
1929 _debug _checkaddr "$_checkaddr"
1930
1931 _addrIndex="$(_math $_addrIndex + 1)"
1932
1933 _netprc="$(_ss "$_checkport" | grep "$_checkport")"
1934 netprc="$(echo "$_netprc" | grep "$_checkaddr")"
1935 if [ -z "$netprc" ] ; then
1936 netprc="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
1937 fi
1938 if [ "$netprc" ] ; then
1939 _err "$netprc"
1940 _err "tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
1941 _err "Please stop it first"
1942 return 1
1943 fi
1944 fi
1945 done
1946
1947 if _hasfield "$Le_Webroot" "apache" ; then
1948 if ! _setApache ; then
1949 _err "set up apache error. Report error to me."
1950 return 1
1951 fi
1952 else
1953 usingApache=""
1954 fi
1955
1956 #run pre hook
1957 if [ "$Le_PreHook" ] ; then
1958 _info "Run pre hook:'$Le_PreHook'"
1959 if ! (
1960 cd "$DOMAIN_PATH" && eval "$Le_PreHook"
1961 ) ; then
1962 _err "Error when run pre hook."
1963 return 1
1964 fi
1965 fi
1966 }
1967
1968 _on_issue_err() {
1969 _debug _on_issue_err
1970 if [ "$LOG_FILE" ] ; then
1971 _err "Please check log file for more details: $LOG_FILE"
1972 else
1973 _err "Please use add '--debug' or '--log' to check more details."
1974 _err "See: $_DEBUG_WIKI"
1975 fi
1976
1977 #run the post hook
1978 if [ "$Le_PostHook" ] ; then
1979 _info "Run post hook:'$Le_PostHook'"
1980 if ! (
1981 cd "$DOMAIN_PATH" && eval "$Le_PostHook"
1982 ) ; then
1983 _err "Error when run post hook."
1984 return 1
1985 fi
1986 fi
1987 }
1988
1989 _on_issue_success() {
1990 _debug _on_issue_success
1991 #run the post hook
1992 if [ "$Le_PostHook" ] ; then
1993 _info "Run post hook:'$Le_PostHook'"
1994 if ! (
1995 cd "$DOMAIN_PATH" && eval "$Le_PostHook"
1996 ) ; then
1997 _err "Error when run post hook."
1998 return 1
1999 fi
2000 fi
2001
2002 #run renew hook
2003 if [ "$IS_RENEW" ] && [ "$Le_RenewHook" ] ; then
2004 _info "Run renew hook:'$Le_RenewHook'"
2005 if ! (
2006 cd "$DOMAIN_PATH" && eval "$Le_RenewHook"
2007 ) ; then
2008 _err "Error when run renew hook."
2009 return 1
2010 fi
2011 fi
2012
2013 }
2014
2015 updateaccount() {
2016 _initpath
2017 _regAccount
2018 }
2019
2020 registeraccount() {
2021 _initpath
2022 _regAccount
2023 }
2024
2025 _regAccount() {
2026 _initpath
2027
2028 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
2029 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
2030 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
2031 fi
2032
2033 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
2034 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
2035 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
2036 fi
2037
2038 if [ ! -f "$ACCOUNT_KEY_PATH" ] ; then
2039 _acck="no"
2040 if [ "$Le_Keylength" ] ; then
2041 _acck="$Le_Keylength"
2042 fi
2043 if ! createAccountKey "$_acck" ; then
2044 _err "Create account key error."
2045 return 1
2046 fi
2047 fi
2048
2049 if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
2050 return 1
2051 fi
2052
2053 _updateTos=""
2054 _reg_res="new-reg"
2055 while true ;
2056 do
2057 _debug AGREEMENT "$AGREEMENT"
2058 accountkey_json=$(printf "%s" "$jwk" | tr -d ' ' )
2059 thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _urlencode)
2060
2061 regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}'
2062
2063 if [ "$ACCOUNT_EMAIL" ] ; then
2064 regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
2065 fi
2066
2067 if [ -z "$_updateTos" ] ; then
2068 _info "Registering account"
2069
2070 if ! _send_signed_request "$API/acme/new-reg" "$regjson" ; then
2071 _err "Register account Error: $response"
2072 return 1
2073 fi
2074
2075 if [ "$code" = "" ] || [ "$code" = '201' ] ; then
2076 echo "$response" > $ACCOUNT_JSON_PATH
2077 _info "Registered"
2078 elif [ "$code" = '409' ] ; then
2079 _info "Already registered"
2080 else
2081 _err "Register account Error: $response"
2082 return 1
2083 fi
2084
2085 _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2| tr -d "\r\n")"
2086 _debug "_accUri" "$_accUri"
2087
2088 _tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')"
2089 _debug "_tos" "$_tos"
2090 if [ -z "$_tos" ] ; then
2091 _debug "Use default tos: $DEFAULT_AGREEMENT"
2092 _tos="$DEFAULT_AGREEMENT"
2093 fi
2094 if [ "$_tos" != "$AGREEMENT" ]; then
2095 _updateTos=1
2096 AGREEMENT="$_tos"
2097 _reg_res="reg"
2098 continue
2099 fi
2100
2101 else
2102 _debug "Update tos: $_tos"
2103 if ! _send_signed_request "$_accUri" "$regjson" ; then
2104 _err "Update tos error."
2105 return 1
2106 fi
2107 if [ "$code" = '202' ] ; then
2108 _info "Update success."
2109 else
2110 _err "Update account error."
2111 return 1
2112 fi
2113 fi
2114 return 0
2115 done
2116
2117 }
2118
2119
2120 # domain folder file
2121 _findHook() {
2122 _hookdomain="$1"
2123 _hookcat="$2"
2124 _hookname="$3"
2125
2126 if [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ] ; then
2127 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname"
2128 elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ] ; then
2129 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
2130 elif [ -f "$LE_WORKING_DIR/$_hookname" ] ; then
2131 d_api="$LE_WORKING_DIR/$_hookname"
2132 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ] ; then
2133 d_api="$LE_WORKING_DIR/$_hookname.sh"
2134 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ] ; then
2135 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname"
2136 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ] ; then
2137 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
2138 fi
2139
2140 printf "%s" "$d_api"
2141 }
2142
2143 #domain
2144 __get_domain_new_authz() {
2145 _gdnd="$1"
2146 _info "Getting new-authz for domain" "$_gdnd"
2147
2148 _Max_new_authz_retry_times=5
2149 _authz_i=0
2150 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ] ; do
2151 _info "Try new-authz for the $_authz_i time."
2152 if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}" ; then
2153 _err "Can not get domain new authz."
2154 return 1
2155 fi
2156 if ! _contains "$response" "An error occurred while processing your request" ; then
2157 _info "The new-authz request is ok."
2158 break
2159 fi
2160 _authz_i="$(_math "$_authz_i" + 1)"
2161 _info "The server is busy, Sleep $_authz_i to retry."
2162 _sleep "$_authz_i"
2163 done;
2164
2165 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ] ; then
2166 _debug "new-authz retry reach the max $_Max_new_authz_retry_times times."
2167 fi
2168
2169 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
2170 _err "new-authz error: $response"
2171 return 1
2172 fi
2173
2174 }
2175
2176 #webroot, domain domainlist keylength
2177 issue() {
2178 if [ -z "$2" ] ; then
2179 _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
2180 return 1
2181 fi
2182 Le_Webroot="$1"
2183 Le_Domain="$2"
2184 Le_Alt="$3"
2185 Le_Keylength="$4"
2186 Le_RealCertPath="$5"
2187 Le_RealKeyPath="$6"
2188 Le_RealCACertPath="$7"
2189 Le_ReloadCmd="$8"
2190 Le_RealFullChainPath="$9"
2191 Le_PreHook="${10}"
2192 Le_PostHook="${11}"
2193 Le_RenewHook="${12}"
2194 Le_LocalAddress="${13}"
2195
2196 #remove these later.
2197 if [ "$Le_Webroot" = "dns-cf" ] ; then
2198 Le_Webroot="dns_cf"
2199 fi
2200 if [ "$Le_Webroot" = "dns-dp" ] ; then
2201 Le_Webroot="dns_dp"
2202 fi
2203 if [ "$Le_Webroot" = "dns-cx" ] ; then
2204 Le_Webroot="dns_cx"
2205 fi
2206 _debug "Using api: $API"
2207
2208 if [ ! "$IS_RENEW" ] ; then
2209 _initpath $Le_Domain "$Le_Keylength"
2210 mkdir -p "$DOMAIN_PATH"
2211 fi
2212
2213 if [ -f "$DOMAIN_CONF" ] ; then
2214 Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
2215 _debug Le_NextRenewTime "$Le_NextRenewTime"
2216 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ $(_time) -lt $Le_NextRenewTime ] ; then
2217 _saved_domain=$(_readdomainconf Le_Domain)
2218 _debug _saved_domain "$_saved_domain"
2219 _saved_alt=$(_readdomainconf Le_Alt)
2220 _debug _saved_alt "$_saved_alt"
2221 if [ "$_saved_domain,$_saved_alt" = "$Le_Domain,$Le_Alt" ] ; then
2222 _info "Domains not changed."
2223 _info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
2224 _info "Add '$(__red '--force')' to force to renew."
2225 return $RENEW_SKIP
2226 else
2227 _info "Domains have changed."
2228 fi
2229 fi
2230 fi
2231
2232 _savedomainconf "Le_Domain" "$Le_Domain"
2233 _savedomainconf "Le_Alt" "$Le_Alt"
2234 _savedomainconf "Le_Webroot" "$Le_Webroot"
2235
2236 _savedomainconf "Le_PreHook" "$Le_PreHook"
2237 _savedomainconf "Le_PostHook" "$Le_PostHook"
2238 _savedomainconf "Le_RenewHook" "$Le_RenewHook"
2239 _savedomainconf "Le_LocalAddress" "$Le_LocalAddress"
2240
2241
2242 Le_API="$API"
2243 _savedomainconf "Le_API" "$Le_API"
2244
2245 if [ "$Le_Alt" = "$NO_VALUE" ] ; then
2246 Le_Alt=""
2247 fi
2248
2249 if [ "$Le_Keylength" = "$NO_VALUE" ] ; then
2250 Le_Keylength=""
2251 fi
2252
2253 if ! _on_before_issue ; then
2254 _err "_on_before_issue."
2255 return 1
2256 fi
2257
2258 if ! _regAccount ; then
2259 _on_issue_err
2260 return 1
2261 fi
2262
2263
2264 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ] ; then
2265 _info "Signing from existing CSR."
2266 else
2267 _key=$(_readdomainconf Le_Keylength)
2268 _debug "Read key length:$_key"
2269 if [ ! -f "$CERT_KEY_PATH" ] || [ "$Le_Keylength" != "$_key" ] ; then
2270 if ! createDomainKey $Le_Domain $Le_Keylength ; then
2271 _err "Create domain key error."
2272 _clearup
2273 _on_issue_err
2274 return 1
2275 fi
2276 fi
2277
2278 if ! _createcsr "$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" ; then
2279 _err "Create CSR error."
2280 _clearup
2281 _on_issue_err
2282 return 1
2283 fi
2284 fi
2285
2286 _savedomainconf "Le_Keylength" "$Le_Keylength"
2287
2288 vlist="$Le_Vlist"
2289 # verify each domain
2290 _info "Verify each domain"
2291 sep='#'
2292 if [ -z "$vlist" ] ; then
2293 alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
2294 _index=1
2295 _currentRoot=""
2296 for d in $alldomains
2297 do
2298 _info "Getting webroot for domain" $d
2299 _w="$(echo $Le_Webroot | cut -d , -f $_index)"
2300 _info _w "$_w"
2301 if [ "$_w" ] ; then
2302 _currentRoot="$_w"
2303 fi
2304 _debug "_currentRoot" "$_currentRoot"
2305 _index=$(_math $_index + 1)
2306
2307 vtype="$VTYPE_HTTP"
2308 if _startswith "$_currentRoot" "dns" ; then
2309 vtype="$VTYPE_DNS"
2310 fi
2311
2312 if [ "$_currentRoot" = "$W_TLS" ] ; then
2313 vtype="$VTYPE_TLS"
2314 fi
2315
2316 if ! __get_domain_new_authz "$d" ; then
2317 _clearup
2318 _on_issue_err
2319 return 1
2320 fi
2321
2322 entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
2323 _debug entry "$entry"
2324 if [ -z "$entry" ] ; then
2325 _err "Error, can not get domain token $d"
2326 _clearup
2327 _on_issue_err
2328 return 1
2329 fi
2330 token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
2331 _debug token $token
2332
2333 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
2334 _debug uri $uri
2335
2336 keyauthorization="$token.$thumbprint"
2337 _debug keyauthorization "$keyauthorization"
2338
2339
2340 if printf "$response" | grep '"status":"valid"' >/dev/null 2>&1 ; then
2341 _info "$d is already verified, skip."
2342 keyauthorization=$STATE_VERIFIED
2343 _debug keyauthorization "$keyauthorization"
2344 fi
2345
2346
2347 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
2348 _debug dvlist "$dvlist"
2349
2350 vlist="$vlist$dvlist,"
2351
2352 done
2353
2354 #add entry
2355 dnsadded=""
2356 ventries=$(echo "$vlist" | tr ',' ' ' )
2357 for ventry in $ventries
2358 do
2359 d=$(echo $ventry | cut -d $sep -f 1)
2360 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
2361 vtype=$(echo $ventry | cut -d $sep -f 4)
2362 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
2363
2364 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
2365 _info "$d is already verified, skip $vtype."
2366 continue
2367 fi
2368
2369 if [ "$vtype" = "$VTYPE_DNS" ] ; then
2370 dnsadded='0'
2371 txtdomain="_acme-challenge.$d"
2372 _debug txtdomain "$txtdomain"
2373 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _urlencode)"
2374 _debug txt "$txt"
2375
2376 d_api="$(_findHook $d dnsapi $_currentRoot)"
2377
2378 _debug d_api "$d_api"
2379
2380 if [ "$d_api" ] ; then
2381 _info "Found domain api file: $d_api"
2382 else
2383 _err "Add the following TXT record:"
2384 _err "Domain: '$(__green $txtdomain)'"
2385 _err "TXT value: '$(__green $txt)'"
2386 _err "Please be aware that you prepend _acme-challenge. before your domain"
2387 _err "so the resulting subdomain will be: $txtdomain"
2388 continue
2389 fi
2390
2391 (
2392 if ! . $d_api ; then
2393 _err "Load file $d_api error. Please check your api file and try again."
2394 return 1
2395 fi
2396
2397 addcommand="${_currentRoot}_add"
2398 if ! _exists $addcommand ; then
2399 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
2400 return 1
2401 fi
2402
2403 if ! $addcommand $txtdomain $txt ; then
2404 _err "Error add txt for domain:$txtdomain"
2405 return 1
2406 fi
2407 )
2408
2409 if [ "$?" != "0" ] ; then
2410 _clearup
2411 _on_issue_err
2412 return 1
2413 fi
2414 dnsadded='1'
2415 fi
2416 done
2417
2418 if [ "$dnsadded" = '0' ] ; then
2419 _savedomainconf "Le_Vlist" "$vlist"
2420 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
2421 _err "Please add the TXT records to the domains, and retry again."
2422 _clearup
2423 _on_issue_err
2424 return 1
2425 fi
2426
2427 fi
2428
2429 if [ "$dnsadded" = '1' ] ; then
2430 if [ -z "$Le_DNSSleep" ] ; then
2431 Le_DNSSleep=$DEFAULT_DNS_SLEEP
2432 else
2433 _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
2434 fi
2435
2436 _info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
2437 _sleep $Le_DNSSleep
2438 fi
2439
2440 _debug "ok, let's start to verify"
2441
2442 _ncIndex=1
2443 ventries=$(echo "$vlist" | tr ',' ' ' )
2444 for ventry in $ventries
2445 do
2446 d=$(echo $ventry | cut -d $sep -f 1)
2447 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
2448 uri=$(echo $ventry | cut -d $sep -f 3)
2449 vtype=$(echo $ventry | cut -d $sep -f 4)
2450 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
2451
2452 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
2453 _info "$d is already verified, skip $vtype."
2454 continue
2455 fi
2456
2457 _info "Verifying:$d"
2458 _debug "d" "$d"
2459 _debug "keyauthorization" "$keyauthorization"
2460 _debug "uri" "$uri"
2461 removelevel=""
2462 token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
2463
2464 _debug "_currentRoot" "$_currentRoot"
2465
2466
2467 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
2468 if [ "$_currentRoot" = "$NO_VALUE" ] ; then
2469 _info "Standalone mode server"
2470 _ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex" )"
2471 _ncIndex="$(_math $_ncIndex + 1)"
2472 _startserver "$keyauthorization" "$_ncaddr" &
2473 if [ "$?" != "0" ] ; then
2474 _clearup
2475 _on_issue_err
2476 return 1
2477 fi
2478 serverproc="$!"
2479 sleep 2
2480 _debug serverproc $serverproc
2481
2482 else
2483 if [ "$_currentRoot" = "apache" ] ; then
2484 wellknown_path="$ACME_DIR"
2485 else
2486 wellknown_path="$_currentRoot/.well-known/acme-challenge"
2487 if [ ! -d "$_currentRoot/.well-known" ] ; then
2488 removelevel='1'
2489 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ] ; then
2490 removelevel='2'
2491 else
2492 removelevel='3'
2493 fi
2494 fi
2495
2496 _debug wellknown_path "$wellknown_path"
2497
2498 _debug "writing token:$token to $wellknown_path/$token"
2499
2500 mkdir -p "$wellknown_path"
2501 printf "%s" "$keyauthorization" > "$wellknown_path/$token"
2502 if [ ! "$usingApache" ] ; then
2503 if webroot_owner=$(_stat $_currentRoot) ; then
2504 _debug "Changing owner/group of .well-known to $webroot_owner"
2505 chown -R $webroot_owner "$_currentRoot/.well-known"
2506 else
2507 _debug "not chaning owner/group of webroot";
2508 fi
2509 fi
2510
2511 fi
2512
2513 elif [ "$vtype" = "$VTYPE_TLS" ] ; then
2514 #create A
2515 #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
2516 #_debug2 _hash_A "$_hash_A"
2517 #_x="$(echo $_hash_A | cut -c 1-32)"
2518 #_debug2 _x "$_x"
2519 #_y="$(echo $_hash_A | cut -c 33-64)"
2520 #_debug2 _y "$_y"
2521 #_SAN_A="$_x.$_y.token.acme.invalid"
2522 #_debug2 _SAN_A "$_SAN_A"
2523
2524 #create B
2525 _hash_B="$(printf "%s" $keyauthorization | _digest "sha256" "hex" )"
2526 _debug2 _hash_B "$_hash_B"
2527 _x="$(echo $_hash_B | cut -c 1-32)"
2528 _debug2 _x "$_x"
2529 _y="$(echo $_hash_B | cut -c 33-64)"
2530 _debug2 _y "$_y"
2531
2532 #_SAN_B="$_x.$_y.ka.acme.invalid"
2533
2534 _SAN_B="$_x.$_y.acme.invalid"
2535 _debug2 _SAN_B "$_SAN_B"
2536
2537 _ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex" )"
2538 _ncIndex="$(_math $_ncIndex + 1)"
2539 if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
2540 _err "Start tls server error."
2541 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2542 _clearup
2543 _on_issue_err
2544 return 1
2545 fi
2546 fi
2547
2548 if ! _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then
2549 _err "$d:Can not get challenge: $response"
2550 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2551 _clearup
2552 _on_issue_err
2553 return 1
2554 fi
2555
2556 if [ ! -z "$code" ] && [ ! "$code" = '202' ] ; then
2557 _err "$d:Challenge error: $response"
2558 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2559 _clearup
2560 _on_issue_err
2561 return 1
2562 fi
2563
2564 waittimes=0
2565 if [ -z "$MAX_RETRY_TIMES" ] ; then
2566 MAX_RETRY_TIMES=30
2567 fi
2568
2569 while true ; do
2570 waittimes=$(_math $waittimes + 1)
2571 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ] ; then
2572 _err "$d:Timeout"
2573 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2574 _clearup
2575 _on_issue_err
2576 return 1
2577 fi
2578
2579 _debug "sleep 5 secs to verify"
2580 sleep 5
2581 _debug "checking"
2582 response="$(_get $uri)"
2583 if [ "$?" != "0" ] ; then
2584 _err "$d:Verify error:$response"
2585 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2586 _clearup
2587 _on_issue_err
2588 return 1
2589 fi
2590 _debug2 original "$response"
2591
2592 response="$(echo "$response" | _normalizeJson )"
2593 _debug2 response "$response"
2594
2595 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
2596 if [ "$status" = "valid" ] ; then
2597 _info "Success"
2598 _stopserver $serverproc
2599 serverproc=""
2600 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2601 break;
2602 fi
2603
2604 if [ "$status" = "invalid" ] ; then
2605 error="$(echo "$response" | tr -d "\r\n" | _egrep_o '"error":\{[^\}]*')"
2606 _debug2 error "$error"
2607 errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
2608 _debug2 errordetail "$errordetail"
2609 if [ "$errordetail" ] ; then
2610 _err "$d:Verify error:$errordetail"
2611 else
2612 _err "$d:Verify error:$error"
2613 fi
2614 if [ "$DEBUG" ] ; then
2615 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
2616 _debug "Debug: get token url."
2617 _get "http://$d/.well-known/acme-challenge/$token" "" 1
2618 fi
2619 fi
2620 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2621 _clearup
2622 _on_issue_err
2623 return 1;
2624 fi
2625
2626 if [ "$status" = "pending" ] ; then
2627 _info "Pending"
2628 else
2629 _err "$d:Verify error:$response"
2630 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2631 _clearup
2632 _on_issue_err
2633 return 1
2634 fi
2635
2636 done
2637
2638 done
2639
2640 _clearup
2641 _info "Verify finished, start to sign."
2642 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
2643
2644 if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" ; then
2645 _err "Sign failed."
2646 _on_issue_err
2647 return 1
2648 fi
2649
2650 _rcert="$response"
2651 Le_LinkCert="$(grep -i '^Location.*$' $HTTP_HEADER | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
2652 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
2653
2654 if [ "$Le_LinkCert" ] ; then
2655 echo "$BEGIN_CERT" > "$CERT_PATH"
2656
2657 if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
2658 _debug "Get cert failed. Let's try last response."
2659 printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
2660 fi
2661
2662 echo "$END_CERT" >> "$CERT_PATH"
2663 _info "$(__green "Cert success.")"
2664 cat "$CERT_PATH"
2665
2666 _info "Your cert is in $( __green " $CERT_PATH ")"
2667
2668 if [ -f "$CERT_KEY_PATH" ] ; then
2669 _info "Your cert key is in $( __green " $CERT_KEY_PATH ")"
2670 fi
2671
2672 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
2673
2674 if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ] ; then
2675 USER_PATH="$PATH"
2676 _saveaccountconf "USER_PATH" "$USER_PATH"
2677 fi
2678 fi
2679
2680
2681 if [ -z "$Le_LinkCert" ] ; then
2682 response="$(echo $response | _dbase64 "multiline" | _normalizeJson )"
2683 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
2684 _on_issue_err
2685 return 1
2686 fi
2687
2688 _cleardomainconf "Le_Vlist"
2689
2690 Le_LinkIssuer=$(grep -i '^Link' $HTTP_HEADER | _head_n 1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
2691 if ! _contains "$Le_LinkIssuer" ":" ; then
2692 Le_LinkIssuer="$API$Le_LinkIssuer"
2693 fi
2694
2695 _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
2696
2697 if [ "$Le_LinkIssuer" ] ; then
2698 echo "$BEGIN_CERT" > "$CA_CERT_PATH"
2699 _get "$Le_LinkIssuer" | _base64 "multiline" >> "$CA_CERT_PATH"
2700 echo "$END_CERT" >> "$CA_CERT_PATH"
2701 _info "The intermediate CA cert is in $( __green " $CA_CERT_PATH ")"
2702 cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
2703 _info "And the full chain certs is there: $( __green " $CERT_FULLCHAIN_PATH ")"
2704 fi
2705
2706 Le_CertCreateTime=$(_time)
2707 _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
2708
2709 Le_CertCreateTimeStr=$(date -u )
2710 _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
2711
2712 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ] ; then
2713 Le_RenewalDays=$MAX_RENEW
2714 else
2715 _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
2716 fi
2717
2718 if [ "$CA_BUNDLE" ] ; then
2719 _saveaccountconf CA_BUNDLE "$CA_BUNDLE"
2720 else
2721 _clearaccountconf "CA_BUNDLE"
2722 fi
2723
2724 if [ "$HTTPS_INSECURE" ] ; then
2725 _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
2726 else
2727 _clearaccountconf "HTTPS_INSECURE"
2728 fi
2729
2730 if [ "$Le_Listen_V4" ] ; then
2731 _savedomainconf "Le_Listen_V4" "$Le_Listen_V4"
2732 _cleardomainconf Le_Listen_V6
2733 elif [ "$Le_Listen_V6" ] ; then
2734 _savedomainconf "Le_Listen_V6" "$Le_Listen_V6"
2735 _cleardomainconf Le_Listen_V4
2736 fi
2737
2738 Le_NextRenewTime=$(_math $Le_CertCreateTime + $Le_RenewalDays \* 24 \* 60 \* 60)
2739
2740
2741 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
2742 _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
2743
2744 Le_NextRenewTime=$(_math $Le_NextRenewTime - 86400)
2745 _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
2746
2747
2748 _on_issue_success
2749
2750 if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ] ; then
2751 _installcert
2752 fi
2753
2754 }
2755
2756 #domain [isEcc]
2757 renew() {
2758 Le_Domain="$1"
2759 if [ -z "$Le_Domain" ] ; then
2760 _usage "Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
2761 return 1
2762 fi
2763
2764 _isEcc="$2"
2765
2766 _initpath $Le_Domain "$_isEcc"
2767
2768 _info "$(__green "Renew: '$Le_Domain'")"
2769 if [ ! -f "$DOMAIN_CONF" ] ; then
2770 _info "'$Le_Domain' is not a issued domain, skip."
2771 return 0;
2772 fi
2773
2774 if [ "$Le_RenewalDays" ] ; then
2775 _savedomainconf Le_RenewalDays "$Le_RenewalDays"
2776 fi
2777
2778 . "$DOMAIN_CONF"
2779
2780 if [ "$Le_API" ] ; then
2781 API="$Le_API"
2782 fi
2783
2784 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ] ; then
2785 _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
2786 _info "Add '$(__red '--force')' to force to renew."
2787 return $RENEW_SKIP
2788 fi
2789
2790 IS_RENEW="1"
2791 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"
2792 res=$?
2793 if [ "$res" != "0" ] ; then
2794 return $res
2795 fi
2796
2797 if [ "$Le_DeployHook" ] ; then
2798 deploy $Le_Domain "$Le_DeployHook" "$Le_Keylength"
2799 res=$?
2800 fi
2801
2802 IS_RENEW=""
2803
2804 return $res
2805 }
2806
2807 #renewAll [stopRenewOnError]
2808 renewAll() {
2809 _initpath
2810 _stopRenewOnError="$1"
2811 _debug "_stopRenewOnError" "$_stopRenewOnError"
2812 _ret="0"
2813
2814 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
2815 d=$(echo $d | cut -d '/' -f 1)
2816 (
2817 if _endswith $d "$ECC_SUFFIX" ; then
2818 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2819 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2820 fi
2821 renew "$d" "$_isEcc"
2822 )
2823 rc="$?"
2824 _debug "Return code: $rc"
2825 if [ "$rc" != "0" ] ; then
2826 if [ "$rc" = "$RENEW_SKIP" ] ; then
2827 _info "Skipped $d"
2828 elif [ "$_stopRenewOnError" ] ; then
2829 _err "Error renew $d, stop now."
2830 return $rc
2831 else
2832 _ret="$rc"
2833 _err "Error renew $d, Go ahead to next one."
2834 fi
2835 fi
2836 done
2837 return $_ret
2838 }
2839
2840
2841 #csr webroot
2842 signcsr(){
2843 _csrfile="$1"
2844 _csrW="$2"
2845 if [ -z "$_csrfile" ] || [ -z "$_csrW" ]; then
2846 _usage "Usage: $PROJECT_ENTRY --signcsr --csr mycsr.csr -w /path/to/webroot/a.com/ "
2847 return 1
2848 fi
2849
2850 _initpath
2851
2852 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
2853 if [ "$?" != "0" ] ; then
2854 _err "Can not read subject from csr: $_csrfile"
2855 return 1
2856 fi
2857 _debug _csrsubj "$_csrsubj"
2858
2859 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
2860 if [ "$?" != "0" ] ; then
2861 _err "Can not read domain list from csr: $_csrfile"
2862 return 1
2863 fi
2864 _debug "_csrdomainlist" "$_csrdomainlist"
2865
2866
2867 if [ -z "$_csrsubj" ] ; then
2868 _csrsubj="$(_getfield "$_csrdomainlist" 1)"
2869 _debug _csrsubj "$_csrsubj"
2870 _csrdomainlist="$(echo "$_csrdomainlist" | cut -d , -f 2-)"
2871 _debug "_csrdomainlist" "$_csrdomainlist"
2872 fi
2873
2874 if [ -z "$_csrsubj" ] ; then
2875 _err "Can not read subject from csr: $_csrfile"
2876 return 1
2877 fi
2878
2879 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
2880 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ] ; then
2881 _err "Can not read key length from csr: $_csrfile"
2882 return 1
2883 fi
2884
2885 _initpath "$_csrsubj" "$_csrkeylength"
2886 mkdir -p "$DOMAIN_PATH"
2887
2888 _info "Copy csr to: $CSR_PATH"
2889 cp "$_csrfile" "$CSR_PATH"
2890
2891 issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength"
2892
2893 }
2894
2895 showcsr() {
2896 _csrfile="$1"
2897 _csrd="$2"
2898 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
2899 _usage "Usage: $PROJECT_ENTRY --showcsr --csr mycsr.csr"
2900 return 1
2901 fi
2902
2903 _initpath
2904
2905 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
2906 if [ "$?" != "0" ] || [ -z "$_csrsubj" ] ; then
2907 _err "Can not read subject from csr: $_csrfile"
2908 return 1
2909 fi
2910
2911 _info "Subject=$_csrsubj"
2912
2913 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
2914 if [ "$?" != "0" ] ; then
2915 _err "Can not read domain list from csr: $_csrfile"
2916 return 1
2917 fi
2918 _debug "_csrdomainlist" "$_csrdomainlist"
2919
2920 _info "SubjectAltNames=$_csrdomainlist"
2921
2922
2923 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
2924 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ] ; then
2925 _err "Can not read key length from csr: $_csrfile"
2926 return 1
2927 fi
2928 _info "KeyLength=$_csrkeylength"
2929 }
2930
2931 list() {
2932 _raw="$1"
2933 _initpath
2934
2935 _sep="|"
2936 if [ "$_raw" ] ; then
2937 printf "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew\n"
2938 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
2939 d=$(echo $d | cut -d '/' -f 1)
2940 (
2941 if _endswith $d "$ECC_SUFFIX" ; then
2942 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2943 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2944 fi
2945 _initpath $d "$_isEcc"
2946 if [ -f "$DOMAIN_CONF" ] ; then
2947 . "$DOMAIN_CONF"
2948 printf "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
2949 fi
2950 )
2951 done
2952 else
2953 if _exists column ; then
2954 list "raw" | column -t -s "$_sep"
2955 else
2956 list "raw" | tr "$_sep" '\t'
2957 fi
2958 fi
2959
2960
2961 }
2962
2963 deploy() {
2964 Le_Domain="$1"
2965 Le_DeployHook="$2"
2966 _isEcc="$3"
2967 if [ -z "$Le_DeployHook" ] ; then
2968 _usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
2969 return 1
2970 fi
2971
2972 _initpath $Le_Domain "$_isEcc"
2973 if [ ! -d "$DOMAIN_PATH" ] ; then
2974 _err "Domain is not valid:'$Le_Domain'"
2975 return 1
2976 fi
2977
2978 _deployApi="$(_findHook $Le_Domain deploy $Le_DeployHook)"
2979 if [ -z "$_deployApi" ] ; then
2980 _err "The deploy hook $Le_DeployHook is not found."
2981 return 1
2982 fi
2983 _debug _deployApi "$_deployApi"
2984
2985 _savedomainconf Le_DeployHook "$Le_DeployHook"
2986
2987 if ! (
2988 if ! . $_deployApi ; then
2989 _err "Load file $_deployApi error. Please check your api file and try again."
2990 return 1
2991 fi
2992
2993 d_command="${Le_DeployHook}_deploy"
2994 if ! _exists $d_command ; then
2995 _err "It seems that your api file is not correct, it must have a function named: $d_command"
2996 return 1
2997 fi
2998
2999 if ! $d_command $Le_Domain "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH" ; then
3000 _err "Error deploy for domain:$Le_Domain"
3001 _on_issue_err
3002 return 1
3003 fi
3004 ) ; then
3005 _err "Deploy error."
3006 return 1
3007 else
3008 _info "$(__green Success)"
3009 fi
3010
3011 }
3012
3013 installcert() {
3014 Le_Domain="$1"
3015 if [ -z "$Le_Domain" ] ; then
3016 _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
3017 return 1
3018 fi
3019
3020 Le_RealCertPath="$2"
3021 Le_RealKeyPath="$3"
3022 Le_RealCACertPath="$4"
3023 Le_ReloadCmd="$5"
3024 Le_RealFullChainPath="$6"
3025 _isEcc="$7"
3026
3027 _initpath $Le_Domain "$_isEcc"
3028 if [ ! -d "$DOMAIN_PATH" ] ; then
3029 _err "Domain is not valid:'$Le_Domain'"
3030 return 1
3031 fi
3032
3033 _installcert
3034 }
3035
3036
3037 _installcert() {
3038
3039 _savedomainconf "Le_RealCertPath" "$Le_RealCertPath"
3040 _savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath"
3041 _savedomainconf "Le_RealKeyPath" "$Le_RealKeyPath"
3042 _savedomainconf "Le_ReloadCmd" "$Le_ReloadCmd"
3043 _savedomainconf "Le_RealFullChainPath" "$Le_RealFullChainPath"
3044
3045 if [ "$Le_RealCertPath" = "$NO_VALUE" ] ; then
3046 Le_RealCertPath=""
3047 fi
3048 if [ "$Le_RealKeyPath" = "$NO_VALUE" ] ; then
3049 Le_RealKeyPath=""
3050 fi
3051 if [ "$Le_RealCACertPath" = "$NO_VALUE" ] ; then
3052 Le_RealCACertPath=""
3053 fi
3054 if [ "$Le_ReloadCmd" = "$NO_VALUE" ] ; then
3055 Le_ReloadCmd=""
3056 fi
3057 if [ "$Le_RealFullChainPath" = "$NO_VALUE" ] ; then
3058 Le_RealFullChainPath=""
3059 fi
3060
3061 _installed="0"
3062 if [ "$Le_RealCertPath" ] ; then
3063 _installed=1
3064 _info "Installing cert to:$Le_RealCertPath"
3065 if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ] ; then
3066 cp "$Le_RealCertPath" "$Le_RealCertPath".bak
3067 fi
3068 cat "$CERT_PATH" > "$Le_RealCertPath"
3069 fi
3070
3071 if [ "$Le_RealCACertPath" ] ; then
3072 _installed=1
3073 _info "Installing CA to:$Le_RealCACertPath"
3074 if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ] ; then
3075 echo "" >> "$Le_RealCACertPath"
3076 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
3077 else
3078 if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ] ; then
3079 cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
3080 fi
3081 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
3082 fi
3083 fi
3084
3085
3086 if [ "$Le_RealKeyPath" ] ; then
3087 _installed=1
3088 _info "Installing key to:$Le_RealKeyPath"
3089 if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ] ; then
3090 cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
3091 fi
3092 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
3093 fi
3094
3095 if [ "$Le_RealFullChainPath" ] ; then
3096 _installed=1
3097 _info "Installing full chain to:$Le_RealFullChainPath"
3098 if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ] ; then
3099 cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
3100 fi
3101 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
3102 fi
3103
3104 if [ "$Le_ReloadCmd" ] ; then
3105 _installed=1
3106 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
3107 if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") ; then
3108 _info "$(__green "Reload success")"
3109 else
3110 _err "Reload error for :$Le_Domain"
3111 fi
3112 fi
3113
3114
3115 }
3116
3117 installcronjob() {
3118 _initpath
3119 if ! _exists "crontab" ; then
3120 _err "crontab doesn't exist, so, we can not install cron jobs."
3121 _err "All your certs will not be renewed automatically."
3122 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
3123 return 1
3124 fi
3125
3126 _info "Installing cron job"
3127 if ! crontab -l | grep "$PROJECT_ENTRY --cron" ; then
3128 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ] ; then
3129 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
3130 else
3131 _err "Can not install cronjob, $PROJECT_ENTRY not found."
3132 return 1
3133 fi
3134 if _exists uname && uname -a | grep solaris >/dev/null ; then
3135 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab --
3136 else
3137 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab -
3138 fi
3139 fi
3140 if [ "$?" != "0" ] ; then
3141 _err "Install cron job failed. You need to manually renew your certs."
3142 _err "Or you can add cronjob by yourself:"
3143 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
3144 return 1
3145 fi
3146 }
3147
3148 uninstallcronjob() {
3149 if ! _exists "crontab" ; then
3150 return
3151 fi
3152 _info "Removing cron job"
3153 cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")"
3154 if [ "$cr" ] ; then
3155 if _exists uname && uname -a | grep solaris >/dev/null ; then
3156 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab --
3157 else
3158 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -
3159 fi
3160 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
3161 _info LE_WORKING_DIR "$LE_WORKING_DIR"
3162 fi
3163 _initpath
3164
3165 }
3166
3167 revoke() {
3168 Le_Domain="$1"
3169 if [ -z "$Le_Domain" ] ; then
3170 _usage "Usage: $PROJECT_ENTRY --revoke -d domain.com"
3171 return 1
3172 fi
3173
3174 _isEcc="$2"
3175
3176 _initpath $Le_Domain "$_isEcc"
3177 if [ ! -f "$DOMAIN_CONF" ] ; then
3178 _err "$Le_Domain is not a issued domain, skip."
3179 return 1;
3180 fi
3181
3182 if [ ! -f "$CERT_PATH" ] ; then
3183 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
3184 return 1
3185 fi
3186
3187 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)"
3188
3189 if [ -z "$cert" ] ; then
3190 _err "Cert for $Le_Domain is empty found, skip."
3191 return 1
3192 fi
3193
3194 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
3195 uri="$API/acme/revoke-cert"
3196
3197 _info "Try domain key first."
3198 if _send_signed_request $uri "$data" "" "$CERT_KEY_PATH"; then
3199 if [ -z "$response" ] ; then
3200 _info "Revoke success."
3201 rm -f $CERT_PATH
3202 return 0
3203 else
3204 _err "Revoke error by domain key."
3205 _err "$response"
3206 fi
3207 fi
3208
3209 _info "Then try account key."
3210
3211 if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
3212 if [ -z "$response" ] ; then
3213 _info "Revoke success."
3214 rm -f $CERT_PATH
3215 return 0
3216 else
3217 _err "Revoke error."
3218 _debug "$response"
3219 fi
3220 fi
3221 return 1
3222 }
3223
3224
3225 #domain vtype
3226 _deactivate() {
3227 _d_domain="$1"
3228 _d_type="$2"
3229 _initpath
3230
3231 _d_i=0
3232 _d_max_retry=9
3233 while [ "$_d_i" -lt "$_d_max_retry" ] ;
3234 do
3235 _info "Deactivate: $_d_domain"
3236 _d_i="$(_math $_d_i + 1)"
3237
3238
3239 if ! __get_domain_new_authz "$_d_domain" ; then
3240 _err "Can not get domain new authz token."
3241 return 1
3242 fi
3243
3244 authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
3245 _debug "authzUri" "$authzUri"
3246
3247 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
3248 _err "new-authz error: $response"
3249 return 1
3250 fi
3251
3252 entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"status":"valid","uri"[^\}]*')"
3253 _debug entry "$entry"
3254
3255 if [ -z "$entry" ] ; then
3256 _info "No more valid entry found."
3257 break
3258 fi
3259
3260 _vtype="$(printf "%s\n" "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')"
3261 _debug _vtype $_vtype
3262 _info "Found $_vtype"
3263
3264
3265 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
3266 _debug uri $uri
3267
3268 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ] ; then
3269 _info "Skip $_vtype"
3270 continue
3271 fi
3272
3273 _info "Deactivate: $_vtype"
3274
3275 if ! _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" ; then
3276 _err "Can not deactivate $_vtype."
3277 return 1
3278 fi
3279
3280 _info "Deactivate: $_vtype success."
3281
3282 done
3283 _debug "$_d_i"
3284 if [ "$_d_i" -lt "$_d_max_retry" ] ; then
3285 _info "Deactivated success!"
3286 else
3287 _err "Deactivate failed."
3288 fi
3289
3290 }
3291
3292 deactivate() {
3293 _d_domain_list="$1"
3294 _d_type="$2"
3295 _initpath
3296 _debug _d_domain_list "$_d_domain_list"
3297 if [ -z "$(echo $_d_domain_list | cut -d , -f 1 )" ] ; then
3298 _usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
3299 return 1
3300 fi
3301 for _d_dm in $(echo "$_d_domain_list" | tr ',' ' ' ) ;
3302 do
3303 if [ -z "$_d_dm" ] || [ "$_d_dm" = "$NO_VALUE" ] ; then
3304 continue
3305 fi
3306 if ! _deactivate "$_d_dm" $_d_type ; then
3307 return 1
3308 fi
3309 done
3310 }
3311
3312 # Detect profile file if not specified as environment variable
3313 _detect_profile() {
3314 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
3315 echo "$PROFILE"
3316 return
3317 fi
3318
3319 DETECTED_PROFILE=''
3320 SHELLTYPE="$(basename "/$SHELL")"
3321
3322 if [ "$SHELLTYPE" = "bash" ] ; then
3323 if [ -f "$HOME/.bashrc" ] ; then
3324 DETECTED_PROFILE="$HOME/.bashrc"
3325 elif [ -f "$HOME/.bash_profile" ] ; then
3326 DETECTED_PROFILE="$HOME/.bash_profile"
3327 fi
3328 elif [ "$SHELLTYPE" = "zsh" ] ; then
3329 DETECTED_PROFILE="$HOME/.zshrc"
3330 fi
3331
3332 if [ -z "$DETECTED_PROFILE" ] ; then
3333 if [ -f "$HOME/.profile" ] ; then
3334 DETECTED_PROFILE="$HOME/.profile"
3335 elif [ -f "$HOME/.bashrc" ] ; then
3336 DETECTED_PROFILE="$HOME/.bashrc"
3337 elif [ -f "$HOME/.bash_profile" ] ; then
3338 DETECTED_PROFILE="$HOME/.bash_profile"
3339 elif [ -f "$HOME/.zshrc" ] ; then
3340 DETECTED_PROFILE="$HOME/.zshrc"
3341 fi
3342 fi
3343
3344 if [ ! -z "$DETECTED_PROFILE" ] ; then
3345 echo "$DETECTED_PROFILE"
3346 fi
3347 }
3348
3349 _initconf() {
3350 _initpath
3351 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
3352 echo "#ACCOUNT_CONF_PATH=xxxx
3353
3354 #Account configurations:
3355 #Here are the supported macros, uncomment them to make them take effect.
3356
3357 #ACCOUNT_EMAIL=aaa@example.com # the account email used to register account.
3358 #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
3359 #CERT_HOME=\"/path/to/cert/home\"
3360
3361
3362
3363 #LOG_FILE=\"$DEFAULT_LOG_FILE\"
3364 #LOG_LEVEL=1
3365
3366 #AUTO_UPGRADE=\"1\"
3367
3368 #STAGE=1 # Use the staging api
3369 #FORCE=1 # Force to issue cert
3370 #DEBUG=1 # Debug mode
3371
3372
3373 #USER_AGENT=\"$USER_AGENT\"
3374
3375 #USER_PATH=""
3376
3377 #dns api
3378 #######################
3379 #Cloudflare:
3380 #api key
3381 #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
3382 #account email
3383 #CF_Email=\"xxxx@sss.com\"
3384
3385 #######################
3386 #Dnspod.cn:
3387 #api key id
3388 #DP_Id=\"1234\"
3389 #api key
3390 #DP_Key=\"sADDsdasdgdsf\"
3391
3392 #######################
3393 #Cloudxns.com:
3394 #CX_Key=\"1234\"
3395 #
3396 #CX_Secret=\"sADDsdasdgdsf\"
3397
3398 #######################
3399 #Godaddy.com:
3400 #GD_Key=\"sdfdsgdgdfdasfds\"
3401 #
3402 #GD_Secret=\"sADDsdasdfsdfdssdgdsf\"
3403
3404 #######################
3405 #PowerDNS:
3406 #PDNS_Url=\"http://ns.example.com:8081\"
3407 #PDNS_ServerId=\"localhost\"
3408 #PDNS_Token=\"0123456789ABCDEF\"
3409 #PDNS_Ttl=60
3410
3411 " > $ACCOUNT_CONF_PATH
3412 fi
3413 }
3414
3415 # nocron
3416 _precheck() {
3417 _nocron="$1"
3418
3419 if ! _exists "curl" && ! _exists "wget"; then
3420 _err "Please install curl or wget first, we need to access http resources."
3421 return 1
3422 fi
3423
3424 if [ -z "$_nocron" ] ; then
3425 if ! _exists "crontab" ; then
3426 _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
3427 _err "We need to set cron job to renew the certs automatically."
3428 _err "Otherwise, your certs will not be able to be renewed automatically."
3429 if [ -z "$FORCE" ] ; then
3430 _err "Please add '--force' and try install again to go without crontab."
3431 _err "./$PROJECT_ENTRY --install --force"
3432 return 1
3433 fi
3434 fi
3435 fi
3436
3437 if ! _exists "openssl" ; then
3438 _err "Please install openssl first."
3439 _err "We need openssl to generate keys."
3440 return 1
3441 fi
3442
3443 if ! _exists "nc" ; then
3444 _err "It is recommended to install nc first, try to install 'nc' or 'netcat'."
3445 _err "We use nc for standalone server if you use standalone mode."
3446 _err "If you don't use standalone mode, just ignore this warning."
3447 fi
3448
3449 return 0
3450 }
3451
3452 _setShebang() {
3453 _file="$1"
3454 _shebang="$2"
3455 if [ -z "$_shebang" ] ; then
3456 _usage "Usage: file shebang"
3457 return 1
3458 fi
3459 cp "$_file" "$_file.tmp"
3460 echo "$_shebang" > "$_file"
3461 sed -n 2,99999p "$_file.tmp" >> "$_file"
3462 rm -f "$_file.tmp"
3463 }
3464
3465 _installalias() {
3466 _initpath
3467
3468 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
3469 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ] ; then
3470 echo "$(cat $_envfile)" | sed "s|^LE_WORKING_DIR.*$||" > "$_envfile"
3471 echo "$(cat $_envfile)" | sed "s|^alias le.*$||" > "$_envfile"
3472 echo "$(cat $_envfile)" | sed "s|^alias le.sh.*$||" > "$_envfile"
3473 fi
3474
3475 _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
3476 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3477
3478 _profile="$(_detect_profile)"
3479 if [ "$_profile" ] ; then
3480 _debug "Found profile: $_profile"
3481 _info "Installing alias to '$_profile'"
3482 _setopt "$_profile" ". \"$_envfile\""
3483 _info "OK, Close and reopen your terminal to start using $PROJECT_NAME"
3484 else
3485 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
3486 fi
3487
3488
3489 #for csh
3490 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
3491 _csh_profile="$HOME/.cshrc"
3492 if [ -f "$_csh_profile" ] ; then
3493 _info "Installing alias to '$_csh_profile'"
3494 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
3495 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3496 _setopt "$_csh_profile" "source \"$_cshfile\""
3497 fi
3498
3499 #for tcsh
3500 _tcsh_profile="$HOME/.tcshrc"
3501 if [ -f "$_tcsh_profile" ] ; then
3502 _info "Installing alias to '$_tcsh_profile'"
3503 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
3504 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3505 _setopt "$_tcsh_profile" "source \"$_cshfile\""
3506 fi
3507
3508 }
3509
3510 # nocron
3511 install() {
3512
3513 if [ -z "$LE_WORKING_DIR" ] ; then
3514 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
3515 fi
3516
3517 _nocron="$1"
3518 if ! _initpath ; then
3519 _err "Install failed."
3520 return 1
3521 fi
3522 if [ "$_nocron" ] ; then
3523 _debug "Skip install cron job"
3524 fi
3525
3526 if ! _precheck "$_nocron" ; then
3527 _err "Pre-check failed, can not install."
3528 return 1
3529 fi
3530
3531 #convert from le
3532 if [ -d "$HOME/.le" ] ; then
3533 for envfile in "le.env" "le.sh.env"
3534 do
3535 if [ -f "$HOME/.le/$envfile" ] ; then
3536 if grep "le.sh" "$HOME/.le/$envfile" >/dev/null ; then
3537 _upgrading="1"
3538 _info "You are upgrading from le.sh"
3539 _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
3540 mv "$HOME/.le" "$LE_WORKING_DIR"
3541 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
3542 break;
3543 fi
3544 fi
3545 done
3546 fi
3547
3548 _info "Installing to $LE_WORKING_DIR"
3549
3550 if ! mkdir -p "$LE_WORKING_DIR" ; then
3551 _err "Can not create working dir: $LE_WORKING_DIR"
3552 return 1
3553 fi
3554
3555 chmod 700 "$LE_WORKING_DIR"
3556
3557 cp $PROJECT_ENTRY "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
3558
3559 if [ "$?" != "0" ] ; then
3560 _err "Install failed, can not copy $PROJECT_ENTRY"
3561 return 1
3562 fi
3563
3564 _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
3565
3566 _installalias
3567
3568 for subf in $_SUB_FOLDERS ; do
3569 if [ -d "$subf" ] ; then
3570 mkdir -p $LE_WORKING_DIR/$subf
3571 cp $subf/* $LE_WORKING_DIR/$subf/
3572 fi
3573 done
3574
3575
3576 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
3577 _initconf
3578 fi
3579
3580 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ] ; then
3581 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
3582 fi
3583
3584 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ] ; then
3585 _saveaccountconf "CERT_HOME" "$CERT_HOME"
3586 fi
3587
3588 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ] ; then
3589 _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
3590 fi
3591
3592 if [ -z "$_nocron" ] ; then
3593 installcronjob
3594 fi
3595
3596 if [ -z "$NO_DETECT_SH" ] ; then
3597 #Modify shebang
3598 if _exists bash ; then
3599 _info "Good, bash is found, so change the shebang to use bash as prefered."
3600 _shebang='#!/usr/bin/env bash'
3601 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
3602 for subf in $_SUB_FOLDERS ; do
3603 if [ -d "$LE_WORKING_DIR/$subf" ] ; then
3604 for _apifile in "$LE_WORKING_DIR/$subf/"*.sh ; do
3605 _setShebang "$_apifile" "$_shebang"
3606 done
3607 fi
3608 done
3609 fi
3610 fi
3611
3612 _info OK
3613 }
3614
3615 # nocron
3616 uninstall() {
3617 _nocron="$1"
3618 if [ -z "$_nocron" ] ; then
3619 uninstallcronjob
3620 fi
3621 _initpath
3622
3623 _uninstallalias
3624
3625 rm -f $LE_WORKING_DIR/$PROJECT_ENTRY
3626 _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
3627
3628 }
3629
3630 _uninstallalias() {
3631 _initpath
3632
3633 _profile="$(_detect_profile)"
3634 if [ "$_profile" ] ; then
3635 _info "Uninstalling alias from: '$_profile'"
3636 text="$(cat $_profile)"
3637 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" > "$_profile"
3638 fi
3639
3640 _csh_profile="$HOME/.cshrc"
3641 if [ -f "$_csh_profile" ] ; then
3642 _info "Uninstalling alias from: '$_csh_profile'"
3643 text="$(cat $_csh_profile)"
3644 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_csh_profile"
3645 fi
3646
3647 _tcsh_profile="$HOME/.tcshrc"
3648 if [ -f "$_tcsh_profile" ] ; then
3649 _info "Uninstalling alias from: '$_csh_profile'"
3650 text="$(cat $_tcsh_profile)"
3651 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_tcsh_profile"
3652 fi
3653
3654 }
3655
3656 cron() {
3657 IN_CRON=1
3658 _initpath
3659 if [ "$AUTO_UPGRADE" = "1" ] ; then
3660 export LE_WORKING_DIR
3661 (
3662 if ! upgrade ; then
3663 _err "Cron:Upgrade failed!"
3664 return 1
3665 fi
3666 )
3667 . $LE_WORKING_DIR/$PROJECT_ENTRY >/dev/null
3668
3669 if [ -t 1 ] ; then
3670 __INTERACTIVE="1"
3671 fi
3672
3673 _info "Auto upgraded to: $VER"
3674 fi
3675 renewAll
3676 _ret="$?"
3677 IN_CRON=""
3678 exit $_ret
3679 }
3680
3681 version() {
3682 echo "$PROJECT"
3683 echo "v$VER"
3684 }
3685
3686 showhelp() {
3687 _initpath
3688 version
3689 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
3690 Commands:
3691 --help, -h Show this help message.
3692 --version, -v Show version info.
3693 --install Install $PROJECT_NAME to your system.
3694 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
3695 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT .
3696 --issue Issue a cert.
3697 --signcsr Issue a cert from an existing csr.
3698 --deploy Deploy the cert to your server.
3699 --installcert Install the issued cert to apache/nginx or any other server.
3700 --renew, -r Renew a cert.
3701 --renewAll Renew all the certs.
3702 --revoke Revoke a cert.
3703 --list List all the certs.
3704 --showcsr Show the content of a csr.
3705 --installcronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
3706 --uninstallcronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
3707 --cron Run cron job to renew all the certs.
3708 --toPkcs Export the certificate and key to a pfx file.
3709 --updateaccount Update account info.
3710 --registeraccount Register account key.
3711 --createAccountKey, -cak Create an account private key, professional use.
3712 --createDomainKey, -cdk Create an domain private key, professional use.
3713 --createCSR, -ccsr Create CSR , professional use.
3714 --deactivate Deactivate the domain authz, professional use.
3715
3716 Parameters:
3717 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
3718 --force, -f Used to force to install or force to renew a cert immediately.
3719 --staging, --test Use staging server, just for test.
3720 --debug Output debug info.
3721
3722 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
3723 --standalone Use standalone mode.
3724 --tls Use standalone tls mode.
3725 --apache Use apache mode.
3726 --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
3727 --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.
3728
3729 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
3730 --accountkeylength, -ak [2048] Specifies the account key length.
3731 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
3732 --log-level 1|2 Specifies the log level, default is 1.
3733
3734 These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
3735
3736 --certpath /path/to/real/cert/file After issue/renew, the cert will be copied to this path.
3737 --keypath /path/to/real/key/file After issue/renew, the key will be copied to this path.
3738 --capath /path/to/real/ca/file After issue/renew, the intermediate cert will be copied to this path.
3739 --fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path.
3740
3741 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
3742
3743 --accountconf Specifies a customized account config file.
3744 --home Specifies the home dir for $PROJECT_NAME .
3745 --certhome Specifies the home dir to save all the certs, only valid for '--install' command.
3746 --useragent Specifies the user agent string. it will be saved for future use too.
3747 --accountemail Specifies the account email for registering, Only valid for the '--install' command.
3748 --accountkey Specifies the account key path, Only valid for the '--install' command.
3749 --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
3750 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
3751 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
3752 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
3753 --listraw Only used for '--list' command, list the certs in raw format.
3754 --stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal.
3755 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
3756 --ca-bundle Specifices the path to the CA certificate bundle to verify api server's certificate.
3757 --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.
3758 --ecc Specifies to use the ECC cert. Valid for '--installcert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
3759 --csr Specifies the input csr.
3760 --pre-hook Command to be run before obtaining any certificates.
3761 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obain/renew is success or failed.
3762 --renew-hook Command to be run once for each successfully renewed certificate.
3763 --deploy-hook The hook file to deploy cert
3764 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
3765 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
3766 --listen-v4 Force standalone/tls server to listen at ipv4.
3767 --listen-v6 Force standalone/tls server to listen at ipv6.
3768 "
3769 }
3770
3771 # nocron
3772 _installOnline() {
3773 _info "Installing from online archive."
3774 _nocron="$1"
3775 if [ ! "$BRANCH" ] ; then
3776 BRANCH="master"
3777 fi
3778
3779 target="$PROJECT/archive/$BRANCH.tar.gz"
3780 _info "Downloading $target"
3781 localname="$BRANCH.tar.gz"
3782 if ! _get "$target" > $localname ; then
3783 _err "Download error."
3784 return 1
3785 fi
3786 (
3787 _info "Extracting $localname"
3788 tar xzf $localname
3789
3790 cd "$PROJECT_NAME-$BRANCH"
3791 chmod +x $PROJECT_ENTRY
3792 if ./$PROJECT_ENTRY install "$_nocron" ; then
3793 _info "Install success!"
3794 fi
3795
3796 cd ..
3797
3798 rm -rf "$PROJECT_NAME-$BRANCH"
3799 rm -f "$localname"
3800 )
3801 }
3802
3803 upgrade() {
3804 if (
3805 _initpath
3806 export LE_WORKING_DIR
3807 cd "$LE_WORKING_DIR"
3808 _installOnline "nocron"
3809 ) ; then
3810 _info "Upgrade success!"
3811 exit 0
3812 else
3813 _err "Upgrade failed!"
3814 exit 1
3815 fi
3816 }
3817
3818 _processAccountConf() {
3819 if [ "$_useragent" ] ; then
3820 _saveaccountconf "USER_AGENT" "$_useragent"
3821 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ] ; then
3822 _saveaccountconf "USER_AGENT" "$USER_AGENT"
3823 fi
3824
3825 if [ "$_accountemail" ] ; then
3826 _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
3827 elif [ "$ACCOUNT_EMAIL" ] && [ "$ACCOUNT_EMAIL" != "$DEFAULT_ACCOUNT_EMAIL" ] ; then
3828 _saveaccountconf "ACCOUNT_EMAIL" "$ACCOUNT_EMAIL"
3829 fi
3830
3831 if [ "$_auto_upgrade" ] ; then
3832 _saveaccountconf "AUTO_UPGRADE" "$_auto_upgrade"
3833 elif [ "$AUTO_UPGRADE" ] ; then
3834 _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
3835 fi
3836
3837 }
3838
3839 _process() {
3840 _CMD=""
3841 _domain=""
3842 _altdomains="$NO_VALUE"
3843 _webroot=""
3844 _keylength=""
3845 _accountkeylength=""
3846 _certpath=""
3847 _keypath=""
3848 _capath=""
3849 _fullchainpath=""
3850 _reloadcmd=""
3851 _password=""
3852 _accountconf=""
3853 _useragent=""
3854 _accountemail=""
3855 _accountkey=""
3856 _certhome=""
3857 _httpport=""
3858 _tlsport=""
3859 _dnssleep=""
3860 _listraw=""
3861 _stopRenewOnError=""
3862 _insecure=""
3863 _ca_bundle=""
3864 _nocron=""
3865 _ecc=""
3866 _csr=""
3867 _pre_hook=""
3868 _post_hook=""
3869 _renew_hook=""
3870 _deploy_hook=""
3871 _logfile=""
3872 _log=""
3873 _local_address=""
3874 _log_level=""
3875 _auto_upgrade=""
3876 _listen_v4=""
3877 _listen_v6=""
3878 while [ ${#} -gt 0 ] ; do
3879 case "${1}" in
3880
3881 --help|-h)
3882 showhelp
3883 return
3884 ;;
3885 --version|-v)
3886 version
3887 return
3888 ;;
3889 --install)
3890 _CMD="install"
3891 ;;
3892 --uninstall)
3893 _CMD="uninstall"
3894 ;;
3895 --upgrade)
3896 _CMD="upgrade"
3897 ;;
3898 --issue)
3899 _CMD="issue"
3900 ;;
3901 --deploy)
3902 _CMD="deploy"
3903 ;;
3904 --signcsr)
3905 _CMD="signcsr"
3906 ;;
3907 --showcsr)
3908 _CMD="showcsr"
3909 ;;
3910 --installcert|-i)
3911 _CMD="installcert"
3912 ;;
3913 --renew|-r)
3914 _CMD="renew"
3915 ;;
3916 --renewAll|--renewall)
3917 _CMD="renewAll"
3918 ;;
3919 --revoke)
3920 _CMD="revoke"
3921 ;;
3922 --list)
3923 _CMD="list"
3924 ;;
3925 --installcronjob)
3926 _CMD="installcronjob"
3927 ;;
3928 --uninstallcronjob)
3929 _CMD="uninstallcronjob"
3930 ;;
3931 --cron)
3932 _CMD="cron"
3933 ;;
3934 --toPkcs)
3935 _CMD="toPkcs"
3936 ;;
3937 --createAccountKey|--createaccountkey|-cak)
3938 _CMD="createAccountKey"
3939 ;;
3940 --createDomainKey|--createdomainkey|-cdk)
3941 _CMD="createDomainKey"
3942 ;;
3943 --createCSR|--createcsr|-ccr)
3944 _CMD="createCSR"
3945 ;;
3946 --deactivate)
3947 _CMD="deactivate"
3948 ;;
3949 --updateaccount)
3950 _CMD="updateaccount"
3951 ;;
3952 --registeraccount)
3953 _CMD="registeraccount"
3954 ;;
3955 --domain|-d)
3956 _dvalue="$2"
3957
3958 if [ "$_dvalue" ] ; then
3959 if _startswith "$_dvalue" "-" ; then
3960 _err "'$_dvalue' is not a valid domain for parameter '$1'"
3961 return 1
3962 fi
3963 if _is_idn "$_dvalue" && ! _exists idn ; then
3964 _err "It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first."
3965 return 1
3966 fi
3967
3968 if [ -z "$_domain" ] ; then
3969 _domain="$_dvalue"
3970 else
3971 if [ "$_altdomains" = "$NO_VALUE" ] ; then
3972 _altdomains="$_dvalue"
3973 else
3974 _altdomains="$_altdomains,$_dvalue"
3975 fi
3976 fi
3977 fi
3978
3979 shift
3980 ;;
3981
3982 --force|-f)
3983 FORCE="1"
3984 ;;
3985 --staging|--test)
3986 STAGE="1"
3987 ;;
3988 --debug)
3989 if [ -z "$2" ] || _startswith "$2" "-" ; then
3990 DEBUG="1"
3991 else
3992 DEBUG="$2"
3993 shift
3994 fi
3995 ;;
3996 --webroot|-w)
3997 wvalue="$2"
3998 if [ -z "$_webroot" ] ; then
3999 _webroot="$wvalue"
4000 else
4001 _webroot="$_webroot,$wvalue"
4002 fi
4003 shift
4004 ;;
4005 --standalone)
4006 wvalue="$NO_VALUE"
4007 if [ -z "$_webroot" ] ; then
4008 _webroot="$wvalue"
4009 else
4010 _webroot="$_webroot,$wvalue"
4011 fi
4012 ;;
4013 --local-address)
4014 lvalue="$2"
4015 _local_address="$_local_address$lvalue,"
4016 shift
4017 ;;
4018 --apache)
4019 wvalue="apache"
4020 if [ -z "$_webroot" ] ; then
4021 _webroot="$wvalue"
4022 else
4023 _webroot="$_webroot,$wvalue"
4024 fi
4025 ;;
4026 --tls)
4027 wvalue="$W_TLS"
4028 if [ -z "$_webroot" ] ; then
4029 _webroot="$wvalue"
4030 else
4031 _webroot="$_webroot,$wvalue"
4032 fi
4033 ;;
4034 --dns)
4035 wvalue="dns"
4036 if ! _startswith "$2" "-" ; then
4037 wvalue="$2"
4038 shift
4039 fi
4040 if [ -z "$_webroot" ] ; then
4041 _webroot="$wvalue"
4042 else
4043 _webroot="$_webroot,$wvalue"
4044 fi
4045 ;;
4046 --dnssleep)
4047 _dnssleep="$2"
4048 Le_DNSSleep="$_dnssleep"
4049 shift
4050 ;;
4051
4052 --keylength|-k)
4053 _keylength="$2"
4054 if [ "$_accountkeylength" = "$NO_VALUE" ] ; then
4055 _accountkeylength="$2"
4056 fi
4057 shift
4058 ;;
4059 --accountkeylength|-ak)
4060 _accountkeylength="$2"
4061 shift
4062 ;;
4063
4064 --certpath)
4065 _certpath="$2"
4066 shift
4067 ;;
4068 --keypath)
4069 _keypath="$2"
4070 shift
4071 ;;
4072 --capath)
4073 _capath="$2"
4074 shift
4075 ;;
4076 --fullchainpath)
4077 _fullchainpath="$2"
4078 shift
4079 ;;
4080 --reloadcmd|--reloadCmd)
4081 _reloadcmd="$2"
4082 shift
4083 ;;
4084 --password)
4085 _password="$2"
4086 shift
4087 ;;
4088 --accountconf)
4089 _accountconf="$2"
4090 ACCOUNT_CONF_PATH="$_accountconf"
4091 shift
4092 ;;
4093 --home)
4094 LE_WORKING_DIR="$2"
4095 shift
4096 ;;
4097 --certhome)
4098 _certhome="$2"
4099 CERT_HOME="$_certhome"
4100 shift
4101 ;;
4102 --useragent)
4103 _useragent="$2"
4104 USER_AGENT="$_useragent"
4105 shift
4106 ;;
4107 --accountemail )
4108 _accountemail="$2"
4109 ACCOUNT_EMAIL="$_accountemail"
4110 shift
4111 ;;
4112 --accountkey )
4113 _accountkey="$2"
4114 ACCOUNT_KEY_PATH="$_accountkey"
4115 shift
4116 ;;
4117 --days )
4118 _days="$2"
4119 Le_RenewalDays="$_days"
4120 shift
4121 ;;
4122 --httpport )
4123 _httpport="$2"
4124 Le_HTTPPort="$_httpport"
4125 shift
4126 ;;
4127 --tlsport )
4128 _tlsport="$2"
4129 Le_TLSPort="$_tlsport"
4130 shift
4131 ;;
4132
4133 --listraw )
4134 _listraw="raw"
4135 ;;
4136 --stopRenewOnError|--stoprenewonerror|-se )
4137 _stopRenewOnError="1"
4138 ;;
4139 --insecure)
4140 _insecure="1"
4141 HTTPS_INSECURE="1"
4142 ;;
4143 --ca-bundle)
4144 _ca_bundle="$(readlink -f $2)"
4145 CA_BUNDLE="$_ca_bundle"
4146 shift
4147 ;;
4148 --nocron)
4149 _nocron="1"
4150 ;;
4151 --ecc)
4152 _ecc="isEcc"
4153 ;;
4154 --csr)
4155 _csr="$2"
4156 shift
4157 ;;
4158 --pre-hook)
4159 _pre_hook="$2"
4160 shift
4161 ;;
4162 --post-hook)
4163 _post_hook="$2"
4164 shift
4165 ;;
4166 --renew-hook)
4167 _renew_hook="$2"
4168 shift
4169 ;;
4170 --deploy-hook)
4171 _deploy_hook="$2"
4172 shift
4173 ;;
4174 --ocsp-must-staple|--ocsp)
4175 Le_OCSP_Stable="1"
4176 ;;
4177 --log|--logfile)
4178 _log="1"
4179 _logfile="$2"
4180 if _startswith "$_logfile" '-' ; then
4181 _logfile=""
4182 else
4183 shift
4184 fi
4185 LOG_FILE="$_logfile"
4186 if [ -z "$LOG_LEVEL" ] ; then
4187 LOG_LEVEL="$DEFAULT_LOG_LEVEL"
4188 fi
4189 ;;
4190 --log-level)
4191 _log_level="$2"
4192 LOG_LEVEL="$_log_level"
4193 shift
4194 ;;
4195 --auto-upgrade)
4196 _auto_upgrade="$2"
4197 if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-' ; then
4198 _auto_upgrade="1"
4199 else
4200 shift
4201 fi
4202 AUTO_UPGRADE="$_auto_upgrade"
4203 ;;
4204 --listen-v4)
4205 _listen_v4="1"
4206 Le_Listen_V4="$_listen_v4"
4207 ;;
4208 --listen-v6)
4209 _listen_v6="1"
4210 Le_Listen_V6="$_listen_v6"
4211 ;;
4212
4213 *)
4214 _err "Unknown parameter : $1"
4215 return 1
4216 ;;
4217 esac
4218
4219 shift 1
4220 done
4221
4222 if [ "${_CMD}" != "install" ] ; then
4223 __initHome
4224 if [ "$_log" ]; then
4225 if [ -z "$_logfile" ] ; then
4226 _logfile="$DEFAULT_LOG_FILE"
4227 fi
4228 fi
4229 if [ "$_logfile" ] ; then
4230 _saveaccountconf "LOG_FILE" "$_logfile"
4231 LOG_FILE="$_logfile"
4232 fi
4233
4234 if [ "$_log_level" ] ; then
4235 _saveaccountconf "LOG_LEVEL" "$_log_level"
4236 LOG_LEVEL="$_log_level"
4237 fi
4238
4239 _processAccountConf
4240 fi
4241
4242 if [ "$DEBUG" ] ; then
4243 version
4244 fi
4245
4246 case "${_CMD}" in
4247 install) install "$_nocron" ;;
4248 uninstall) uninstall "$_nocron" ;;
4249 upgrade) upgrade ;;
4250 issue)
4251 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address"
4252 ;;
4253 deploy)
4254 deploy "$_domain" "$_deploy_hook" "$_ecc"
4255 ;;
4256 signcsr)
4257 signcsr "$_csr" "$_webroot"
4258 ;;
4259 showcsr)
4260 showcsr "$_csr" "$_domain"
4261 ;;
4262 installcert)
4263 installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc"
4264 ;;
4265 renew)
4266 renew "$_domain" "$_ecc"
4267 ;;
4268 renewAll)
4269 renewAll "$_stopRenewOnError"
4270 ;;
4271 revoke)
4272 revoke "$_domain" "$_ecc"
4273 ;;
4274 deactivate)
4275 deactivate "$_domain,$_altdomains"
4276 ;;
4277 registeraccount)
4278 registeraccount
4279 ;;
4280 updateaccount)
4281 updateaccount
4282 ;;
4283 list)
4284 list "$_listraw"
4285 ;;
4286 installcronjob) installcronjob ;;
4287 uninstallcronjob) uninstallcronjob ;;
4288 cron) cron ;;
4289 toPkcs)
4290 toPkcs "$_domain" "$_password" "$_ecc"
4291 ;;
4292 createAccountKey)
4293 createAccountKey "$_accountkeylength"
4294 ;;
4295 createDomainKey)
4296 createDomainKey "$_domain" "$_keylength"
4297 ;;
4298 createCSR)
4299 createCSR "$_domain" "$_altdomains" "$_ecc"
4300 ;;
4301
4302 *)
4303 _err "Invalid command: $_CMD"
4304 showhelp;
4305 return 1
4306 ;;
4307 esac
4308 _ret="$?"
4309 if [ "$_ret" != "0" ] ; then
4310 return $_ret
4311 fi
4312
4313 if [ "${_CMD}" = "install" ] ; then
4314 if [ "$_log" ] ; then
4315 if [ -z "$LOG_FILE" ] ; then
4316 LOG_FILE="$DEFAULT_LOG_FILE"
4317 fi
4318 _saveaccountconf "LOG_FILE" "$LOG_FILE"
4319 fi
4320
4321 if [ "$_log_level" ] ; then
4322 _saveaccountconf "LOG_LEVEL" "$_log_level"
4323 fi
4324 _processAccountConf
4325 fi
4326
4327 }
4328
4329
4330 if [ "$INSTALLONLINE" ] ; then
4331 INSTALLONLINE=""
4332 _installOnline $BRANCH
4333 exit
4334 fi
4335
4336
4337
4338
4339
4340 main() {
4341 [ -z "$1" ] && showhelp && return
4342 if _startswith "$1" '-' ; then _process "$@"; else "$@";fi
4343 }
4344
4345
4346 main "$@"
4347
4348
4349