7 PROJECT_ENTRY
="acme.sh"
9 PROJECT
="https://github.com/Neilpang/$PROJECT_NAME"
11 DEFAULT_INSTALL_HOME
="$HOME/.$PROJECT_NAME"
14 _SUB_FOLDERS
="dnsapi deploy"
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"
19 DEFAULT_USER_AGENT
="$PROJECT_ENTRY client v$VER : $PROJECT"
20 DEFAULT_ACCOUNT_EMAIL
=""
22 STAGE_CA
="https://acme-staging.api.letsencrypt.org"
26 VTYPE_TLS
="tls-sni-01"
27 VTYPE_TLS2
="tls-sni-02"
29 LOCAL_ANY_ADDRESS
="0.0.0.0"
39 STATE_VERIFIED
="verified_ok"
41 BEGIN_CSR
="-----BEGIN CERTIFICATE REQUEST-----"
42 END_CSR
="-----END CERTIFICATE REQUEST-----"
44 BEGIN_CERT
="-----BEGIN CERTIFICATE-----"
45 END_CERT
="-----END CERTIFICATE-----"
50 ECC_SUFFIX
="${ECC_SEP}ecc"
55 DEFAULT_LOG_LEVEL
="$LOG_LEVEL_1"
57 _DEBUG_WIKI
="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh"
65 if [ "$__INTERACTIVE" ] ; then
66 printf '\033[1;31;32m'
69 if [ "$__INTERACTIVE" ] ; then
75 if [ "$__INTERACTIVE" ] ; then
76 printf '\033[1;31;40m'
79 if [ "$__INTERACTIVE" ] ; then
87 printf -- "[$(date)] $1"
89 printf -- "[$(date)] $1='$2'"
96 [ -z "$LOG_FILE" ] && return
97 _printargs
"$@" >> $LOG_FILE
108 printf -- "[$(date)] " >&2
109 if [ -z "$2" ] ; then
125 if [ -z "$LOG_LEVEL" ] ||
[ "$LOG_LEVEL" -ge "$LOG_LEVEL_1" ] ; then
128 if [ -z "$DEBUG" ] ; then
135 if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_2" ] ; then
138 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
144 if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_3" ] ; then
147 if [ "$DEBUG" ] && [ "$DEBUG" -ge "3" ] ; then
155 echo "$_str" |
grep "^$_sub" >/dev
/null
2>&1
161 echo "$_str" |
grep -- "$_sub\$" >/dev
/null
2>&1
167 echo "$_str" |
grep -- "$_sub" >/dev
/null
2>&1
174 if [ -z "$_field" ] ; then
175 _usage
"Usage: str field [sep]"
179 if [ -z "$_sep" ] ; then
183 for f
in $
(echo "$_str" |
tr ',' ' ') ; do
184 if [ "$f" = "$_field" ] ; then
185 _debug2
"'$_str' contains '$_field'"
186 return 0 #contains ok
189 _debug2
"'$_str' does not contain '$_field'"
190 return 1 #not contains
198 if [ -z "$_findex" ] ; then
199 _usage
"Usage: str field [sep]"
203 if [ -z "$_sep" ] ; then
208 while [ "$_ffi" -gt "0" ]
210 _fv
="$(echo "$_str" | cut -d $_sep -f $_ffi)"
212 printf -- "%s" "$_fv"
215 _ffi
="$(_math $_ffi - 1)"
218 printf -- "%s" "$_str"
224 if [ -z "$cmd" ] ; then
225 _usage
"Usage: _exists cmd"
228 if type command >/dev
/null
2>&1 ; then
229 command -v "$cmd" >/dev
/null
2>&1
231 type "$cmd" >/dev
/null
2>&1
234 _debug3
"$cmd exists=$ret"
273 if [ "$(printf '\x41')" != 'A' ] ; then
281 if _exists
let ; then
284 _debug3 uselet
"$uselet"
285 _debug3 _URGLY_PRINTF
"$_URGLY_PRINTF"
287 if [ -z "$_URGLY_PRINTF" ] ; then
288 h
="$(printf $hex | cut -c $i-$j)"
289 if [ -z "$h" ] ; then
294 ic
="$(printf $hex | cut -c $i)"
295 jc
="$(printf $hex | cut -c $j)"
296 if [ -z "$ic$jc" ] ; then
299 ic
="$(_h_char_2_dec "$ic")"
300 jc
="$(_h_char_2_dec "$jc")"
301 printf '\'"$(printf %o "$(_math $ic \* 16 + $jc)")"
303 if [ "$uselet" ] ; then
304 let "i+=2" >/dev/null
305 let "j+=2" >/dev/null
317 if [ -z "$filename" ] ; then
318 _usage "Usage:_sed_i options filename"
321 _debug2 options "$options"
322 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
323 _debug "Using sed -i"
324 sed -i "$options" "$filename"
326 _debug "No -i support in sed"
327 text="$(cat "$filename")"
328 echo "$text" | sed "$options" > "$filename"
333 if _contains "$(egrep -o 2>&1)" "egrep: illegal option -- o" ; then
334 sed -n 's
/.
*\
('"$1"'\
).
*/\
1/p
'
340 #Usage: file startline endline
345 if [ -z "$endline" ] ; then
346 _usage "Usage: file startline endline"
350 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
351 if [ -z "$i" ] ; then
352 _err "Can not find start line: $startline"
355 i="$(_math "$i" + 1)"
358 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
359 if [ -z "$j" ] ; then
360 _err "Can not find end line: $endline"
363 j="$(_math "$j" - 1)"
366 sed -n "$i,${j}p" "$filename"
375 openssl base64 -e | tr -d '\r\n'
388 #Usage: hashalg [outputhex]
389 #Output Base64-encoded digest
392 if [ -z "$alg" ] ; then
393 _usage "Usage: _digest hashalg"
399 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
400 if [ "$outputhex" ] ; then
401 openssl dgst -$alg -hex | cut -d = -f 2 | tr -d ' '
403 openssl dgst -$alg -binary | _base64
406 _err "$alg is not supported yet"
412 #Usage: keyfile hashalg
413 #Output: Base64-encoded signature value
417 if [ -z "$alg" ] ; then
418 _usage "Usage: _sign keyfile hashalg"
422 _sign_openssl="openssl dgst -sign $keyfile "
423 if [ "$alg" = "sha256" ] ; then
424 _sign_openssl="$_sign_openssl -$alg"
426 _err "$alg is not supported yet"
430 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
431 $_sign_openssl | _base64
432 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
433 _signedECText="$($_sign_openssl | openssl asn1parse -inform DER)"
434 _debug3 "_signedECText" "$_signedECText"
435 _ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
436 _debug3 "_ec_r" "$_ec_r"
437 _ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
438 _debug3 "_ec_s" "$_ec_s"
439 printf "%s" "$_ec_r$_ec_s" | _h2b | _base64
441 _err "Unknown key file format."
451 if [ -z "$_length" ] ;then
455 [ "$_length" != "1024" ] \
456 && [ "$_length" != "2048" ] \
457 && [ "$_length" != "3072" ] \
458 && [ "$_length" != "4096" ] \
459 && [ "$_length" != "8192" ]
462 # _createkey 2048|ec-256 file
467 if _startswith "$length" "ec-" ; then
468 length=$(printf $length | cut -d '-' -f 2-100)
470 if [ "$length" = "256" ] ; then
473 if [ "$length" = "384" ] ; then
476 if [ "$length" = "521" ] ; then
482 if [ -z "$length" ] ; then
486 _debug "Use length $length"
488 if _isEccKey "$length" ; then
489 _debug "Using ec name: $eccname"
490 openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
492 _debug "Using RSA: $length"
493 openssl genrsa $length 2>/dev/null > "$f"
496 if [ "$?" != "0" ] ; then
497 _err "Create key error."
506 _debug2 _is_idn_d "$_is_idn_d"
507 _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d "[0-9a-zA-Z.,-]")
508 _debug2 _idn_temp "$_idn_temp"
513 #aa.com,bb.com,cc.com
516 if ! _is_idn "$__idn_d" ; then
517 printf "%s" "$__idn_d"
521 if _exists idn ; then
522 if _contains "$__idn_d" ',' ; then
524 for f in $(echo "$__idn_d" | tr ',' ' ') ; do
525 [ -z "$f" ] && continue
526 if [ -z "$_i_first" ] ; then
531 idn "$f" | tr -d "\r\n"
534 idn "$__idn_d" | tr -d "\r\n"
537 _err "Please install idn to process IDN names."
541 #_createcsr cn san_list keyfile csrfile conf
549 _debug2 domain "$domain"
550 _debug2 domainlist "$domainlist"
551 _debug2 csrkey "$csrkey"
553 _debug2 csrconf "$csrconf"
555 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" > "$csrconf"
557 if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
559 _info "Single domain" "$domain"
561 domainlist="$(_idn $domainlist)"
562 _debug2 domainlist "$domainlist"
563 if _contains "$domainlist" "," ; then
564 alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
566 alt="DNS:$domainlist"
569 _info "Multi domain" "$alt"
570 printf -- "\nsubjectAltName=$alt" >> "$csrconf"
572 if [ "$Le_OCSP_Stable" ] ; then
573 _savedomainconf Le_OCSP_Stable "$Le_OCSP_Stable"
574 printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >> "$csrconf"
577 _csr_cn="$(_idn "$domain")"
578 _debug2 _csr_cn "$_csr_cn"
579 openssl req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
582 #_signcsr key csr conf cert
590 _msg="$(openssl x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
597 _readSubjectFromCSR() {
599 if [ -z "$_csrfile" ] ; then
600 _usage "_readSubjectFromCSR mycsr.csr"
603 openssl req -noout -in "$_csrfile" -subject | _egrep_o "CN=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n'
607 #echo comma separated domain list
608 _readSubjectAltNamesFromCSR() {
610 if [ -z "$_csrfile" ] ; then
611 _usage "_readSubjectAltNamesFromCSR mycsr.csr"
615 _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
616 _debug _csrsubj "$_csrsubj"
618 _dnsAltnames="$(openssl req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
619 _debug _dnsAltnames "$_dnsAltnames"
621 if _contains "$_dnsAltnames," "DNS:$_csrsubj," ; then
622 _debug "AltNames contains subject"
623 _dnsAltnames="$(printf "%s" "$_dnsAltnames," | sed "s/DNS:$_csrsubj,//g")"
625 _debug "AltNames doesn't contain subject
"
628 printf "%s
" "$_dnsAltnames" | sed "s
/DNS
://g
"
632 _readKeyLengthFromCSR() {
634 if [ -z "$_csrfile" ] ; then
635 _usage "_readKeyLengthFromCSR mycsr.csr
"
639 _outcsr="$
(openssl req
-noout -text -in "$_csrfile")"
640 if _contains "$_outcsr" "Public Key Algorithm
: id-ecPublicKey
" ; then
642 echo "$_outcsr" | _egrep_o "^
*ASN1 OID
:.
*" | cut -d ':' -f 2 | tr -d ' '
645 echo "$_outcsr" | _egrep_o "^
*Public-Key
:.
*" | cut -d '(' -f 2 | cut -d ' ' -f 1
653 if _exists "ss
" ; then
655 ss -ntpl | grep ":$_port "
659 if _exists "netstat
" ; then
660 _debug "Using
: netstat
"
661 if netstat -h 2>&1 | grep "\
-p proto
" >/dev/null ; then
662 #for windows version netstat tool
663 netstat -an -p tcp | grep "LISTENING
" | grep ":$_port "
665 if netstat -help 2>&1 | grep "\
-p protocol
" >/dev/null ; then
666 netstat -an -p tcp | grep LISTEN | grep ":$_port "
667 elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null ; then
669 netstat -an -P tcp | grep "\.
$_port " | grep "LISTEN
"
671 netstat -ntpl | grep ":$_port "
680 #domain [password] [isEcc]
684 if [ -z "$domain" ] ; then
685 _usage "Usage
: $PROJECT_ENTRY --toPkcs -d domain
[--password pfx-password
]"
691 _initpath "$domain" "$_isEcc"
693 if [ "$pfxPassword" ] ; then
694 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass
:$pfxPassword"
696 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
699 if [ "$?
" = "0" ] ; then
700 _info "Success
, Pfx is exported to
: $CERT_PFX_PATH"
707 _info "Creating account key
"
708 if [ -z "$1" ] ; then
709 _usage "Usage
: $PROJECT_ENTRY --createAccountKey --accountkeylength 2048"
715 if [ -z "$length" ] || [ "$length" = "$NO_VALUE" ] ; then
716 _debug "Use default length
2048"
719 _debug length "$length"
722 if [ -f "$ACCOUNT_KEY_PATH" ] ; then
723 _info "Account key exists
, skip
"
726 #generate account key
727 _createkey "$length" "$ACCOUNT_KEY_PATH"
734 _info "Creating domain key
"
735 if [ -z "$1" ] ; then
736 _usage "Usage
: $PROJECT_ENTRY --createDomainKey -d domain.com
[ --keylength 2048 ]"
743 _initpath $domain "$length"
745 if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
746 _createkey "$length" "$CERT_KEY_PATH"
748 if [ "$IS_RENEW" ] ; then
749 _info "Domain key exists
, skip
"
752 _err "Domain key exists
, do you want to overwrite the key?
"
753 _err "Add
'--force', and try again.
"
760 # domain domainlist isEcc
763 if [ -z "$1" ] ; then
764 _usage "Usage
: $PROJECT_ENTRY --createCSR -d domain1.com
[-d domain2.com
-d domain3.com ...
]"
772 _initpath "$domain" "$_isEcc"
774 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
775 _info "CSR exists
, skip
"
779 if [ ! -f "$CERT_KEY_PATH" ] ; then
780 _err "The key
file is not found
: $CERT_KEY_PATH"
781 _err "Please create the key
file first.
"
784 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
790 echo $__n | tr '/+' '_-' | tr -d '= '
795 if date -u -d@$1 2>/dev/null ; then
800 if date -u -r $1 2>/dev/null ; then
805 if _exists adb ; then
806 echo $(echo "0t
${1}=Y
" | adb)
812 sed "s
/\" *: *\
([\"{\
[]\
)/\":\
1/g
" | sed "s
/^
*\
([^
]\
)/\
1/" | tr -d "\r\n"
817 if stat -c '%U:%G' "$1" 2>/dev/null ; then
822 if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
826 return 1; #error, 'stat' not found
832 if [ -z "$keyfile" ] ; then
833 _usage "Usage
: _calcjwk keyfile
"
837 if grep "BEGIN RSA PRIVATE KEY
" "$keyfile" > /dev/null 2>&1 ; then
839 pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent
:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
840 if [ "${#pub_exp}" = "5" ] ; then
843 _debug3 pub_exp "$pub_exp"
845 e=$(echo $pub_exp | _h2b | _base64)
848 modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 )
849 _debug3 modulus "$modulus"
850 n="$
(printf "%s" "$modulus"| _h2b | _base64 | _urlencode
)"
851 jwk='{"e
": "'$e'", "kty
": "RSA
", "n
": "'$n'"}'
854 JWK_HEADER='{"alg
": "RS256
", "jwk
": '$jwk'}'
855 JWK_HEADERPLACE_PART1='{"nonce
": "'
856 JWK_HEADERPLACE_PART2='", "alg
": "RS256
", "jwk
": '$jwk'}'
857 elif grep "BEGIN EC PRIVATE KEY
" "$keyfile" > /dev/null 2>&1 ; then
860 crv="$
(openssl ec
-in $keyfile -noout -text 2>/dev
/null |
grep "^NIST CURVE:" | cut
-d ":" -f 2 |
tr -d " \r\n")"
863 pubi="$
(openssl ec
-in $keyfile -noout -text 2>/dev
/null |
grep -n pub
: | cut
-d : -f 1)"
864 pubi=$(_math $pubi + 1)
867 pubj="$
(openssl ec
-in $keyfile -noout -text 2>/dev
/null |
grep -n "ASN1 OID:" | cut
-d : -f 1)"
868 pubj=$(_math $pubj - 1)
871 pubtext="$
(openssl ec
-in $keyfile -noout -text 2>/dev
/null |
sed -n "$pubi,${pubj}p" |
tr -d " \n\r")"
872 _debug3 pubtext "$pubtext"
874 xlen="$
(printf "$pubtext" |
tr -d ':' |
wc -c)"
875 xlen=$(_math $xlen / 4)
878 xend=$(_math "$xlen" + 1)
879 x="$
(printf $pubtext | cut
-d : -f 2-$xend)"
882 x64="$
(printf $x |
tr -d : | _h2b | _base64 | _urlencode
)"
885 xend=$(_math "$xend" + 1)
886 y="$
(printf $pubtext | cut
-d : -f $xend-10000)"
889 y64="$
(printf $y |
tr -d : | _h2b | _base64 | _urlencode
)"
892 jwk='{"kty
": "EC
", "crv
": "'$crv'", "x
": "'$x64'", "y
": "'$y64'"}'
895 JWK_HEADER='{"alg
": "ES256
", "jwk
": '$jwk'}'
896 JWK_HEADERPLACE_PART1='{"nonce
": "'
897 JWK_HEADERPLACE_PART2='", "alg
": "ES256
", "jwk
": '$jwk'}'
899 _err "Only RSA or EC key is supported.
"
903 _debug3 JWK_HEADER "$JWK_HEADER"
911 if _exists mktemp ; then
912 if mktemp 2>/dev/null ; then
914 elif _contains "$
(mktemp
2>&1)" "-t prefix
" && mktemp -t "$PROJECT_NAME" 2>/dev/null ; then
919 if [ -d "/tmp
" ] ; then
920 echo "/tmp
/${PROJECT_NAME}wefADf24sf.$
(_time
).tmp
"
923 _err "Can not create temp
file.
"
928 if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER" ; then
929 HTTP_HEADER="$
(_mktemp
)"
930 _debug2 HTTP_HEADER "$HTTP_HEADER"
933 if [ -z "$CURL" ] ; then
934 CURL="curl
-L --silent --dump-header $HTTP_HEADER "
935 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
936 _CURL_DUMP="$
(_mktemp
)"
937 CURL="$CURL --trace-ascii $_CURL_DUMP "
940 if [ "$CA_BUNDLE" ] ; then
941 CURL="$CURL --cacert $CA_BUNDLE "
944 if [ "$HTTPS_INSECURE" ] ; then
945 CURL="$CURL --insecure "
949 if [ -z "$WGET" ] ; then
951 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
954 if [ "$CA_BUNDLE" ] ; then
955 WGET="$WGET --ca-certificate $CA_BUNDLE "
957 if [ "$HTTPS_INSECURE" ] ; then
958 WGET="$WGET --no-check-certificate "
965 # body url [needbase64] [POST|PUT]
972 if [ -z "$httpmethod" ] ; then
977 _debug2 "body
" "$body"
981 if _exists "curl
" ; then
983 _debug "_CURL
" "$_CURL"
984 if [ "$needbase64" ] ; then
985 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64
)"
987 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" )"
990 if [ "$_ret" != "0" ] ; then
991 _err "Please refer to https
://curl.haxx.se
/libcurl
/c
/libcurl-errors.html
for error code
: $_ret"
992 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
993 _err "Here is the curl dump log
:"
994 _err "$
(cat "$_CURL_DUMP")"
997 elif _exists "wget
" ; then
998 _debug "WGET
" "$WGET"
999 if [ "$needbase64" ] ; then
1000 if [ "$httpmethod" = "POST
" ] ; then
1001 response="$
($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64
)"
1003 response="$
($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64
)"
1006 if [ "$httpmethod" = "POST
" ] ; then
1007 response="$
($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")"
1009 response="$
($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")"
1013 if [ "$_ret" = "8" ] ; then
1015 _debug "wget returns
8, the server returns a
'Bad request' respons
, lets process the response later.
"
1017 if [ "$_ret" != "0" ] ; then
1018 _err "Please refer to https
://www.gnu.org
/software
/wget
/manual
/html_node
/Exit-Status.html
for error code
: $_ret"
1020 _sed_i "s
/^
*//g
" "$HTTP_HEADER"
1023 _err "Neither curl nor wget is found
, can not
do $httpmethod.
"
1025 _debug "_ret
" "$_ret"
1026 printf "%s
" "$response"
1031 # url getheader timeout
1038 _debug "timeout
" "$t"
1042 if _exists "curl
" ; then
1045 _CURL="$_CURL --connect-timeout $t"
1047 _debug "_CURL
" "$_CURL"
1048 if [ "$onlyheader" ] ; then
1049 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" $url
1051 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" $url
1054 if [ "$ret" != "0" ] ; then
1055 _err "Please refer to https
://curl.haxx.se
/libcurl
/c
/libcurl-errors.html
for error code
: $ret"
1056 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
1057 _err "Here is the curl dump log
:"
1058 _err "$
(cat "$_CURL_DUMP")"
1061 elif _exists "wget
" ; then
1064 _WGET="$_WGET --timeout=$t"
1066 _debug "_WGET
" "$_WGET"
1067 if [ "$onlyheader" ] ; then
1068 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null $url 2>&1 | sed 's/^[ ]*//g'
1070 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - $url
1073 if [ "$_ret" = "8" ] ; then
1075 _debug "wget returns
8, the server returns a
'Bad request' respons
, lets process the response later.
"
1077 if [ "$ret" != "0" ] ; then
1078 _err "Please refer to https
://www.gnu.org
/software
/wget
/manual
/html_node
/Exit-Status.html
for error code
: $ret"
1082 _err "Neither curl nor wget is found
, can not
do GET.
"
1093 if ! tail -n $1 2>/dev/null ; then
1099 # url payload needbase64 keyfile
1100 _send_signed_request() {
1105 if [ -z "$keyfile" ] ; then
1106 keyfile="$ACCOUNT_KEY_PATH"
1109 _debug payload "$payload"
1111 if ! _calcjwk "$keyfile" ; then
1115 payload64=$(printf "%s
" "$payload" | _base64 | _urlencode)
1116 _debug3 payload64 $payload64
1118 nonceurl="$API/directory
"
1119 _headers="$
(_get
$nonceurl "onlyheader")"
1121 if [ "$?
" != "0" ] ; then
1122 _err "Can not connect to
$nonceurl to get nonce.
"
1126 _debug3 _headers "$_headers"
1128 nonce="$
( echo "$_headers" |
grep "Replay-Nonce:" | _head_n
1 |
tr -d "\r\n " | cut
-d ':' -f 2)"
1130 _debug3 nonce "$nonce"
1132 protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
1133 _debug3 protected "$protected"
1135 protected64="$
(printf "$protected" | _base64 | _urlencode
)"
1136 _debug3 protected64 "$protected64"
1138 sig=$(printf "%s
" "$protected64.
$payload64" | _sign "$keyfile" "sha256
" | _urlencode)
1141 body="{\"header
\": $JWK_HEADER, \"protected
\": \"$protected64\", \"payload
\": \"$payload64\", \"signature
\": \"$sig\"}"
1142 _debug3 body "$body"
1145 response="$
(_post
"$body" $url "$needbase64")"
1146 if [ "$?
" != "0" ] ; then
1147 _err "Can not post to
$url"
1150 _debug2 original "$response"
1152 response="$
( echo "$response" | _normalizeJson
)"
1154 responseHeaders="$
(cat $HTTP_HEADER)"
1156 _debug2 responseHeaders "$responseHeaders"
1157 _debug2 response "$response"
1158 code="$
(grep "^HTTP" $HTTP_HEADER | _tail_n
1 | cut
-d " " -f 2 |
tr -d "\r\n" )"
1164 #setopt "file" "opt
" "=" "value
" [";"]
1171 if [ -z "$__opt" ] ; then
1172 _usage usage: _setopt '"file" "opt
" "=" "value
" [";"]'
1175 if [ ! -f "$__conf" ] ; then
1179 if grep -n "^
$__opt$__sep" "$__conf" > /dev/null ; then
1181 if _contains "$__val" "&" ; then
1182 __val="$
(echo $__val |
sed 's/&/\\&/g')"
1184 text="$
(cat $__conf)"
1185 echo "$text" | sed "s|^
$__opt$__sep.
*$|
$__opt$__sep$__val$__end|
" > "$__conf"
1187 elif grep -n "^
#$__opt$__sep" "$__conf" > /dev/null ; then
1188 if _contains
"$__val" "&" ; then
1189 __val
="$(echo $__val | sed 's/&/\\&/g')"
1191 text
="$(cat $__conf)"
1192 echo "$text" |
sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
1196 echo "$__opt$__sep$__val$__end" >> "$__conf"
1198 _debug2
"$(grep -n "^
$__opt$__sep" $__conf)"
1201 #_savedomainconf key value
1202 #save to domain.conf
1206 if [ "$DOMAIN_CONF" ] ; then
1207 _setopt
"$DOMAIN_CONF" "$_sdkey" "=" "\"$_sdvalue\""
1209 _err
"DOMAIN_CONF is empty, can not save $_sdkey=$_sdvalue"
1213 #_cleardomainconf key
1214 _cleardomainconf
() {
1216 if [ "$DOMAIN_CONF" ] ; then
1217 _sed_i
"s/^$_sdkey.*$//" "$DOMAIN_CONF"
1219 _err
"DOMAIN_CONF is empty, can not save $_sdkey=$value"
1223 #_readdomainconf key
1226 if [ "$DOMAIN_CONF" ] ; then
1228 eval $
(grep "^$_sdkey *=" "$DOMAIN_CONF")
1229 eval "printf \"%s\" \"\$$_sdkey\""
1232 _err
"DOMAIN_CONF is empty, can not read $_sdkey"
1236 #_saveaccountconf key value
1237 _saveaccountconf
() {
1240 if [ "$ACCOUNT_CONF_PATH" ] ; then
1241 _setopt
"$ACCOUNT_CONF_PATH" "$_sckey" "=" "'$_scvalue'"
1243 _err
"ACCOUNT_CONF_PATH is empty, can not save $_sckey=$_scvalue"
1247 #_clearaccountconf key
1248 _clearaccountconf
() {
1250 if [ "$ACCOUNT_CONF_PATH" ] ; then
1251 _sed_i
"s/^$_scvalue.*$//" "$ACCOUNT_CONF_PATH"
1253 _err
"ACCOUNT_CONF_PATH is empty, can not clear $_scvalue"
1257 # content localaddress
1261 _debug
"ncaddr" "$ncaddr"
1263 _debug
"startserver: $$"
1264 nchelp
="$(nc -h 2>&1)"
1266 _debug Le_HTTPPort
"$Le_HTTPPort"
1267 _debug Le_Listen_V4
"$Le_Listen_V4"
1268 _debug Le_Listen_V6
"$Le_Listen_V6"
1271 if [ "$Le_Listen_V4" ] ; then
1273 elif [ "$Le_Listen_V6" ] ; then
1277 if echo "$nchelp" |
grep "\-q[ ,]" >/dev
/null
; then
1278 _NC
="$_NC -q 1 -l $ncaddr"
1280 if echo "$nchelp" |
grep "GNU netcat" >/dev
/null
&& echo "$nchelp" |
grep "\-c, \-\-close" >/dev
/null
; then
1281 _NC
="$_NC -c -l $ncaddr"
1282 elif echo "$nchelp" |
grep "\-N" |
grep "Shutdown the network socket after EOF on stdin" >/dev
/null
; then
1283 _NC
="$_NC -N -l $ncaddr"
1285 _NC
="$_NC -l $ncaddr"
1293 if _contains
"$nchelp" "nmap.org" ; then
1294 _debug
"Using ncat: nmap.org"
1295 if [ "$DEBUG" ] ; then
1296 if printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort ; then
1300 if printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort > /dev
/null
2>&1; then
1304 _err
"ncat listen error."
1308 if [ "$DEBUG" ] ; then
1309 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -p $Le_HTTPPort ; then
1310 printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort ;
1313 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -p $Le_HTTPPort > /dev
/null
2>&1; then
1314 printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort > /dev
/null
2>&1
1317 if [ "$?" != "0" ] ; then
1318 _err
"nc listen error."
1327 if [ -z "$pid" ] ; then
1331 _debug2
"Le_HTTPPort" "$Le_HTTPPort"
1332 if [ "$Le_HTTPPort" ] ; then
1333 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ] ; then
1334 _get
"http://localhost:$Le_HTTPPort" "" 1
1336 _get
"http://localhost:$Le_HTTPPort" "" 1 >/dev
/null
2>&1
1340 _debug2
"Le_TLSPort" "$Le_TLSPort"
1341 if [ "$Le_TLSPort" ] ; then
1342 if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ] ; then
1343 _get
"https://localhost:$Le_TLSPort" "" 1
1344 _get
"https://localhost:$Le_TLSPort" "" 1
1346 _get
"https://localhost:$Le_TLSPort" "" 1 >/dev
/null
2>&1
1347 _get
"https://localhost:$Le_TLSPort" "" 1 >/dev
/null
2>&1
1355 if [ "$__INTERACTIVE" ] ; then
1356 _sleep_c
="$_sleep_sec"
1357 while [ "$_sleep_c" -ge "0" ] ;
1361 _sleep_c
="$(_math $_sleep_c - 1)"
1370 # _starttlsserver san_a san_b port content _ncaddr
1372 _info
"Starting tls server."
1379 _debug san_a
"$san_a"
1380 _debug san_b
"$san_b"
1384 if ! _createkey
"2048" "$TLS_KEY" ; then
1385 _err
"Create tls validation key error."
1391 if [ "$san_b" ] ; then
1394 if ! _createcsr
"tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" ; then
1395 _err
"Create tls validation csr error."
1400 if ! _signcsr
"$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT" ; then
1401 _err
"Create tls validation cert error."
1405 __S_OPENSSL
="openssl s_server -cert $TLS_CERT -key $TLS_KEY "
1406 if [ "$opaddr" ] ; then
1407 __S_OPENSSL
="$__S_OPENSSL -accept $opaddr:$port"
1409 __S_OPENSSL
="$__S_OPENSSL -accept $port"
1412 _debug Le_Listen_V4
"$Le_Listen_V4"
1413 _debug Le_Listen_V6
"$Le_Listen_V6"
1414 if [ "$Le_Listen_V4" ] ; then
1415 __S_OPENSSL
="$__S_OPENSSL -4"
1416 elif [ "$Le_Listen_V6" ] ; then
1417 __S_OPENSSL
="$__S_OPENSSL -6"
1421 _debug
"$__S_OPENSSL"
1422 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
1423 (printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$__S_OPENSSL -tlsextdebug ) &
1425 (printf "HTTP/1.1 200 OK\r\n\r\n$content" |
$__S_OPENSSL >/dev
/null
2>&1) &
1430 _debug serverproc
$serverproc
1436 if ! readlink
-f "$_rf" 2>/dev
/null
; then
1437 if _startswith
"$_rf" "\./$PROJECT_ENTRY" ; then
1438 printf -- "%s" "$(pwd)/$PROJECT_ENTRY"
1446 if [ -z "$_SCRIPT_HOME" ] ; then
1447 if _exists readlink
&& _exists
dirname ; then
1448 _debug
"Lets find script dir."
1449 _debug
"_SCRIPT_" "$_SCRIPT_"
1450 _script
="$(_readlink "$_SCRIPT_")"
1451 _debug
"_script" "$_script"
1452 _script_home
="$(dirname "$_script")"
1453 _debug
"_script_home" "$_script_home"
1454 if [ -d "$_script_home" ] ; then
1455 _SCRIPT_HOME
="$_script_home"
1457 _err
"It seems the script home is not correct:$_script_home"
1463 if [ -z "$LE_WORKING_DIR" ] ; then
1464 if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ] ; then
1465 _debug
"It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
1466 LE_WORKING_DIR
="$DEFAULT_INSTALL_HOME"
1468 LE_WORKING_DIR
="$_SCRIPT_HOME"
1472 if [ -z "$LE_WORKING_DIR" ] ; then
1473 _debug
"Using default home:$DEFAULT_INSTALL_HOME"
1474 LE_WORKING_DIR
="$DEFAULT_INSTALL_HOME"
1476 export LE_WORKING_DIR
1478 _DEFAULT_ACCOUNT_CONF_PATH
="$LE_WORKING_DIR/account.conf"
1480 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
1481 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ] ; then
1482 .
"$_DEFAULT_ACCOUNT_CONF_PATH"
1486 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
1487 ACCOUNT_CONF_PATH
="$_DEFAULT_ACCOUNT_CONF_PATH"
1490 DEFAULT_LOG_FILE
="$LE_WORKING_DIR/$PROJECT_NAME.log"
1492 DEFAULT_CA_HOME
="$LE_WORKING_DIR/ca"
1495 #[domain] [keylength]
1500 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
1501 .
"$ACCOUNT_CONF_PATH"
1504 if [ "$IN_CRON" ] ; then
1505 if [ ! "$_USER_PATH_EXPORTED" ] ; then
1506 _USER_PATH_EXPORTED
=1
1507 export PATH
="$USER_PATH:$PATH"
1511 if [ -z "$CA_HOME" ] ; then
1512 CA_HOME
="$DEFAULT_CA_HOME"
1515 if [ -z "$API" ] ; then
1516 if [ -z "$STAGE" ] ; then
1520 _info
"Using stage api:$API"
1524 _API_HOST
="$(echo "$API" | cut -d : -f 2 | tr -d '/')"
1525 CA_DIR
="$CA_HOME/$_API_HOST"
1527 _DEFAULT_CA_CONF
="$CA_DIR/ca.conf"
1529 if [ -z "$CA_CONF" ] ; then
1530 CA_CONF
="$_DEFAULT_CA_CONF"
1533 if [ -f "$CA_CONF" ] ; then
1537 if [ -z "$ACME_DIR" ] ; then
1538 ACME_DIR
="/home/.acme"
1541 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
1542 APACHE_CONF_BACKUP_DIR
="$LE_WORKING_DIR"
1545 if [ -z "$USER_AGENT" ] ; then
1546 USER_AGENT
="$DEFAULT_USER_AGENT"
1549 if [ -z "$HTTP_HEADER" ] ; then
1550 HTTP_HEADER
="$LE_WORKING_DIR/http.header"
1553 _OLD_ACCOUNT_KEY
="$LE_WORKING_DIR/account.key"
1554 _OLD_ACCOUNT_JSON
="$LE_WORKING_DIR/account.json"
1556 _DEFAULT_ACCOUNT_KEY_PATH
="$CA_DIR/account.key"
1557 _DEFAULT_ACCOUNT_JSON_PATH
="$CA_DIR/account.json"
1558 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
1559 ACCOUNT_KEY_PATH
="$_DEFAULT_ACCOUNT_KEY_PATH"
1562 if [ -z "$ACCOUNT_JSON_PATH" ] ; then
1563 ACCOUNT_JSON_PATH
="$_DEFAULT_ACCOUNT_JSON_PATH"
1567 _DEFAULT_CERT_HOME
="$LE_WORKING_DIR"
1568 if [ -z "$CERT_HOME" ] ; then
1569 CERT_HOME
="$_DEFAULT_CERT_HOME"
1572 if [ -z "$1" ] ; then
1581 if [ -z "$DOMAIN_PATH" ] ; then
1582 domainhome
="$CERT_HOME/$domain"
1583 domainhomeecc
="$CERT_HOME/$domain$ECC_SUFFIX"
1585 DOMAIN_PATH
="$domainhome"
1587 if _isEccKey
"$_ilength" ; then
1588 DOMAIN_PATH
="$domainhomeecc"
1590 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ] ; then
1591 _info
"The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
1594 _debug DOMAIN_PATH
"$DOMAIN_PATH"
1597 if [ ! -d "$DOMAIN_PATH" ] ; then
1598 if ! mkdir
-p "$DOMAIN_PATH" ; then
1599 _err
"Can not create domain path: $DOMAIN_PATH"
1604 if [ -z "$DOMAIN_CONF" ] ; then
1605 DOMAIN_CONF
="$DOMAIN_PATH/$domain.conf"
1608 if [ -z "$DOMAIN_SSL_CONF" ] ; then
1609 DOMAIN_SSL_CONF
="$DOMAIN_PATH/$domain.csr.conf"
1612 if [ -z "$CSR_PATH" ] ; then
1613 CSR_PATH
="$DOMAIN_PATH/$domain.csr"
1615 if [ -z "$CERT_KEY_PATH" ] ; then
1616 CERT_KEY_PATH
="$DOMAIN_PATH/$domain.key"
1618 if [ -z "$CERT_PATH" ] ; then
1619 CERT_PATH
="$DOMAIN_PATH/$domain.cer"
1621 if [ -z "$CA_CERT_PATH" ] ; then
1622 CA_CERT_PATH
="$DOMAIN_PATH/ca.cer"
1624 if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
1625 CERT_FULLCHAIN_PATH
="$DOMAIN_PATH/fullchain.cer"
1627 if [ -z "$CERT_PFX_PATH" ] ; then
1628 CERT_PFX_PATH
="$DOMAIN_PATH/$domain.pfx"
1631 if [ -z "$TLS_CONF" ] ; then
1632 TLS_CONF
="$DOMAIN_PATH/tls.valdation.conf"
1634 if [ -z "$TLS_CERT" ] ; then
1635 TLS_CERT
="$DOMAIN_PATH/tls.valdation.cert"
1637 if [ -z "$TLS_KEY" ] ; then
1638 TLS_KEY
="$DOMAIN_PATH/tls.valdation.key"
1640 if [ -z "$TLS_CSR" ] ; then
1641 TLS_CSR
="$DOMAIN_PATH/tls.valdation.csr"
1648 _APACHECTL
="apachectl"
1649 if ! _exists apachectl
; then
1650 if _exists apache2ctl
; then
1651 _APACHECTL
="apache2ctl"
1653 _err
"'apachectl not found. It seems that apache is not installed, or you are not root user.'"
1654 _err
"Please use webroot mode to try again."
1658 httpdconfname
="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
1659 _debug httpdconfname "$httpdconfname"
1660 if _startswith "$httpdconfname" '/' ; then
1661 httpdconf="$httpdconfname"
1662 httpdconfname="$(basename $httpdconfname)"
1664 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
1665 _debug httpdroot
"$httpdroot"
1666 httpdconf
="$httpdroot/$httpdconfname"
1667 httpdconfname
="$(basename $httpdconfname)"
1669 _debug httpdconf
"$httpdconf"
1670 _debug httpdconfname
"$httpdconfname"
1671 if [ ! -f "$httpdconf" ] ; then
1672 _err
"Apache Config file not found" "$httpdconf"
1679 if [ -z "$usingApache" ] ; then
1683 if ! _apachePath
; then
1687 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
1688 _debug
"No config file to restore."
1692 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" > "$httpdconf"
1693 _debug
"Restored: $httpdconf."
1694 if ! $_APACHECTL -t >/dev
/null
2>&1 ; then
1695 _err
"Sorry, restore apache config error, please contact me."
1698 _debug
"Restored successfully."
1699 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
1705 if ! _apachePath
; then
1709 #test the conf first
1710 _info
"Checking if there is an error in the apache config file before starting."
1711 _msg
="$($_APACHECTL -t 2>&1 )"
1712 if [ "$?" != "0" ] ; then
1713 _err
"Sorry, apache config file has error, please fix it first, then try again."
1714 _err
"Don't worry, there is nothing changed to your system."
1722 _debug
"Backup apache config file" "$httpdconf"
1723 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/" ; then
1724 _err
"Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
1725 _err
"This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT"
1728 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
1729 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
1730 _info
"The backup file will be deleted on sucess, just forget it."
1734 apacheVer
="$($_APACHECTL -V | grep "Server version
:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )"
1735 _debug
"apacheVer" "$apacheVer"
1736 apacheMajer
="$(echo "$apacheVer" | cut -d . -f 1)"
1737 apacheMinor
="$(echo "$apacheVer" | cut -d . -f 2)"
1739 if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ] ; then
1741 Alias /.well-known/acme-challenge $ACME_DIR
1743 <Directory $ACME_DIR >
1749 Alias /.well-known/acme-challenge $ACME_DIR
1751 <Directory $ACME_DIR >
1758 _msg
="$($_APACHECTL -t 2>&1 )"
1759 if [ "$?" != "0" ] ; then
1760 _err
"Sorry, apache config error"
1761 if _restoreApache
; then
1762 _err
"The apache config file is restored."
1764 _err
"Sorry, The apache config file can not be restored, please report bug."
1769 if [ ! -d "$ACME_DIR" ] ; then
1770 mkdir
-p "$ACME_DIR"
1771 chmod 755 "$ACME_DIR"
1774 if ! $_APACHECTL graceful
; then
1775 _err
"Sorry, $_APACHECTL graceful error, please contact me."
1784 _stopserver
$serverproc
1788 if [ -z "$DEBUG" ] ; then
1797 _debug
"_clearupdns"
1798 if [ "$dnsadded" != 1 ] ||
[ -z "$vlist" ] ; then
1799 _info
"Dns not added, skip."
1803 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
1804 for ventry
in $ventries
1806 d
=$
(echo $ventry | cut
-d $sep -f 1)
1807 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
1808 vtype
=$
(echo $ventry | cut
-d $sep -f 4)
1809 _currentRoot
=$
(echo $ventry | cut
-d $sep -f 5)
1811 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
1812 _info
"$d is already verified, skip $vtype."
1816 if [ "$vtype" != "$VTYPE_DNS" ] ; then
1817 _info
"Skip $d for $vtype"
1821 d_api
="$(_findHook $d dnsapi $_currentRoot)"
1822 _debug d_api
"$d_api"
1824 if [ -z "$d_api" ] ; then
1825 _info
"Not Found domain api file: $d_api"
1830 if ! .
$d_api ; then
1831 _err
"Load file $d_api error. Please check your api file and try again."
1835 rmcommand
="${_currentRoot}_rm"
1836 if ! _exists
$rmcommand ; then
1837 _err
"It seems that your api file doesn't define $rmcommand"
1841 txtdomain
="_acme-challenge.$d"
1843 if ! $rmcommand $txtdomain ; then
1844 _err
"Error removing txt for domain:$txtdomain"
1852 # webroot removelevel tokenfile
1853 _clearupwebbroot
() {
1855 if [ -z "$__webroot" ] ; then
1856 _debug
"no webroot specified, skip"
1861 if [ "$2" = '1' ] ; then
1862 _rmpath
="$__webroot/.well-known"
1863 elif [ "$2" = '2' ] ; then
1864 _rmpath
="$__webroot/.well-known/acme-challenge"
1865 elif [ "$2" = '3' ] ; then
1866 _rmpath
="$__webroot/.well-known/acme-challenge/$3"
1868 _debug
"Skip for removelevel:$2"
1871 if [ "$_rmpath" ] ; then
1872 if [ "$DEBUG" ] ; then
1873 _debug
"Debugging, skip removing: $_rmpath"
1883 _on_before_issue
() {
1884 _debug _on_before_issue
1885 if _hasfield
"$Le_Webroot" "$NO_VALUE" ; then
1886 if ! _exists
"nc" ; then
1887 _err
"Please install netcat(nc) tools first."
1890 elif ! _hasfield
"$Le_Webroot" "$W_TLS" ; then
1891 #no need to check anymore
1895 _debug Le_LocalAddress
"$Le_LocalAddress"
1897 alldomains
=$
(echo "$Le_Domain,$Le_Alt" |
tr ',' ' ' )
1901 for d
in $alldomains
1903 _debug
"Check for domain" $d
1904 _currentRoot
="$(_getfield "$Le_Webroot" $_index)"
1905 _debug
"_currentRoot" "$_currentRoot"
1906 _index
=$
(_math
$_index + 1)
1908 if [ "$_currentRoot" = "$NO_VALUE" ] ; then
1909 _info
"Standalone mode."
1910 if [ -z "$Le_HTTPPort" ] ; then
1913 _savedomainconf
"Le_HTTPPort" "$Le_HTTPPort"
1915 _checkport
="$Le_HTTPPort"
1916 elif [ "$_currentRoot" = "$W_TLS" ] ; then
1917 _info
"Standalone tls mode."
1918 if [ -z "$Le_TLSPort" ] ; then
1921 _savedomainconf
"Le_TLSPort" "$Le_TLSPort"
1923 _checkport
="$Le_TLSPort"
1926 if [ "$_checkport" ] ; then
1927 _debug _checkport
"$_checkport"
1928 _checkaddr
="$(_getfield "$Le_LocalAddress" $_addrIndex)"
1929 _debug _checkaddr
"$_checkaddr"
1931 _addrIndex
="$(_math $_addrIndex + 1)"
1933 _netprc
="$(_ss "$_checkport" | grep "$_checkport")"
1934 netprc
="$(echo "$_netprc" | grep "$_checkaddr")"
1935 if [ -z "$netprc" ] ; then
1936 netprc
="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
1938 if [ "$netprc" ] ; then
1940 _err
"tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
1941 _err
"Please stop it first"
1947 if _hasfield
"$Le_Webroot" "apache" ; then
1948 if ! _setApache
; then
1949 _err
"set up apache error. Report error to me."
1957 if [ "$Le_PreHook" ] ; then
1958 _info
"Run pre hook:'$Le_PreHook'"
1960 cd "$DOMAIN_PATH" && eval "$Le_PreHook"
1962 _err
"Error when run pre hook."
1969 _debug _on_issue_err
1970 if [ "$LOG_FILE" ] ; then
1971 _err
"Please check log file for more details: $LOG_FILE"
1973 _err
"Please use add '--debug' or '--log' to check more details."
1974 _err
"See: $_DEBUG_WIKI"
1978 if [ "$Le_PostHook" ] ; then
1979 _info
"Run post hook:'$Le_PostHook'"
1981 cd "$DOMAIN_PATH" && eval "$Le_PostHook"
1983 _err
"Error when run post hook."
1989 _on_issue_success
() {
1990 _debug _on_issue_success
1992 if [ "$Le_PostHook" ] ; then
1993 _info
"Run post hook:'$Le_PostHook'"
1995 cd "$DOMAIN_PATH" && eval "$Le_PostHook"
1997 _err
"Error when run post hook."
2003 if [ "$IS_RENEW" ] && [ "$Le_RenewHook" ] ; then
2004 _info
"Run renew hook:'$Le_RenewHook'"
2006 cd "$DOMAIN_PATH" && eval "$Le_RenewHook"
2008 _err
"Error when run renew hook."
2028 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
2029 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
2030 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
2033 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
2034 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
2035 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
2038 if [ ! -f "$ACCOUNT_KEY_PATH" ] ; then
2040 if [ "$Le_Keylength" ] ; then
2041 _acck
="$Le_Keylength"
2043 if ! createAccountKey
"$_acck" ; then
2044 _err
"Create account key error."
2049 if ! _calcjwk
"$ACCOUNT_KEY_PATH" ; then
2057 _debug AGREEMENT
"$AGREEMENT"
2058 accountkey_json
=$
(printf "%s" "$jwk" |
tr -d ' ' )
2059 thumbprint
=$
(printf "%s" "$accountkey_json" | _digest
"sha256" | _urlencode
)
2061 regjson
='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}'
2063 if [ "$ACCOUNT_EMAIL" ] ; then
2064 regjson
='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
2067 if [ -z "$_updateTos" ] ; then
2068 _info
"Registering account"
2070 if ! _send_signed_request
"$API/acme/new-reg" "$regjson" ; then
2071 _err
"Register account Error: $response"
2075 if [ "$code" = "" ] ||
[ "$code" = '201' ] ; then
2076 echo "$response" > $ACCOUNT_JSON_PATH
2078 elif [ "$code" = '409' ] ; then
2079 _info
"Already registered"
2081 _err
"Register account Error: $response"
2085 _accUri
="$(echo "$responseHeaders" | grep "^Location
:" | _head_n 1 | cut -d ' ' -f 2| tr -d "\r\n")"
2086 _debug
"_accUri" "$_accUri"
2088 _tos
="$(echo "$responseHeaders" | grep "^Link
:.
*rel
=\"terms-of-service
\"" | _head_n 1 | _egrep_o "<.
*>" | tr -d '<>')"
2089 _debug
"_tos" "$_tos"
2090 if [ -z "$_tos" ] ; then
2091 _debug
"Use default tos: $DEFAULT_AGREEMENT"
2092 _tos
="$DEFAULT_AGREEMENT"
2094 if [ "$_tos" != "$AGREEMENT" ]; then
2102 _debug
"Update tos: $_tos"
2103 if ! _send_signed_request
"$_accUri" "$regjson" ; then
2104 _err
"Update tos error."
2107 if [ "$code" = '202' ] ; then
2108 _info
"Update success."
2110 _err
"Update account error."
2120 # domain folder file
2126 if [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ] ; then
2127 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname"
2128 elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ] ; then
2129 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
2130 elif [ -f "$LE_WORKING_DIR/$_hookname" ] ; then
2131 d_api
="$LE_WORKING_DIR/$_hookname"
2132 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ] ; then
2133 d_api
="$LE_WORKING_DIR/$_hookname.sh"
2134 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ] ; then
2135 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname"
2136 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ] ; then
2137 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
2140 printf "%s" "$d_api"
2144 __get_domain_new_authz
() {
2146 _info
"Getting new-authz for domain" "$_gdnd"
2148 _Max_new_authz_retry_times
=5
2150 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ] ; do
2151 _info
"Try new-authz for the $_authz_i time."
2152 if ! _send_signed_request
"$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}" ; then
2153 _err
"Can not get domain new authz."
2156 if ! _contains
"$response" "An error occurred while processing your request" ; then
2157 _info
"The new-authz request is ok."
2160 _authz_i
="$(_math "$_authz_i" + 1)"
2161 _info
"The server is busy, Sleep $_authz_i to retry."
2165 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ] ; then
2166 _debug
"new-authz retry reach the max $_Max_new_authz_retry_times times."
2169 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
2170 _err
"new-authz error: $response"
2176 #webroot, domain domainlist keylength
2178 if [ -z "$2" ] ; then
2179 _usage
"Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
2186 Le_RealCertPath
="$5"
2188 Le_RealCACertPath
="$7"
2190 Le_RealFullChainPath
="$9"
2193 Le_RenewHook
="${12}"
2194 Le_LocalAddress
="${13}"
2196 #remove these later.
2197 if [ "$Le_Webroot" = "dns-cf" ] ; then
2200 if [ "$Le_Webroot" = "dns-dp" ] ; then
2203 if [ "$Le_Webroot" = "dns-cx" ] ; then
2206 _debug
"Using api: $API"
2208 if [ ! "$IS_RENEW" ] ; then
2209 _initpath
$Le_Domain "$Le_Keylength"
2210 mkdir
-p "$DOMAIN_PATH"
2213 if [ -f "$DOMAIN_CONF" ] ; then
2214 Le_NextRenewTime
=$
(_readdomainconf Le_NextRenewTime
)
2215 _debug Le_NextRenewTime
"$Le_NextRenewTime"
2216 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ $
(_time
) -lt $Le_NextRenewTime ] ; then
2217 _saved_domain
=$
(_readdomainconf Le_Domain
)
2218 _debug _saved_domain
"$_saved_domain"
2219 _saved_alt
=$
(_readdomainconf Le_Alt
)
2220 _debug _saved_alt
"$_saved_alt"
2221 if [ "$_saved_domain,$_saved_alt" = "$Le_Domain,$Le_Alt" ] ; then
2222 _info
"Domains not changed."
2223 _info
"Skip, Next renewal time is: $(__green "$
(_readdomainconf Le_NextRenewTimeStr
)")"
2224 _info
"Add '$(__red '--force')' to force to renew."
2227 _info
"Domains have changed."
2232 _savedomainconf
"Le_Domain" "$Le_Domain"
2233 _savedomainconf
"Le_Alt" "$Le_Alt"
2234 _savedomainconf
"Le_Webroot" "$Le_Webroot"
2236 _savedomainconf
"Le_PreHook" "$Le_PreHook"
2237 _savedomainconf
"Le_PostHook" "$Le_PostHook"
2238 _savedomainconf
"Le_RenewHook" "$Le_RenewHook"
2239 _savedomainconf
"Le_LocalAddress" "$Le_LocalAddress"
2243 _savedomainconf
"Le_API" "$Le_API"
2245 if [ "$Le_Alt" = "$NO_VALUE" ] ; then
2249 if [ "$Le_Keylength" = "$NO_VALUE" ] ; then
2253 if ! _on_before_issue
; then
2254 _err
"_on_before_issue."
2258 if ! _regAccount
; then
2264 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ] ; then
2265 _info
"Signing from existing CSR."
2267 _key
=$
(_readdomainconf Le_Keylength
)
2268 _debug
"Read key length:$_key"
2269 if [ ! -f "$CERT_KEY_PATH" ] ||
[ "$Le_Keylength" != "$_key" ] ; then
2270 if ! createDomainKey
$Le_Domain $Le_Keylength ; then
2271 _err
"Create domain key error."
2278 if ! _createcsr
"$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" ; then
2279 _err
"Create CSR error."
2286 _savedomainconf
"Le_Keylength" "$Le_Keylength"
2289 # verify each domain
2290 _info
"Verify each domain"
2292 if [ -z "$vlist" ] ; then
2293 alldomains
=$
(echo "$Le_Domain,$Le_Alt" |
tr ',' ' ' )
2296 for d
in $alldomains
2298 _info
"Getting webroot for domain" $d
2299 _w
="$(echo $Le_Webroot | cut -d , -f $_index)"
2304 _debug
"_currentRoot" "$_currentRoot"
2305 _index
=$
(_math
$_index + 1)
2308 if _startswith
"$_currentRoot" "dns" ; then
2312 if [ "$_currentRoot" = "$W_TLS" ] ; then
2316 if ! __get_domain_new_authz
"$d" ; then
2322 entry
="$(printf "%s
\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
2323 _debug entry
"$entry"
2324 if [ -z "$entry" ] ; then
2325 _err
"Error, can not get domain token $d"
2330 token
="$(printf "%s
\n" "$entry" | _egrep_o '"token
":"[^
"]*' | cut -d : -f 2 | tr -d '"')"
2333 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
2336 keyauthorization
="$token.$thumbprint"
2337 _debug keyauthorization
"$keyauthorization"
2340 if printf "$response" |
grep '"status":"valid"' >/dev
/null
2>&1 ; then
2341 _info
"$d is already verified, skip."
2342 keyauthorization
=$STATE_VERIFIED
2343 _debug keyauthorization
"$keyauthorization"
2347 dvlist
="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
2348 _debug dvlist
"$dvlist"
2350 vlist
="$vlist$dvlist,"
2356 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
2357 for ventry
in $ventries
2359 d
=$
(echo $ventry | cut
-d $sep -f 1)
2360 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
2361 vtype
=$
(echo $ventry | cut
-d $sep -f 4)
2362 _currentRoot
=$
(echo $ventry | cut
-d $sep -f 5)
2364 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
2365 _info
"$d is already verified, skip $vtype."
2369 if [ "$vtype" = "$VTYPE_DNS" ] ; then
2371 txtdomain
="_acme-challenge.$d"
2372 _debug txtdomain
"$txtdomain"
2373 txt
="$(printf "%s
" "$keyauthorization" | _digest "sha256
" | _urlencode)"
2376 d_api
="$(_findHook $d dnsapi $_currentRoot)"
2378 _debug d_api
"$d_api"
2380 if [ "$d_api" ] ; then
2381 _info
"Found domain api file: $d_api"
2383 _err
"Add the following TXT record:"
2384 _err
"Domain: '$(__green $txtdomain)'"
2385 _err
"TXT value: '$(__green $txt)'"
2386 _err
"Please be aware that you prepend _acme-challenge. before your domain"
2387 _err
"so the resulting subdomain will be: $txtdomain"
2392 if ! .
$d_api ; then
2393 _err
"Load file $d_api error. Please check your api file and try again."
2397 addcommand
="${_currentRoot}_add"
2398 if ! _exists
$addcommand ; then
2399 _err
"It seems that your api file is not correct, it must have a function named: $addcommand"
2403 if ! $addcommand $txtdomain $txt ; then
2404 _err
"Error add txt for domain:$txtdomain"
2409 if [ "$?" != "0" ] ; then
2418 if [ "$dnsadded" = '0' ] ; then
2419 _savedomainconf
"Le_Vlist" "$vlist"
2420 _debug
"Dns record not added yet, so, save to $DOMAIN_CONF and exit."
2421 _err
"Please add the TXT records to the domains, and retry again."
2429 if [ "$dnsadded" = '1' ] ; then
2430 if [ -z "$Le_DNSSleep" ] ; then
2431 Le_DNSSleep
=$DEFAULT_DNS_SLEEP
2433 _savedomainconf
"Le_DNSSleep" "$Le_DNSSleep"
2436 _info
"Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
2440 _debug
"ok, let's start to verify"
2443 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
2444 for ventry
in $ventries
2446 d
=$
(echo $ventry | cut
-d $sep -f 1)
2447 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
2448 uri
=$
(echo $ventry | cut
-d $sep -f 3)
2449 vtype
=$
(echo $ventry | cut
-d $sep -f 4)
2450 _currentRoot
=$
(echo $ventry | cut
-d $sep -f 5)
2452 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
2453 _info
"$d is already verified, skip $vtype."
2457 _info
"Verifying:$d"
2459 _debug
"keyauthorization" "$keyauthorization"
2462 token
="$(printf "%s
" "$keyauthorization" | cut -d '.' -f 1)"
2464 _debug
"_currentRoot" "$_currentRoot"
2467 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
2468 if [ "$_currentRoot" = "$NO_VALUE" ] ; then
2469 _info
"Standalone mode server"
2470 _ncaddr
="$(_getfield "$Le_LocalAddress" "$_ncIndex" )"
2471 _ncIndex
="$(_math $_ncIndex + 1)"
2472 _startserver
"$keyauthorization" "$_ncaddr" &
2473 if [ "$?" != "0" ] ; then
2480 _debug serverproc
$serverproc
2483 if [ "$_currentRoot" = "apache" ] ; then
2484 wellknown_path
="$ACME_DIR"
2486 wellknown_path
="$_currentRoot/.well-known/acme-challenge"
2487 if [ ! -d "$_currentRoot/.well-known" ] ; then
2489 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ] ; then
2496 _debug wellknown_path
"$wellknown_path"
2498 _debug
"writing token:$token to $wellknown_path/$token"
2500 mkdir
-p "$wellknown_path"
2501 printf "%s" "$keyauthorization" > "$wellknown_path/$token"
2502 if [ ! "$usingApache" ] ; then
2503 if webroot_owner
=$
(_stat
$_currentRoot) ; then
2504 _debug
"Changing owner/group of .well-known to $webroot_owner"
2505 chown
-R $webroot_owner "$_currentRoot/.well-known"
2507 _debug
"not chaning owner/group of webroot";
2513 elif [ "$vtype" = "$VTYPE_TLS" ] ; then
2515 #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
2516 #_debug2 _hash_A "$_hash_A"
2517 #_x="$(echo $_hash_A | cut -c 1-32)"
2519 #_y="$(echo $_hash_A | cut -c 33-64)"
2521 #_SAN_A="$_x.$_y.token.acme.invalid"
2522 #_debug2 _SAN_A "$_SAN_A"
2525 _hash_B
="$(printf "%s
" $keyauthorization | _digest "sha256
" "hex
" )"
2526 _debug2 _hash_B
"$_hash_B"
2527 _x
="$(echo $_hash_B | cut -c 1-32)"
2529 _y
="$(echo $_hash_B | cut -c 33-64)"
2532 #_SAN_B="$_x.$_y.ka.acme.invalid"
2534 _SAN_B
="$_x.$_y.acme.invalid"
2535 _debug2 _SAN_B
"$_SAN_B"
2537 _ncaddr
="$(_getfield "$Le_LocalAddress" "$_ncIndex" )"
2538 _ncIndex
="$(_math $_ncIndex + 1)"
2539 if ! _starttlsserver
"$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
2540 _err
"Start tls server error."
2541 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2548 if ! _send_signed_request
$uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then
2549 _err
"$d:Can not get challenge: $response"
2550 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2556 if [ ! -z "$code" ] && [ ! "$code" = '202' ] ; then
2557 _err
"$d:Challenge error: $response"
2558 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2565 if [ -z "$MAX_RETRY_TIMES" ] ; then
2570 waittimes
=$
(_math
$waittimes + 1)
2571 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ] ; then
2573 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2579 _debug
"sleep 5 secs to verify"
2582 response
="$(_get $uri)"
2583 if [ "$?" != "0" ] ; then
2584 _err
"$d:Verify error:$response"
2585 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2590 _debug2 original
"$response"
2592 response
="$(echo "$response" | _normalizeJson )"
2593 _debug2 response
"$response"
2595 status
=$
(echo "$response" | _egrep_o
'"status":"[^"]*' | cut
-d : -f 2 |
tr -d '"')
2596 if [ "$status" = "valid" ] ; then
2598 _stopserver
$serverproc
2600 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
2604 if [ "$status" = "invalid" ] ; then
2605 error
="$(echo "$response" | tr -d "\r\n" | _egrep_o '"error
":\{[^\}]*')"
2606 _debug2 error
"$error"
2607 errordetail
="$(echo "$error" | _egrep_o '"detail
": *"[^
"]*' | cut -d '"' -f 4)"
2608 _debug2 errordetail "$errordetail"
2609 if [ "$errordetail" ] ; then
2610 _err "$d:Verify error:$errordetail"
2612 _err "$d:Verify error:$error"
2614 if [ "$DEBUG" ] ; then
2615 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
2616 _debug "Debug: get token url."
2617 _get "http://$d/.well-known/acme-challenge/$token" "" 1
2620 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2626 if [ "$status" = "pending" ] ; then
2629 _err "$d:Verify error:$response"
2630 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
2641 _info "Verify finished, start to sign."
2642 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
2644 if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" ; then
2651 Le_LinkCert="$(grep -i '^Location.
*$
' $HTTP_HEADER | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
2652 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
2654 if [ "$Le_LinkCert" ] ; then
2655 echo "$BEGIN_CERT" > "$CERT_PATH"
2657 if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
2658 _debug "Get cert failed. Let's try last response.
"
2659 printf -- "%s
" "$_rcert" | _dbase64 "multiline
" | _base64 "multiline
" >> "$CERT_PATH"
2662 echo "$END_CERT" >> "$CERT_PATH"
2663 _info "$
(__green
"Cert success.")"
2666 _info "Your cert is
in $
( __green
" $CERT_PATH ")"
2668 if [ -f "$CERT_KEY_PATH" ] ; then
2669 _info "Your cert key is
in $
( __green
" $CERT_KEY_PATH ")"
2672 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
2674 if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ] ; then
2676 _saveaccountconf "USER_PATH
" "$USER_PATH"
2681 if [ -z "$Le_LinkCert" ] ; then
2682 response="$
(echo $response | _dbase64
"multiline" | _normalizeJson
)"
2683 _err "Sign failed
: $
(echo "$response" | _egrep_o
'"detail":"[^"]*"')"
2688 _cleardomainconf "Le_Vlist
"
2690 Le_LinkIssuer=$(grep -i '^Link' $HTTP_HEADER | _head_n 1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
2691 if ! _contains "$Le_LinkIssuer" ":" ; then
2692 Le_LinkIssuer="$API$Le_LinkIssuer"
2695 _savedomainconf "Le_LinkIssuer
" "$Le_LinkIssuer"
2697 if [ "$Le_LinkIssuer" ] ; then
2698 echo "$BEGIN_CERT" > "$CA_CERT_PATH"
2699 _get "$Le_LinkIssuer" | _base64 "multiline
" >> "$CA_CERT_PATH"
2700 echo "$END_CERT" >> "$CA_CERT_PATH"
2701 _info "The intermediate CA cert is
in $
( __green
" $CA_CERT_PATH ")"
2702 cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
2703 _info "And the full chain certs is there
: $
( __green
" $CERT_FULLCHAIN_PATH ")"
2706 Le_CertCreateTime=$(_time)
2707 _savedomainconf "Le_CertCreateTime
" "$Le_CertCreateTime"
2709 Le_CertCreateTimeStr=$(date -u )
2710 _savedomainconf "Le_CertCreateTimeStr
" "$Le_CertCreateTimeStr"
2712 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ] ; then
2713 Le_RenewalDays=$MAX_RENEW
2715 _savedomainconf "Le_RenewalDays
" "$Le_RenewalDays"
2718 if [ "$CA_BUNDLE" ] ; then
2719 _saveaccountconf CA_BUNDLE "$CA_BUNDLE"
2721 _clearaccountconf "CA_BUNDLE
"
2724 if [ "$HTTPS_INSECURE" ] ; then
2725 _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
2727 _clearaccountconf "HTTPS_INSECURE
"
2730 if [ "$Le_Listen_V4" ] ; then
2731 _savedomainconf "Le_Listen_V4
" "$Le_Listen_V4"
2732 _cleardomainconf Le_Listen_V6
2733 elif [ "$Le_Listen_V6" ] ; then
2734 _savedomainconf "Le_Listen_V6
" "$Le_Listen_V6"
2735 _cleardomainconf Le_Listen_V4
2738 Le_NextRenewTime=$(_math $Le_CertCreateTime + $Le_RenewalDays \* 24 \* 60 \* 60)
2741 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
2742 _savedomainconf "Le_NextRenewTimeStr
" "$Le_NextRenewTimeStr"
2744 Le_NextRenewTime=$(_math $Le_NextRenewTime - 86400)
2745 _savedomainconf "Le_NextRenewTime
" "$Le_NextRenewTime"
2750 if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ] ; then
2759 if [ -z "$Le_Domain" ] ; then
2760 _usage "Usage
: $PROJECT_ENTRY --renew -d domain.com
[--ecc]"
2766 _initpath $Le_Domain "$_isEcc"
2768 _info "$
(__green
"Renew: '$Le_Domain'")"
2769 if [ ! -f "$DOMAIN_CONF" ] ; then
2770 _info "'$Le_Domain' is not a issued domain
, skip.
"
2774 if [ "$Le_RenewalDays" ] ; then
2775 _savedomainconf Le_RenewalDays "$Le_RenewalDays"
2780 if [ "$Le_API" ] ; then
2784 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$
(_time
)" -lt "$Le_NextRenewTime" ] ; then
2785 _info "Skip
, Next renewal
time is
: $
(__green
"$Le_NextRenewTimeStr")"
2786 _info "Add
'$(__red '--force')' to force to renew.
"
2791 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress"
2793 if [ "$res" != "0" ] ; then
2797 if [ "$Le_DeployHook" ] ; then
2798 deploy $Le_Domain "$Le_DeployHook" "$Le_Keylength"
2807 #renewAll [stopRenewOnError]
2810 _stopRenewOnError="$1"
2811 _debug "_stopRenewOnError
" "$_stopRenewOnError"
2814 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
2815 d=$(echo $d | cut -d '/' -f 1)
2817 if _endswith $d "$ECC_SUFFIX" ; then
2818 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2819 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2821 renew "$d" "$_isEcc"
2824 _debug "Return code
: $rc"
2825 if [ "$rc" != "0" ] ; then
2826 if [ "$rc" = "$RENEW_SKIP" ] ; then
2828 elif [ "$_stopRenewOnError" ] ; then
2829 _err "Error renew
$d, stop now.
"
2833 _err "Error renew
$d, Go ahead to next one.
"
2845 if [ -z "$_csrfile" ] || [ -z "$_csrW" ]; then
2846 _usage "Usage
: $PROJECT_ENTRY --signcsr --csr mycsr.csr
-w /path
/to
/webroot
/a.com
/ "
2852 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
2853 if [ "$?
" != "0" ] ; then
2854 _err "Can not
read subject from csr
: $_csrfile"
2857 _debug _csrsubj "$_csrsubj"
2859 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
2860 if [ "$?
" != "0" ] ; then
2861 _err "Can not
read domain list from csr
: $_csrfile"
2864 _debug "_csrdomainlist
" "$_csrdomainlist"
2867 if [ -z "$_csrsubj" ] ; then
2868 _csrsubj="$
(_getfield
"$_csrdomainlist" 1)"
2869 _debug _csrsubj "$_csrsubj"
2870 _csrdomainlist="$
(echo "$_csrdomainlist" | cut
-d , -f 2-)"
2871 _debug "_csrdomainlist
" "$_csrdomainlist"
2874 if [ -z "$_csrsubj" ] ; then
2875 _err "Can not
read subject from csr
: $_csrfile"
2879 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
2880 if [ "$?
" != "0" ] || [ -z "$_csrkeylength" ] ; then
2881 _err "Can not
read key length from csr
: $_csrfile"
2885 _initpath "$_csrsubj" "$_csrkeylength"
2886 mkdir -p "$DOMAIN_PATH"
2888 _info "Copy csr to
: $CSR_PATH"
2889 cp "$_csrfile" "$CSR_PATH"
2891 issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength"
2898 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
2899 _usage "Usage
: $PROJECT_ENTRY --showcsr --csr mycsr.csr
"
2905 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
2906 if [ "$?
" != "0" ] || [ -z "$_csrsubj" ] ; then
2907 _err "Can not
read subject from csr
: $_csrfile"
2911 _info "Subject
=$_csrsubj"
2913 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
2914 if [ "$?
" != "0" ] ; then
2915 _err "Can not
read domain list from csr
: $_csrfile"
2918 _debug "_csrdomainlist
" "$_csrdomainlist"
2920 _info "SubjectAltNames
=$_csrdomainlist"
2923 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
2924 if [ "$?
" != "0" ] || [ -z "$_csrkeylength" ] ; then
2925 _err "Can not
read key length from csr
: $_csrfile"
2928 _info "KeyLength
=$_csrkeylength"
2936 if [ "$_raw" ] ; then
2937 printf "Main_Domain
${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew
\n"
2938 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
2939 d=$(echo $d | cut -d '/' -f 1)
2941 if _endswith $d "$ECC_SUFFIX" ; then
2942 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2943 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2945 _initpath $d "$_isEcc"
2946 if [ -f "$DOMAIN_CONF" ] ; then
2948 printf "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
2953 if _exists column ; then
2954 list "raw
" | column -t -s "$_sep"
2956 list "raw
" | tr "$_sep" '\t'
2967 if [ -z "$Le_DeployHook" ] ; then
2968 _usage "Usage
: $PROJECT_ENTRY --deploy -d domain.com
--deploy-hook cpanel
[--ecc] "
2972 _initpath $Le_Domain "$_isEcc"
2973 if [ ! -d "$DOMAIN_PATH" ] ; then
2974 _err "Domain is not valid
:'$Le_Domain'"
2978 _deployApi="$
(_findHook
$Le_Domain deploy
$Le_DeployHook)"
2979 if [ -z "$_deployApi" ] ; then
2980 _err "The deploy hook
$Le_DeployHook is not found.
"
2983 _debug _deployApi "$_deployApi"
2985 _savedomainconf Le_DeployHook "$Le_DeployHook"
2988 if ! . $_deployApi ; then
2989 _err "Load
file $_deployApi error. Please check your api
file and try again.
"
2993 d_command="${Le_DeployHook}_deploy
"
2994 if ! _exists $d_command ; then
2995 _err "It seems that your api
file is not correct
, it must have a
function named
: $d_command"
2999 if ! $d_command $Le_Domain "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH" ; then
3000 _err "Error deploy
for domain
:$Le_Domain"
3005 _err "Deploy error.
"
3008 _info "$
(__green Success
)"
3015 if [ -z "$Le_Domain" ] ; then
3016 _usage "Usage
: $PROJECT_ENTRY --installcert -d domain.com
[--ecc] [--certpath cert-file-path
] [--keypath key-file-path
] [--capath ca-cert-file-path
] [ --reloadCmd reloadCmd
] [--fullchainpath fullchain-path
]"
3020 Le_RealCertPath="$2"
3022 Le_RealCACertPath="$4"
3024 Le_RealFullChainPath="$6"
3027 _initpath $Le_Domain "$_isEcc"
3028 if [ ! -d "$DOMAIN_PATH" ] ; then
3029 _err "Domain is not valid
:'$Le_Domain'"
3039 _savedomainconf "Le_RealCertPath
" "$Le_RealCertPath"
3040 _savedomainconf "Le_RealCACertPath
" "$Le_RealCACertPath"
3041 _savedomainconf "Le_RealKeyPath
" "$Le_RealKeyPath"
3042 _savedomainconf "Le_ReloadCmd
" "$Le_ReloadCmd"
3043 _savedomainconf "Le_RealFullChainPath
" "$Le_RealFullChainPath"
3045 if [ "$Le_RealCertPath" = "$NO_VALUE" ] ; then
3048 if [ "$Le_RealKeyPath" = "$NO_VALUE" ] ; then
3051 if [ "$Le_RealCACertPath" = "$NO_VALUE" ] ; then
3052 Le_RealCACertPath=""
3054 if [ "$Le_ReloadCmd" = "$NO_VALUE" ] ; then
3057 if [ "$Le_RealFullChainPath" = "$NO_VALUE" ] ; then
3058 Le_RealFullChainPath=""
3062 if [ "$Le_RealCertPath" ] ; then
3064 _info "Installing cert to
:$Le_RealCertPath"
3065 if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ] ; then
3066 cp "$Le_RealCertPath" "$Le_RealCertPath".bak
3068 cat "$CERT_PATH" > "$Le_RealCertPath"
3071 if [ "$Le_RealCACertPath" ] ; then
3073 _info "Installing CA to
:$Le_RealCACertPath"
3074 if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ] ; then
3075 echo "" >> "$Le_RealCACertPath"
3076 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
3078 if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ] ; then
3079 cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
3081 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
3086 if [ "$Le_RealKeyPath" ] ; then
3088 _info "Installing key to
:$Le_RealKeyPath"
3089 if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ] ; then
3090 cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
3092 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
3095 if [ "$Le_RealFullChainPath" ] ; then
3097 _info "Installing full chain to
:$Le_RealFullChainPath"
3098 if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ] ; then
3099 cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
3101 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
3104 if [ "$Le_ReloadCmd" ] ; then
3106 _info "Run Le_ReloadCmd
: $Le_ReloadCmd"
3107 if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") ; then
3108 _info "$
(__green
"Reload success")"
3110 _err "Reload error
for :$Le_Domain"
3119 if ! _exists "crontab
" ; then
3120 _err "crontab doesn
't exist, so, we can not install cron jobs."
3121 _err "All your certs will not be renewed automatically."
3122 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
3126 _info "Installing cron job"
3127 if ! crontab -l | grep "$PROJECT_ENTRY --cron" ; then
3128 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ] ; then
3129 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
3131 _err "Can not install cronjob, $PROJECT_ENTRY not found."
3134 if _exists uname && uname -a | grep solaris >/dev/null ; then
3135 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab --
3137 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab -
3140 if [ "$?" != "0" ] ; then
3141 _err "Install cron job failed. You need to manually renew your certs."
3142 _err "Or you can add cronjob by yourself:"
3143 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
3148 uninstallcronjob() {
3149 if ! _exists "crontab" ; then
3152 _info "Removing cron job"
3153 cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")"
3155 if _exists uname && uname -a | grep solaris >/dev/null ; then
3156 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab --
3158 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -
3160 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
3161 _info LE_WORKING_DIR
"$LE_WORKING_DIR"
3169 if [ -z "$Le_Domain" ] ; then
3170 _usage
"Usage: $PROJECT_ENTRY --revoke -d domain.com"
3176 _initpath
$Le_Domain "$_isEcc"
3177 if [ ! -f "$DOMAIN_CONF" ] ; then
3178 _err
"$Le_Domain is not a issued domain, skip."
3182 if [ ! -f "$CERT_PATH" ] ; then
3183 _err
"Cert for $Le_Domain $CERT_PATH is not found, skip."
3187 cert
="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)"
3189 if [ -z "$cert" ] ; then
3190 _err
"Cert for $Le_Domain is empty found, skip."
3194 data
="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
3195 uri
="$API/acme/revoke-cert"
3197 _info
"Try domain key first."
3198 if _send_signed_request
$uri "$data" "" "$CERT_KEY_PATH"; then
3199 if [ -z "$response" ] ; then
3200 _info
"Revoke success."
3204 _err
"Revoke error by domain key."
3209 _info
"Then try account key."
3211 if _send_signed_request
$uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
3212 if [ -z "$response" ] ; then
3213 _info
"Revoke success."
3217 _err
"Revoke error."
3233 while [ "$_d_i" -lt "$_d_max_retry" ] ;
3235 _info
"Deactivate: $_d_domain"
3236 _d_i
="$(_math $_d_i + 1)"
3239 if ! __get_domain_new_authz
"$_d_domain" ; then
3240 _err
"Can not get domain new authz token."
3244 authzUri
="$(echo "$responseHeaders" | grep "^Location
:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
3245 _debug
"authzUri" "$authzUri"
3247 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
3248 _err
"new-authz error: $response"
3252 entry
="$(printf "%s
\n" "$response" | _egrep_o '[^\{]*"status
":"valid
","uri
"[^\}]*')"
3253 _debug entry
"$entry"
3255 if [ -z "$entry" ] ; then
3256 _info
"No more valid entry found."
3260 _vtype
="$(printf "%s
\n" "$entry" | _egrep_o '"type": *"[^
"]*"' | cut -d : -f 2 | tr -d '"')"
3261 _debug _vtype
$_vtype
3262 _info
"Found $_vtype"
3265 uri
="$(printf "%s
\n" "$entry" | _egrep_o '"uri
":"[^
"]*'| cut -d : -f 2,3 | tr -d '"' )"
3268 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ] ; then
3269 _info "Skip $_vtype"
3273 _info "Deactivate: $_vtype"
3275 if ! _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" ; then
3276 _err "Can not deactivate $_vtype."
3280 _info "Deactivate: $_vtype success."
3284 if [ "$_d_i" -lt "$_d_max_retry" ] ; then
3285 _info "Deactivated success!"
3287 _err "Deactivate failed."
3296 _debug _d_domain_list "$_d_domain_list"
3297 if [ -z "$(echo $_d_domain_list | cut -d , -f 1 )" ] ; then
3298 _usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
3301 for _d_dm in $(echo "$_d_domain_list" | tr ',' ' ' ) ;
3303 if [ -z "$_d_dm" ] || [ "$_d_dm" = "$NO_VALUE" ] ; then
3306 if ! _deactivate "$_d_dm" $_d_type ; then
3312 # Detect profile file if not specified as environment variable
3314 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
3320 SHELLTYPE="$(basename "/$SHELL")"
3322 if [ "$SHELLTYPE" = "bash" ] ; then
3323 if [ -f "$HOME/.bashrc" ] ; then
3324 DETECTED_PROFILE="$HOME/.bashrc"
3325 elif [ -f "$HOME/.bash_profile" ] ; then
3326 DETECTED_PROFILE="$HOME/.bash_profile"
3328 elif [ "$SHELLTYPE" = "zsh" ] ; then
3329 DETECTED_PROFILE="$HOME/.zshrc"
3332 if [ -z "$DETECTED_PROFILE" ] ; then
3333 if [ -f "$HOME/.profile" ] ; then
3334 DETECTED_PROFILE="$HOME/.profile"
3335 elif [ -f "$HOME/.bashrc" ] ; then
3336 DETECTED_PROFILE="$HOME/.bashrc"
3337 elif [ -f "$HOME/.bash_profile" ] ; then
3338 DETECTED_PROFILE="$HOME/.bash_profile"
3339 elif [ -f "$HOME/.zshrc" ] ; then
3340 DETECTED_PROFILE="$HOME/.zshrc"
3344 if [ ! -z "$DETECTED_PROFILE" ] ; then
3345 echo "$DETECTED_PROFILE"
3351 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
3352 echo "#ACCOUNT_CONF_PATH=xxxx
3354 #Account configurations:
3355 #Here are the supported macros, uncomment them to make them take effect.
3357 #ACCOUNT_EMAIL=aaa@example.com # the account email used to register account.
3358 #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
3359 #CERT_HOME=\"/path/to/cert/home\"
3363 #LOG_FILE=\"$DEFAULT_LOG_FILE\"
3368 #STAGE=1 # Use the staging api
3369 #FORCE=1 # Force to issue cert
3370 #DEBUG=1 # Debug mode
3373 #USER_AGENT=\"$USER_AGENT\"
3378 #######################
3381 #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
3383 #CF_Email=\"xxxx@sss.com\"
3385 #######################
3390 #DP_Key=\"sADDsdasdgdsf\"
3392 #######################
3396 #CX_Secret=\"sADDsdasdgdsf\"
3398 #######################
3400 #GD_Key=\"sdfdsgdgdfdasfds\"
3402 #GD_Secret=\"sADDsdasdfsdfdssdgdsf\"
3404 #######################
3406 #PDNS_Url=\"http://ns.example.com:8081\"
3407 #PDNS_ServerId=\"localhost\"
3408 #PDNS_Token=\"0123456789ABCDEF\"
3411 " > $ACCOUNT_CONF_PATH
3419 if ! _exists "curl" && ! _exists "wget"; then
3420 _err "Please install curl or wget first, we need to access http resources."
3424 if [ -z "$_nocron" ] ; then
3425 if ! _exists "crontab" ; then
3426 _err "It is recommended to install crontab first. try to install 'cron
, crontab
, crontabs or vixie-cron
'."
3427 _err "We need to set cron job to renew the certs automatically."
3428 _err "Otherwise, your certs will not be able to be renewed automatically."
3429 if [ -z "$FORCE" ] ; then
3430 _err "Please add '--force' and try install again to go without crontab."
3431 _err "./$PROJECT_ENTRY --install --force"
3437 if ! _exists "openssl" ; then
3438 _err "Please install openssl first."
3439 _err "We need openssl to generate keys."
3443 if ! _exists "nc" ; then
3444 _err "It is recommended to install nc first, try to install 'nc
' or 'netcat
'."
3445 _err "We use nc for standalone server if you use standalone mode."
3446 _err "If you don't use standalone mode
, just ignore this warning.
"
3455 if [ -z "$_shebang" ] ; then
3456 _usage "Usage
: file shebang
"
3459 cp "$_file" "$_file.tmp
"
3460 echo "$_shebang" > "$_file"
3461 sed -n 2,99999p "$_file.tmp
" >> "$_file"
3468 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env
"
3469 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ] ; then
3470 echo "$
(cat $_envfile)" | sed "s|^LE_WORKING_DIR.
*$||
" > "$_envfile"
3471 echo "$
(cat $_envfile)" | sed "s|^
alias le.
*$||
" > "$_envfile"
3472 echo "$
(cat $_envfile)" | sed "s|^
alias le.sh.
*$||
" > "$_envfile"
3475 _setopt "$_envfile" "export LE_WORKING_DIR
" "=" "\"$LE_WORKING_DIR\""
3476 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3478 _profile="$
(_detect_profile
)"
3479 if [ "$_profile" ] ; then
3480 _debug "Found profile
: $_profile"
3481 _info "Installing
alias to
'$_profile'"
3482 _setopt "$_profile" ".
\"$_envfile\""
3483 _info "OK
, Close and reopen your terminal to start using
$PROJECT_NAME"
3485 _info "No profile is found
, you will need to go into
$LE_WORKING_DIR to use
$PROJECT_NAME"
3490 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh
"
3491 _csh_profile="$HOME/.cshrc
"
3492 if [ -f "$_csh_profile" ] ; then
3493 _info "Installing
alias to
'$_csh_profile'"
3494 _setopt "$_cshfile" "setenv LE_WORKING_DIR
" " " "\"$LE_WORKING_DIR\""
3495 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3496 _setopt "$_csh_profile" "source \"$_cshfile\""
3500 _tcsh_profile="$HOME/.tcshrc
"
3501 if [ -f "$_tcsh_profile" ] ; then
3502 _info "Installing
alias to
'$_tcsh_profile'"
3503 _setopt "$_cshfile" "setenv LE_WORKING_DIR
" " " "\"$LE_WORKING_DIR\""
3504 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
3505 _setopt "$_tcsh_profile" "source \"$_cshfile\""
3513 if [ -z "$LE_WORKING_DIR" ] ; then
3514 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
3518 if ! _initpath ; then
3519 _err "Install failed.
"
3522 if [ "$_nocron" ] ; then
3523 _debug "Skip
install cron job
"
3526 if ! _precheck "$_nocron" ; then
3527 _err "Pre-check failed
, can not
install.
"
3532 if [ -d "$HOME/.le
" ] ; then
3533 for envfile in "le.env
" "le.sh.env
"
3535 if [ -f "$HOME/.le
/$envfile" ] ; then
3536 if grep "le.sh
" "$HOME/.le
/$envfile" >/dev/null ; then
3538 _info "You are upgrading from le.sh
"
3539 _info "Renaming
\"$HOME/.le
\" to
$LE_WORKING_DIR"
3540 mv "$HOME/.le
" "$LE_WORKING_DIR"
3541 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env
"
3548 _info "Installing to
$LE_WORKING_DIR"
3550 if ! mkdir -p "$LE_WORKING_DIR" ; then
3551 _err "Can not create working dir
: $LE_WORKING_DIR"
3555 chmod 700 "$LE_WORKING_DIR"
3557 cp $PROJECT_ENTRY "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
3559 if [ "$?
" != "0" ] ; then
3560 _err "Install failed
, can not copy
$PROJECT_ENTRY"
3564 _info "Installed to
$LE_WORKING_DIR/$PROJECT_ENTRY"
3568 for subf in $_SUB_FOLDERS ; do
3569 if [ -d "$subf" ] ; then
3570 mkdir -p $LE_WORKING_DIR/$subf
3571 cp $subf/* $LE_WORKING_DIR/$subf/
3576 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
3580 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ] ; then
3581 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH
" "=" "\"$ACCOUNT_CONF_PATH\""
3584 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ] ; then
3585 _saveaccountconf "CERT_HOME
" "$CERT_HOME"
3588 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ] ; then
3589 _saveaccountconf "ACCOUNT_KEY_PATH
" "$ACCOUNT_KEY_PATH"
3592 if [ -z "$_nocron" ] ; then
3596 if [ -z "$NO_DETECT_SH" ] ; then
3598 if _exists bash ; then
3599 _info "Good
, bash is found
, so change the shebang to use bash as prefered.
"
3600 _shebang='#!/usr/bin/env bash'
3601 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
3602 for subf in $_SUB_FOLDERS ; do
3603 if [ -d "$LE_WORKING_DIR/$subf" ] ; then
3604 for _apifile in "$LE_WORKING_DIR/$subf/"*.sh ; do
3605 _setShebang "$_apifile" "$_shebang"
3618 if [ -z "$_nocron" ] ; then
3625 rm -f $LE_WORKING_DIR/$PROJECT_ENTRY
3626 _info "The keys and certs are
in $LE_WORKING_DIR, you can remove them by yourself.
"
3633 _profile="$
(_detect_profile
)"
3634 if [ "$_profile" ] ; then
3635 _info "Uninstalling
alias from
: '$_profile'"
3636 text="$
(cat $_profile)"
3637 echo "$text" | sed "s|^.
*\"$LE_WORKING_DIR/$PROJECT_NAME.env
\"$||
" > "$_profile"
3640 _csh_profile="$HOME/.cshrc
"
3641 if [ -f "$_csh_profile" ] ; then
3642 _info "Uninstalling
alias from
: '$_csh_profile'"
3643 text="$
(cat $_csh_profile)"
3644 echo "$text" | sed "s|^.
*\"$LE_WORKING_DIR/$PROJECT_NAME.csh
\"$||
" > "$_csh_profile"
3647 _tcsh_profile="$HOME/.tcshrc
"
3648 if [ -f "$_tcsh_profile" ] ; then
3649 _info "Uninstalling
alias from
: '$_csh_profile'"
3650 text="$
(cat $_tcsh_profile)"
3651 echo "$text" | sed "s|^.
*\"$LE_WORKING_DIR/$PROJECT_NAME.csh
\"$||
" > "$_tcsh_profile"
3659 if [ "$AUTO_UPGRADE" = "1" ] ; then
3660 export LE_WORKING_DIR
3663 _err "Cron
:Upgrade failed
!"
3667 . $LE_WORKING_DIR/$PROJECT_ENTRY >/dev/null
3673 _info "Auto upgraded to
: $VER"
3689 echo "Usage
: $PROJECT_ENTRY command ...
[parameters
]....
3691 --help, -h Show this
help message.
3692 --version, -v Show version info.
3693 --install Install
$PROJECT_NAME to your system.
3694 --uninstall Uninstall
$PROJECT_NAME, and uninstall the cron job.
3695 --upgrade Upgrade
$PROJECT_NAME to the latest code from
$PROJECT .
3696 --issue Issue a cert.
3697 --signcsr Issue a cert from an existing csr.
3698 --deploy Deploy the cert to your server.
3699 --installcert Install the issued cert to apache
/nginx or any other server.
3700 --renew, -r Renew a cert.
3701 --renewAll Renew all the certs.
3702 --revoke Revoke a cert.
3703 --list List all the certs.
3704 --showcsr Show the content of a csr.
3705 --installcronjob Install the cron job to renew certs
, you don
't need to call this. The 'install' command can automatically install the cron job.
3706 --uninstallcronjob Uninstall the cron job. The 'uninstall
' command can do this automatically.
3707 --cron Run cron job to renew all the certs.
3708 --toPkcs Export the certificate and key to a pfx file.
3709 --updateaccount Update account info.
3710 --registeraccount Register account key.
3711 --createAccountKey, -cak Create an account private key, professional use.
3712 --createDomainKey, -cdk Create an domain private key, professional use.
3713 --createCSR, -ccsr Create CSR , professional use.
3714 --deactivate Deactivate the domain authz, professional use.
3717 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
3718 --force, -f Used to force to install or force to renew a cert immediately.
3719 --staging, --test Use staging server, just for test.
3720 --debug Output debug info.
3722 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
3723 --standalone Use standalone mode.
3724 --tls Use standalone tls mode.
3725 --apache Use apache mode.
3726 --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
3727 --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
3729 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
3730 --accountkeylength, -ak [2048] Specifies the account key length.
3731 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a
file path here.
3732 --log-level 1|
2 Specifies the log level
, default is
1.
3734 These parameters are to
install the cert to nginx
/apache or anyother server after issue
/renew a cert
:
3736 --certpath /path
/to
/real
/cert
/file After issue
/renew
, the cert will be copied to this path.
3737 --keypath /path
/to
/real
/key
/file After issue
/renew
, the key will be copied to this path.
3738 --capath /path
/to
/real
/ca
/file After issue
/renew
, the intermediate cert will be copied to this path.
3739 --fullchainpath /path
/to
/fullchain
/file After issue
/renew
, the fullchain cert will be copied to this path.
3741 --reloadcmd \"service nginx reload
\" After issue
/renew
, it
's used to reload the server.
3743 --accountconf Specifies a customized account config file.
3744 --home Specifies the home dir for $PROJECT_NAME .
3745 --certhome Specifies the home dir to save all the certs, only valid for '--install' command.
3746 --useragent Specifies the user agent string. it will be saved for future use too.
3747 --accountemail Specifies the account email for registering, Only valid for the '--install' command.
3748 --accountkey Specifies the account key path, Only valid for the '--install' command.
3749 --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
3750 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
3751 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
3752 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
3753 --listraw Only used for '--list' command, list the certs in raw format.
3754 --stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal.
3755 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
3756 --ca-bundle Specifices the path to the CA certificate bundle to verify api server
's certificate.
3757 --nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
3758 --ecc Specifies to use the ECC cert. Valid for '--installcert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
3759 --csr Specifies the input csr.
3760 --pre-hook Command to be run before obtaining any certificates.
3761 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obain/renew is success or failed.
3762 --renew-hook Command to be run once for each successfully renewed certificate.
3763 --deploy-hook The hook file to deploy cert
3764 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
3765 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
3766 --listen-v4 Force standalone/tls server to listen at ipv4.
3767 --listen-v6 Force standalone/tls server to listen at ipv6.
3773 _info "Installing from online archive."
3775 if [ ! "$BRANCH" ] ; then
3779 target="$PROJECT/archive/$BRANCH.tar.gz"
3780 _info "Downloading $target"
3781 localname="$BRANCH.tar.gz"
3782 if ! _get "$target" > $localname ; then
3783 _err "Download error."
3787 _info "Extracting $localname"
3790 cd "$PROJECT_NAME-$BRANCH"
3791 chmod +x $PROJECT_ENTRY
3792 if ./$PROJECT_ENTRY install "$_nocron" ; then
3793 _info "Install success!"
3798 rm -rf "$PROJECT_NAME-$BRANCH"
3806 export LE_WORKING_DIR
3807 cd "$LE_WORKING_DIR"
3808 _installOnline "nocron"
3810 _info "Upgrade success!"
3813 _err "Upgrade failed!"
3818 _processAccountConf() {
3819 if [ "$_useragent" ] ; then
3820 _saveaccountconf "USER_AGENT" "$_useragent"
3821 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ] ; then
3822 _saveaccountconf "USER_AGENT" "$USER_AGENT"
3825 if [ "$_accountemail" ] ; then
3826 _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
3827 elif [ "$ACCOUNT_EMAIL" ] && [ "$ACCOUNT_EMAIL" != "$DEFAULT_ACCOUNT_EMAIL" ] ; then
3828 _saveaccountconf "ACCOUNT_EMAIL" "$ACCOUNT_EMAIL"
3831 if [ "$_auto_upgrade" ] ; then
3832 _saveaccountconf "AUTO_UPGRADE" "$_auto_upgrade"
3833 elif [ "$AUTO_UPGRADE" ] ; then
3834 _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
3842 _altdomains="$NO_VALUE"
3845 _accountkeylength=""
3861 _stopRenewOnError=""
3878 while [ ${#} -gt 0 ] ; do
3916 --renewAll|--renewall)
3926 _CMD="installcronjob"
3929 _CMD="uninstallcronjob"
3937 --createAccountKey|--createaccountkey|-cak)
3938 _CMD="createAccountKey"
3940 --createDomainKey|--createdomainkey|-cdk)
3941 _CMD="createDomainKey"
3943 --createCSR|--createcsr|-ccr)
3950 _CMD="updateaccount"
3953 _CMD="registeraccount"
3958 if [ "$_dvalue" ] ; then
3959 if _startswith "$_dvalue" "-" ; then
3960 _err "'$_dvalue' is not a valid domain for parameter '$1'"
3963 if _is_idn "$_dvalue" && ! _exists idn ; then
3964 _err "It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn
' command first."
3968 if [ -z "$_domain" ] ; then
3971 if [ "$_altdomains" = "$NO_VALUE" ] ; then
3972 _altdomains="$_dvalue"
3974 _altdomains="$_altdomains,$_dvalue"
3989 if [ -z "$2" ] || _startswith "$2" "-" ; then
3998 if [ -z "$_webroot" ] ; then
4001 _webroot="$_webroot,$wvalue"
4007 if [ -z "$_webroot" ] ; then
4010 _webroot="$_webroot,$wvalue"
4015 _local_address="$_local_address$lvalue,"
4020 if [ -z "$_webroot" ] ; then
4023 _webroot="$_webroot,$wvalue"
4028 if [ -z "$_webroot" ] ; then
4031 _webroot="$_webroot,$wvalue"
4036 if ! _startswith "$2" "-" ; then
4040 if [ -z "$_webroot" ] ; then
4043 _webroot="$_webroot,$wvalue"
4048 Le_DNSSleep="$_dnssleep"
4054 if [ "$_accountkeylength" = "$NO_VALUE" ] ; then
4055 _accountkeylength="$2"
4059 --accountkeylength|-ak)
4060 _accountkeylength="$2"
4080 --reloadcmd|--reloadCmd)
4090 ACCOUNT_CONF_PATH="$_accountconf"
4099 CERT_HOME="$_certhome"
4104 USER_AGENT="$_useragent"
4109 ACCOUNT_EMAIL="$_accountemail"
4114 ACCOUNT_KEY_PATH="$_accountkey"
4119 Le_RenewalDays="$_days"
4124 Le_HTTPPort="$_httpport"
4129 Le_TLSPort="$_tlsport"
4136 --stopRenewOnError|--stoprenewonerror|-se )
4137 _stopRenewOnError="1"
4144 _ca_bundle="$(readlink -f $2)"
4145 CA_BUNDLE="$_ca_bundle"
4174 --ocsp-must-staple|--ocsp)
4180 if _startswith "$_logfile" '-' ; then
4185 LOG_FILE="$_logfile"
4186 if [ -z "$LOG_LEVEL" ] ; then
4187 LOG_LEVEL="$DEFAULT_LOG_LEVEL"
4192 LOG_LEVEL="$_log_level"
4197 if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-' ; then
4202 AUTO_UPGRADE="$_auto_upgrade"
4206 Le_Listen_V4="$_listen_v4"
4210 Le_Listen_V6="$_listen_v6"
4214 _err "Unknown parameter : $1"
4222 if [ "${_CMD}" != "install" ] ; then
4224 if [ "$_log" ]; then
4225 if [ -z "$_logfile" ] ; then
4226 _logfile="$DEFAULT_LOG_FILE"
4229 if [ "$_logfile" ] ; then
4230 _saveaccountconf "LOG_FILE" "$_logfile"
4231 LOG_FILE="$_logfile"
4234 if [ "$_log_level" ] ; then
4235 _saveaccountconf "LOG_LEVEL" "$_log_level"
4236 LOG_LEVEL="$_log_level"
4242 if [ "$DEBUG" ] ; then
4247 install) install "$_nocron" ;;
4248 uninstall) uninstall "$_nocron" ;;
4251 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address"
4254 deploy "$_domain" "$_deploy_hook" "$_ecc"
4257 signcsr "$_csr" "$_webroot"
4260 showcsr "$_csr" "$_domain"
4263 installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc"
4266 renew "$_domain" "$_ecc"
4269 renewAll "$_stopRenewOnError"
4272 revoke "$_domain" "$_ecc"
4275 deactivate "$_domain,$_altdomains"
4286 installcronjob) installcronjob ;;
4287 uninstallcronjob) uninstallcronjob ;;
4290 toPkcs "$_domain" "$_password" "$_ecc"
4293 createAccountKey "$_accountkeylength"
4296 createDomainKey "$_domain" "$_keylength"
4299 createCSR "$_domain" "$_altdomains" "$_ecc"
4303 _err "Invalid command: $_CMD"
4309 if [ "$_ret" != "0" ] ; then
4313 if [ "${_CMD}" = "install" ] ; then
4314 if [ "$_log" ] ; then
4315 if [ -z "$LOG_FILE" ] ; then
4316 LOG_FILE="$DEFAULT_LOG_FILE"
4318 _saveaccountconf "LOG_FILE" "$LOG_FILE"
4321 if [ "$_log_level" ] ; then
4322 _saveaccountconf "LOG_LEVEL" "$_log_level"
4330 if [ "$INSTALLONLINE" ] ; then
4332 _installOnline $BRANCH
4341 [ -z "$1" ] && showhelp && return
4342 if _startswith "$1" '-' ; then _process "$@"; else "$@";fi