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