7 PROJECT_ENTRY
="acme.sh"
9 PROJECT
="https://github.com/acmesh-official/$PROJECT_NAME"
11 DEFAULT_INSTALL_HOME
="$HOME/.$PROJECT_NAME"
13 _WINDOWS_SCHEDULER_NAME
="$PROJECT_NAME.cron"
17 _SUB_FOLDER_NOTIFY
="notify"
18 _SUB_FOLDER_DNSAPI
="dnsapi"
19 _SUB_FOLDER_DEPLOY
="deploy"
21 _SUB_FOLDERS
="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
23 LETSENCRYPT_CA_V1
="https://acme-v01.api.letsencrypt.org/directory"
24 LETSENCRYPT_STAGING_CA_V1
="https://acme-staging.api.letsencrypt.org/directory"
26 CA_LETSENCRYPT_V2
="https://acme-v02.api.letsencrypt.org/directory"
27 CA_LETSENCRYPT_V2_TEST
="https://acme-staging-v02.api.letsencrypt.org/directory"
29 CA_BUYPASS
="https://api.buypass.com/acme/directory"
30 CA_BUYPASS_TEST
="https://api.test4.buypass.no/acme/directory"
32 CA_ZEROSSL
="https://acme.zerossl.com/v2/DV90"
33 _ZERO_EAB_ENDPOINT
="http://api.zerossl.com/acme/eab-credentials-email"
35 DEFAULT_CA
=$CA_LETSENCRYPT_V2
36 DEFAULT_STAGING_CA
=$CA_LETSENCRYPT_V2_TEST
39 LetsEncrypt.org,letsencrypt
40 LetsEncrypt.org_test,letsencrypt_test,letsencrypttest
42 BuyPass.com_test,buypass_test,buypasstest
46 CA_SERVERS
="$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_ZEROSSL"
48 DEFAULT_USER_AGENT
="$PROJECT_NAME/$VER ($PROJECT)"
50 DEFAULT_ACCOUNT_KEY_LENGTH
=2048
51 DEFAULT_DOMAIN_KEY_LENGTH
=2048
53 DEFAULT_OPENSSL_BIN
="openssl"
57 VTYPE_ALPN
="tls-alpn-01"
59 LOCAL_ANY_ADDRESS
="0.0.0.0"
69 MODE_STATELESS
="stateless"
71 STATE_VERIFIED
="verified_ok"
74 NGINX_START
="#ACME_NGINX_START"
75 NGINX_END
="#ACME_NGINX_END"
77 BEGIN_CSR
="-----BEGIN CERTIFICATE REQUEST-----"
78 END_CSR
="-----END CERTIFICATE REQUEST-----"
80 BEGIN_CERT
="-----BEGIN CERTIFICATE-----"
81 END_CERT
="-----END CERTIFICATE-----"
83 CONTENT_TYPE_JSON
="application/jose+json"
86 B64CONF_START
="__ACME_BASE64__START_"
87 B64CONF_END
="__ACME_BASE64__END_"
90 ECC_SUFFIX
="${ECC_SEP}ecc"
95 DEFAULT_LOG_LEVEL
="$LOG_LEVEL_1"
100 DEBUG_LEVEL_DEFAULT
=$DEBUG_LEVEL_1
106 HIDDEN_VALUE
="[hidden](please add '--output-insecure' to see this value)"
108 SYSLOG_ERROR
="user.error"
109 SYSLOG_INFO
="user.info"
110 SYSLOG_DEBUG
="user.debug"
119 SYSLOG_LEVEL_DEBUG_2
=8
121 SYSLOG_LEVEL_DEBUG_3
=9
123 SYSLOG_LEVEL_DEFAULT
=$SYSLOG_LEVEL_ERROR
127 NOTIFY_LEVEL_DISABLE
=0
132 NOTIFY_LEVEL_DEFAULT
=$NOTIFY_LEVEL_RENEW
137 NOTIFY_MODE_DEFAULT
=$NOTIFY_MODE_BULK
139 _DEBUG_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh"
141 _PREPARE_LINK
="https://github.com/acmesh-official/acme.sh/wiki/Install-preparations"
143 _STATELESS_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode"
145 _DNS_ALIAS_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode"
147 _DNS_MANUAL_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode"
149 _DNS_API_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/dnsapi"
151 _NOTIFY_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/notify"
153 _SUDO_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/sudo"
155 _REVOKE_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/revokecert"
157 _ZEROSSL_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA"
159 _SERVER_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/Server"
161 _PREFERRED_CHAIN_WIKI
="https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain"
163 _DNS_MANUAL_ERR
="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
165 _DNS_MANUAL_WARN
="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
167 _DNS_MANUAL_ERROR
="It seems that you are using dns manual mode. Read this link first: $_DNS_MANUAL_WIKI"
175 if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
176 printf '\33[1;32m%b\33[0m' "$1"
183 if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
184 printf '\33[1;31m%b\33[0m' "$1"
192 if [ -z "$NO_TIMESTAMP" ] ||
[ "$NO_TIMESTAMP" = "0" ]; then
193 printf -- "%s" "[$(date)] "
198 printf -- "%s" "$1='$2'"
201 # return the saved exit status
202 return "$_exitstatus"
206 echo "Diagnosis versions: "
207 echo "openssl:$ACME_OPENSSL_BIN"
208 if _exists
"${ACME_OPENSSL_BIN:-openssl}"; then
209 ${ACME_OPENSSL_BIN:-openssl} version
2>&1
211 echo "$ACME_OPENSSL_BIN doesn't exist."
215 if [ "$_APACHECTL" ] && _exists
"$_APACHECTL"; then
218 echo "apache doesn't exist."
222 if _exists
"nginx"; then
225 echo "nginx doesn't exist."
229 if _exists
"socat"; then
232 _debug
"socat doesn't exist."
239 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
244 if [ -z "$__logger_i" ]; then
245 if _contains
"$(logger --help 2>&1)" "-i"; then
246 __logger_i
="logger -i"
251 $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@
")" >/dev
/null
2>&1
252 return "$_exitstatus"
256 [ -z "$LOG_FILE" ] && return
257 _printargs
"$@" >>"$LOG_FILE"
262 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_INFO" ]; then
263 _syslog
"$SYSLOG_INFO" "$@"
269 _syslog
"$SYSLOG_ERROR" "$@"
271 if [ -z "$NO_TIMESTAMP" ] ||
[ "$NO_TIMESTAMP" = "0" ]; then
272 printf -- "%s" "[$(date)] " >&2
288 __debug_bash_helper
() {
289 # At this point only do for --debug 3
290 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -lt "$DEBUG_LEVEL_3" ]; then
293 # Return extra debug info when running with bash, otherwise return empty
295 if [ -z "${BASH_VERSION}" ]; then
298 # We are a bash shell at this point, return the filename, function name, and
299 # line number as a string
302 # Must use eval or syntax error happens under dash. The eval should use
303 # single quotes as older versions of busybox had a bug with double quotes and
305 # Use 'caller 1' as we want one level up the stack as we should be called
306 # by one of the _debug* functions
307 eval '_dbh_called=($(caller 1))'
309 eval '_dbh_file=${_dbh_called[2]}'
310 if [ -n "${_script_home}" ]; then
311 # Trim off the _script_home directory name
312 eval '_dbh_file=${_dbh_file#$_script_home/}'
314 eval '_dbh_function=${_dbh_called[1]}'
315 eval '_dbh_lineno=${_dbh_called[0]}'
316 printf "%-40s " "$_dbh_file:${_dbh_function}:${_dbh_lineno}"
320 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
323 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
324 _syslog
"$SYSLOG_DEBUG" "$@"
326 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
327 _bash_debug
=$
(__debug_bash_helper
)
328 _printargs
"${_bash_debug}$@" >&2
332 #output the sensitive messages
334 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
335 if [ "$OUTPUT_INSECURE" = "1" ]; then
338 _log
"$1" "$HIDDEN_VALUE"
341 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
342 _syslog
"$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
344 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
345 if [ "$OUTPUT_INSECURE" = "1" ]; then
348 _printargs
"$1" "$HIDDEN_VALUE" >&2
354 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
357 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
358 _syslog
"$SYSLOG_DEBUG" "$@"
360 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
361 _bash_debug
=$
(__debug_bash_helper
)
362 _printargs
"${_bash_debug}$@" >&2
367 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
368 if [ "$OUTPUT_INSECURE" = "1" ]; then
371 _log
"$1" "$HIDDEN_VALUE"
374 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
375 _syslog
"$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
377 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
378 if [ "$OUTPUT_INSECURE" = "1" ]; then
381 _printargs
"$1" "$HIDDEN_VALUE" >&2
387 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
390 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
391 _syslog
"$SYSLOG_DEBUG" "$@"
393 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
394 _bash_debug
=$
(__debug_bash_helper
)
395 _printargs
"${_bash_debug}$@" >&2
400 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
401 if [ "$OUTPUT_INSECURE" = "1" ]; then
404 _log
"$1" "$HIDDEN_VALUE"
407 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
408 _syslog
"$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
410 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
411 if [ "$OUTPUT_INSECURE" = "1" ]; then
414 _printargs
"$1" "$HIDDEN_VALUE" >&2
420 # shellcheck disable=SC2018,SC2019
425 # shellcheck disable=SC2018,SC2019
432 echo "$_str" |
grep "^$_sub" >/dev
/null
2>&1
438 echo "$_str" |
grep -- "$_sub\$" >/dev
/null
2>&1
444 echo "$_str" |
grep -- "$_sub" >/dev
/null
2>&1
451 if [ -z "$_field" ]; then
452 _usage
"Usage: str field [sep]"
456 if [ -z "$_sep" ]; then
460 for f
in $
(echo "$_str" |
tr "$_sep" ' '); do
461 if [ "$f" = "$_field" ]; then
462 _debug2
"'$_str' contains '$_field'"
463 return 0 #contains ok
466 _debug2
"'$_str' does not contain '$_field'"
467 return 1 #not contains
476 if [ -z "$_findex" ]; then
477 _usage
"Usage: str field [sep]"
481 if [ -z "$_sep" ]; then
486 while [ "$_ffi" -gt "0" ]; do
487 _fv
="$(echo "$_str" | cut -d "$_sep" -f "$_ffi")"
489 printf -- "%s" "$_fv"
492 _ffi
="$(_math "$_ffi" - 1)"
495 printf -- "%s" "$_str"
501 if [ -z "$cmd" ]; then
502 _usage
"Usage: _exists cmd"
506 if eval type type >/dev
/null
2>&1; then
507 eval type "$cmd" >/dev
/null
2>&1
508 elif command >/dev
/null
2>&1; then
509 command -v "$cmd" >/dev
/null
2>&1
511 which "$cmd" >/dev
/null
2>&1
514 _debug3
"$cmd exists=$ret"
521 printf "%s" "$(($_m_opts))"
553 if [ "$(printf '\x41')" != 'A' ]; then
558 if _exists
xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
563 if _exists xxd
&& xxd
-r -p 2>/dev
/null
; then
570 _debug2 _URGLY_PRINTF
"$_URGLY_PRINTF"
571 if [ -z "$_URGLY_PRINTF" ]; then
572 if [ "$_ESCAPE_XARGS" ] && _exists
xargs; then
574 echo "$hex" | _upper_case |
sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' |
xargs printf
576 for h
in $
(echo "$hex" | _upper_case |
sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do
584 for c
in $
(echo "$hex" | _upper_case |
sed 's/\([0-9A-F]\)/ \1/g'); do
585 if [ -z "$ic" ]; then
590 ic
="$(_h_char_2_dec "$ic")"
591 jc
="$(_h_char_2_dec "$jc")"
592 printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s"
601 _contains "${__OS__:=$(uname -a)}" "solaris" || _contains "${__OS__:=$(uname -a)}" "SunOS"
605 #this can only process ascii chars, should only be used when od command is missing as a backup way.
607 _debug2 "Using _ascii_hex"
611 while [ "$_h_i" -le "$_str_len" ]; do
612 _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
613 printf " %02x" "'$_str_c"
614 _h_i="$
(_math
"$_h_i" + 1)"
618 #stdin output hexstr splited by one space
623 od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n"
624 elif _exists hexdump; then
625 _debug3 "using
hexdump"
626 hexdump -v -e '/1 ""' -e '/1 " %02x
" ""'
627 elif _exists xxd; then
629 xxd -ps -c 20 -i | sed "s
/ 0x
/ /g
" | tr -d ",\n" | tr -s " "
631 _debug3 "using _ascii_hex
"
637 #url encode, no-preserved chars
638 #A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
639 #41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a
641 #a b c d e f g h i j k l m n o p q r s t u v w x y z
642 #61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a
644 #0 1 2 3 4 5 6 7 8 9 - _ . ~
645 #30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
649 _hex_str=$(_hex_dump)
650 _debug3 "_url_encode
"
651 _debug3 "_hex_str
" "$_hex_str"
652 for _hex_code in $_hex_str; do
654 case "${_hex_code}" in
858 printf '%%%s' "$_hex_code"
865 _j_str="$
(sed 's/"/\\"/g' |
sed "s/\r/\\r/g")"
866 _debug3 "_json_encode
"
867 _debug3 "_j_str
" "$_j_str"
868 echo "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
871 #from: http:\/\/ to http://
873 _j_str="$
(sed 's#\\/#/#g')"
874 _debug3 "_json_decode
"
875 _debug3 "_j_str
" "$_j_str"
883 if [ -z "$filename" ]; then
884 _usage "Usage
:_sed_i options filename
"
887 _debug2 options "$options"
888 if sed -h 2>&1 | grep "\
-i\
[SUFFIX
]" >/dev/null 2>&1; then
889 _debug "Using
sed -i"
890 sed -i "$options" "$filename"
892 _debug "No
-i support
in sed"
893 text="$
(cat "$filename")"
894 echo "$text" | sed "$options" >"$filename"
899 if ! egrep -o "$1" 2>/dev/null; then
900 sed -n 's/.*\('"$1"'\).*/\1/p'
904 #Usage: file startline endline
909 if [ -z "$endline" ]; then
910 _usage "Usage
: file startline endline
"
914 i="$
(grep -n -- "$startline" "$filename" | cut
-d : -f 1)"
916 _err "Can not
find start line
: $startline"
919 i="$
(_math
"$i" + 1)"
922 j="$
(grep -n -- "$endline" "$filename" | cut
-d : -f 1)"
924 _err "Can not
find end line
: $endline"
927 j="$
(_math
"$j" - 1)"
930 sed -n "$i,${j}p
" "$filename"
938 _debug3 "base64 multiline
:'$1'"
939 ${ACME_OPENSSL_BIN:-openssl} base64 -e
941 _debug3 "base64 single line.
"
942 ${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
949 ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
951 ${ACME_OPENSSL_BIN:-openssl} base64 -d
958 if [ "$DEBUG" ]; then
959 openssl x509 -noout -text -in "$_cf"
961 openssl x509 -noout -text -in "$_cf" >/dev/null 2>&1
965 #Usage: hashalg [outputhex]
966 #Output Base64-encoded digest
969 if [ -z "$alg" ]; then
970 _usage "Usage
: _digest hashalg
"
976 if [ "$alg" = "sha256
" ] || [ "$alg" = "sha1
" ] || [ "$alg" = "md5
" ]; then
977 if [ "$outputhex" ]; then
978 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
980 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
983 _err "$alg is not supported yet
"
989 #Usage: hashalg secret_hex [outputhex]
996 if [ -z "$secret_hex" ]; then
997 _usage "Usage
: _hmac hashalg secret
[outputhex
]"
1001 if [ "$alg" = "sha256
" ] || [ "$alg" = "sha1
" ]; then
1002 if [ "$outputhex" ]; then
1003 (${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey
:$secret_hex" 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$
(printf "%s" "$secret_hex" | _h2b
)") | cut -d = -f 2 | tr -d ' '
1005 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey
:$secret_hex" -binary 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$
(printf "%s" "$secret_hex" | _h2b
)" -binary
1008 _err "$alg is not supported yet
"
1014 #Usage: keyfile hashalg
1015 #Output: Base64-encoded signature value
1019 if [ -z "$alg" ]; then
1020 _usage "Usage
: _sign keyfile hashalg
"
1024 _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst
-sign $keyfile "
1026 if grep "BEGIN RSA PRIVATE KEY
" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY
" "$keyfile" >/dev/null 2>&1; then
1027 $_sign_openssl -$alg | _base64
1028 elif grep "BEGIN EC PRIVATE KEY
" "$keyfile" >/dev/null 2>&1; then
1029 if ! _signedECText="$
($_sign_openssl -sha$__ECC_KEY_LEN |
${ACME_OPENSSL_BIN:-openssl} asn1parse
-inform DER
)"; then
1030 _err "Sign failed
: $_sign_openssl"
1031 _err "Key
file: $keyfile"
1032 _err "Key content
:$
(wc -l <"$keyfile") lines
"
1035 _debug3 "_signedECText
" "$_signedECText"
1036 _ec_r="$
(echo "$_signedECText" | _head_n
2 | _tail_n
1 | cut
-d : -f 4 |
tr -d "\r\n")"
1037 _ec_s="$
(echo "$_signedECText" | _head_n
3 | _tail_n
1 | cut
-d : -f 4 |
tr -d "\r\n")"
1038 if [ "$__ECC_KEY_LEN" -eq "256" ]; then
1039 while [ "${#_ec_r}" -lt "64" ]; do
1042 while [ "${#_ec_s}" -lt "64" ]; do
1046 if [ "$__ECC_KEY_LEN" -eq "384" ]; then
1047 while [ "${#_ec_r}" -lt "96" ]; do
1050 while [ "${#_ec_s}" -lt "96" ]; do
1054 if [ "$__ECC_KEY_LEN" -eq "512" ]; then
1055 while [ "${#_ec_r}" -lt "132" ]; do
1058 while [ "${#_ec_s}" -lt "132" ]; do
1062 _debug3 "_ec_r
" "$_ec_r"
1063 _debug3 "_ec_s
" "$_ec_s"
1064 printf "%s
" "$_ec_r$_ec_s" | _h2b | _base64
1066 _err "Unknown key
file format.
"
1072 #keylength or isEcc flag (empty str => not ecc)
1076 if [ -z "$_length" ]; then
1080 [ "$_length" != "1024" ] &&
1081 [ "$_length" != "2048" ] &&
1082 [ "$_length" != "3072" ] &&
1083 [ "$_length" != "4096" ] &&
1084 [ "$_length" != "8192" ]
1087 # _createkey 2048|ec-256 file
1091 _debug2 "_createkey
for file:$f"
1093 if _startswith "$length" "ec-
"; then
1094 length=$(printf "%s
" "$length" | cut -d '-' -f 2-100)
1096 if [ "$length" = "256" ]; then
1097 eccname="prime256v1
"
1099 if [ "$length" = "384" ]; then
1102 if [ "$length" = "521" ]; then
1108 if [ -z "$length" ]; then
1112 _debug "Use length
$length"
1114 if ! touch "$f" >/dev/null 2>&1; then
1115 _f_path="$
(dirname "$f")"
1116 _debug _f_path "$_f_path"
1117 if ! mkdir -p "$_f_path"; then
1118 _err "Can not create path
: $_f_path"
1123 if _isEccKey "$length"; then
1124 _debug "Using ec name
: $eccname"
1125 if _opkey="$
(${ACME_OPENSSL_BIN:-openssl} ecparam
-name "$eccname" -genkey 2>/dev
/null
)"; then
1126 echo "$_opkey" >"$f"
1128 _err "error ecc key name
: $eccname"
1132 _debug "Using RSA
: $length"
1133 if _opkey="$
(${ACME_OPENSSL_BIN:-openssl} genrsa
"$length" 2>/dev
/null
)"; then
1134 echo "$_opkey" >"$f"
1136 _err "error rsa key
: $length"
1141 if [ "$?
" != "0" ]; then
1142 _err "Create key error.
"
1150 _debug2 _is_idn_d "$_is_idn_d"
1151 _idn_temp=$(printf "%s
" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-_')
1152 _debug2 _idn_temp "$_idn_temp"
1157 #aa.com,bb.com,cc.com
1160 if ! _is_idn "$__idn_d"; then
1161 printf "%s
" "$__idn_d"
1165 if _exists idn; then
1166 if _contains "$__idn_d" ','; then
1168 for f in $(echo "$__idn_d" | tr ',' ' '); do
1169 [ -z "$f" ] && continue
1170 if [ -z "$_i_first" ]; then
1175 idn --quiet "$f" | tr -d "\r\n"
1178 idn "$__idn_d" | tr -d "\r\n"
1181 _err "Please
install idn to process IDN names.
"
1185 #_createcsr cn san_list keyfile csrfile conf acmeValidationv1
1193 acmeValidationv1="$6"
1194 _debug2 domain "$domain"
1195 _debug2 domainlist "$domainlist"
1196 _debug2 csrkey "$csrkey"
1198 _debug2 csrconf "$csrconf"
1200 printf "[ req_distinguished_name
]\n[ req
]\ndistinguished_name
= req_distinguished_name
\nreq_extensions
= v3_req
\n[ v3_req
]\n\nkeyUsage
= nonRepudiation
, digitalSignature
, keyEncipherment
" >"$csrconf"
1202 if [ "$acmeValidationv1" ]; then
1203 domainlist="$
(_idn
"$domainlist")"
1204 printf -- "\nsubjectAltName
=DNS
:$domainlist" >>"$csrconf"
1205 elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
1207 _info "Single domain
" "$domain"
1208 printf -- "\nsubjectAltName
=DNS
:$
(_idn
"$domain")" >>"$csrconf"
1210 domainlist="$
(_idn
"$domainlist")"
1211 _debug2 domainlist "$domainlist"
1212 if _contains "$domainlist" ","; then
1213 alt="DNS
:$
(_idn
"$domain"),DNS
:$
(echo "$domainlist" |
sed "s/,,/,/g" |
sed "s/,/,DNS:/g")"
1215 alt="DNS
:$
(_idn
"$domain"),DNS
:$domainlist"
1218 _info "Multi domain
" "$alt"
1219 printf -- "\nsubjectAltName
=$alt" >>"$csrconf"
1221 if [ "$Le_OCSP_Staple" = "1" ]; then
1222 _savedomainconf Le_OCSP_Staple "$Le_OCSP_Staple"
1223 printf -- "\nbasicConstraints
= CA
:FALSE
\n1.3.6.1.5.5.7.1.24=DER
:30:03:02:01:05" >>"$csrconf"
1226 if [ "$acmeValidationv1" ]; then
1227 printf "\n1.3.6.1.5.5.7.1.31=critical
,DER
:04:20:${acmeValidationv1}" >>"${csrconf}"
1230 _csr_cn="$
(_idn
"$domain")"
1231 _debug2 _csr_cn "$_csr_cn"
1232 if _contains "$
(uname
-a)" "MINGW
"; then
1233 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN
=$_csr_cn" -config "$csrconf" -out "$csr"
1235 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN
=$_csr_cn" -config "$csrconf" -out "$csr"
1239 #_signcsr key csr conf cert
1247 _msg="$
(${ACME_OPENSSL_BIN:-openssl} x509
-req -days 365 -in "$csr" -signkey "$key" -extensions v3_req
-extfile "$conf" -out "$cert" 2>&1)"
1254 _readSubjectFromCSR() {
1256 if [ -z "$_csrfile" ]; then
1257 _usage "_readSubjectFromCSR mycsr.csr
"
1260 ${ACME_OPENSSL_BIN:-openssl} req -noout -in "$_csrfile" -subject | tr ',' "\n" | _egrep_o "CN
*=.
*" | cut -d = -f 2 | cut -d / -f 1 | tr -d ' \n'
1264 #echo comma separated domain list
1265 _readSubjectAltNamesFromCSR() {
1267 if [ -z "$_csrfile" ]; then
1268 _usage "_readSubjectAltNamesFromCSR mycsr.csr
"
1272 _csrsubj="$
(_readSubjectFromCSR
"$_csrfile")"
1273 _debug _csrsubj "$_csrsubj"
1275 _dnsAltnames="$
(${ACME_OPENSSL_BIN:-openssl} req
-noout -text -in "$_csrfile" |
grep "^ *DNS:.*" |
tr -d ' \n')"
1276 _debug _dnsAltnames "$_dnsAltnames"
1278 if _contains "$_dnsAltnames," "DNS
:$_csrsubj,"; then
1279 _debug "AltNames contains subject
"
1280 _excapedAlgnames="$
(echo "$_dnsAltnames" |
tr '*' '#')"
1281 _debug _excapedAlgnames "$_excapedAlgnames"
1282 _escapedSubject="$
(echo "$_csrsubj" |
tr '*' '#')"
1283 _debug _escapedSubject "$_escapedSubject"
1284 _dnsAltnames="$
(echo "$_excapedAlgnames," |
sed "s/DNS:$_escapedSubject,//g" |
tr '#' '*' |
sed "s/,\$//g")"
1285 _debug _dnsAltnames "$_dnsAltnames"
1287 _debug "AltNames doesn
't contain subject"
1290 echo "$_dnsAltnames" | sed "s/DNS://g"
1294 _readKeyLengthFromCSR() {
1296 if [ -z "$_csrfile" ]; then
1297 _usage "_readKeyLengthFromCSR mycsr.csr"
1301 _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
1302 _debug2 _outcsr "$_outcsr"
1303 if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
1305 echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
1308 _rkl="$(echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1)"
1309 if [ "$_rkl" ]; then
1312 echo "$_outcsr" | tr "\t" " " | _egrep_o "RSA Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
1320 if _exists "ss"; then
1322 ss -ntpl 2>/dev/null | grep ":$_port "
1326 if _exists "netstat"; then
1327 _debug "Using: netstat"
1328 if netstat -help 2>&1 | grep "\-p proto" >/dev/null; then
1329 #for windows version netstat tool
1330 netstat -an -p tcp | grep "LISTENING" | grep ":$_port "
1332 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null; then
1333 netstat -an -p tcp | grep LISTEN | grep ":$_port "
1334 elif netstat -help 2>&1 | grep -- '-P protocol
' >/dev/null; then
1336 netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
1337 elif netstat -help 2>&1 | grep "\-p" >/dev/null; then
1339 netstat -ntpl | grep ":$_port "
1341 #for busybox (embedded linux; no pid support)
1342 netstat -ntl 2>/dev/null | grep ":$_port "
1351 #outfile key cert cacert [password [name [caname]]]
1361 if [ "$pfxCaname" ]; then
1362 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname"
1363 elif [ "$pfxName" ]; then
1364 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName"
1365 elif [ "$pfxPassword" ]; then
1366 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword"
1368 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
1373 #domain [password] [isEcc]
1377 if [ -z "$domain" ]; then
1378 _usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
1384 _initpath "$domain" "$_isEcc"
1386 _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword"
1388 if [ "$?" = "0" ]; then
1389 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
1398 if [ -z "$domain" ]; then
1399 _usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]"
1405 _initpath "$domain" "$_isEcc"
1407 ${ACME_OPENSSL_BIN:-openssl} pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH"
1409 if [ "$?" = "0" ]; then
1410 _info "Success, $CERT_PKCS8_PATH"
1416 createAccountKey() {
1417 _info "Creating account key"
1418 if [ -z "$1" ]; then
1419 _usage "Usage: $PROJECT_ENTRY --createAccountKey --accountkeylength 2048"
1424 _create_account_key "$length"
1428 _create_account_key() {
1432 if [ -z "$length" ] || [ "$length" = "$NO_VALUE" ]; then
1433 _debug "Use default length $DEFAULT_ACCOUNT_KEY_LENGTH"
1434 length="$DEFAULT_ACCOUNT_KEY_LENGTH"
1437 _debug length "$length"
1441 if [ -s "$ACCOUNT_KEY_PATH" ]; then
1442 _info "Account key exists, skip"
1445 #generate account key
1446 if _createkey "$length" "$ACCOUNT_KEY_PATH"; then
1447 chmod 600 "$ACCOUNT_KEY_PATH"
1448 _info "Create account key ok."
1451 _err "Create account key error."
1460 _info "Creating domain key"
1461 if [ -z "$1" ]; then
1462 _usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
1469 if [ -z "$_cdl" ]; then
1470 _debug "Use DEFAULT_DOMAIN_KEY_LENGTH=$DEFAULT_DOMAIN_KEY_LENGTH"
1471 _cdl="$DEFAULT_DOMAIN_KEY_LENGTH"
1474 _initpath "$domain" "$_cdl"
1476 if [ ! -f "$CERT_KEY_PATH" ] || [ ! -s "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$_ACME_IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then
1477 if _createkey "$_cdl" "$CERT_KEY_PATH"; then
1478 _savedomainconf Le_Keylength "$_cdl"
1479 _info "The domain key is here: $(__green $CERT_KEY_PATH)"
1482 _err "Can not create domain key"
1486 if [ "$_ACME_IS_RENEW" ]; then
1487 _info "Domain key exists, skip"
1490 _err "Domain key exists, do you want to overwrite the key?"
1491 _err "Add '--force', and try again."
1498 # domain domainlist isEcc
1500 _info "Creating csr"
1501 if [ -z "$1" ]; then
1502 _usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
1510 _initpath "$domain" "$_isEcc"
1512 if [ -f "$CSR_PATH" ] && [ "$_ACME_IS_RENEW" ] && [ -z "$FORCE" ]; then
1513 _info "CSR exists, skip"
1517 if [ ! -f "$CERT_KEY_PATH" ]; then
1518 _err "The key file is not found: $CERT_KEY_PATH"
1519 _err "Please create the key file first."
1522 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
1527 tr '/+' '_-
' | tr -d '= '
1531 _durl_replace_base64() {
1533 if [ $_l -eq 2 ]; then
1535 elif [ $_l -eq 3 ]; then
1540 echo "$_s" | tr '_-
' '/+'
1545 if date -u -r "$1" 2>/dev/null; then
1550 if date -u -d@"$1" 2>/dev/null; then
1555 if _exists adb; then
1556 _t_s_a=$(echo "0t${1}=Y" | adb)
1561 if echo "$1" | awk '{ print strftime
("%c", $0); }' 2>/dev/null; then
1567 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
1572 if stat -c '%U
:%G
' "$1" 2>/dev/null; then
1577 if stat -f '%Su
:%Sg
' "$1" 2>/dev/null; then
1581 return 1 #error, 'stat
' not found
1587 if [ -z "$keyfile" ]; then
1588 _usage "Usage: _calcjwk keyfile"
1592 if [ "$JWK_HEADER" ] && [ "$__CACHED_JWK_KEY_FILE" = "$keyfile" ]; then
1593 _debug2 "Use cached jwk for file: $__CACHED_JWK_KEY_FILE"
1597 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
1599 pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x
' -f 2 | cut -d ')' -f 1)
1600 if [ "${#pub_exp}" = "5" ]; then
1603 _debug3 pub_exp "$pub_exp"
1605 e=$(echo "$pub_exp" | _h2b | _base64)
1608 modulus=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
1609 _debug3 modulus "$modulus"
1610 n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
1613 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
1616 JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
1617 JWK_HEADERPLACE_PART1='{"nonce": "'
1618 JWK_HEADERPLACE_PART2='", "alg": "RS256"'
1619 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
1621 crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
1623 __ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2)
1624 if [ "$__ECC_KEY_LEN" = "521" ]; then
1627 _debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN"
1628 if [ -z "$crv" ]; then
1629 _debug "Let's try ASN1 OID
"
1630 crv_oid="$
(${ACME_OPENSSL_BIN:-openssl} ec
-in "$keyfile" -noout -text 2>/dev
/null |
grep "^ASN1 OID:" | cut
-d ":" -f 2 |
tr -d " \r\n")"
1631 _debug3 crv_oid "$crv_oid"
1632 case "${crv_oid}" in
1646 _err "ECC oid
: $crv_oid"
1653 pubi="$
(${ACME_OPENSSL_BIN:-openssl} ec
-in "$keyfile" -noout -text 2>/dev
/null |
grep -n pub
: | cut
-d : -f 1)"
1654 pubi=$(_math "$pubi" + 1)
1655 _debug3 pubi "$pubi"
1657 pubj="$
(${ACME_OPENSSL_BIN:-openssl} ec
-in "$keyfile" -noout -text 2>/dev
/null |
grep -n "ASN1 OID:" | cut
-d : -f 1)"
1658 pubj=$(_math "$pubj" - 1)
1659 _debug3 pubj "$pubj"
1661 pubtext="$
(${ACME_OPENSSL_BIN:-openssl} ec
-in "$keyfile" -noout -text 2>/dev
/null |
sed -n "$pubi,${pubj}p" |
tr -d " \n\r")"
1662 _debug3 pubtext "$pubtext"
1664 xlen="$
(printf "%s" "$pubtext" |
tr -d ':' |
wc -c)"
1665 xlen=$(_math "$xlen" / 4)
1666 _debug3 xlen "$xlen"
1668 xend=$(_math "$xlen" + 1)
1669 x="$
(printf "%s" "$pubtext" | cut
-d : -f 2-"$xend")"
1672 x64="$
(printf "%s" "$x" |
tr -d : | _h2b | _base64 | _url_replace
)"
1675 xend=$(_math "$xend" + 1)
1676 y="$
(printf "%s" "$pubtext" | cut
-d : -f "$xend"-10000)"
1679 y64="$
(printf "%s" "$y" |
tr -d : | _h2b | _base64 | _url_replace
)"
1682 jwk='{"crv
": "'$crv'", "kty
": "EC
", "x
": "'$x64'", "y
": "'$y64'"}'
1685 JWK_HEADER='{"alg
": "ES
'$__ECC_KEY_LEN'", "jwk
": '$jwk'}'
1686 JWK_HEADERPLACE_PART1='{"nonce
": "'
1687 JWK_HEADERPLACE_PART2='", "alg
": "ES
'$__ECC_KEY_LEN'"'
1689 _err "Only RSA or EC key is supported. keyfile
=$keyfile"
1690 _debug2 "$
(cat "$keyfile")"
1694 _debug3 JWK_HEADER "$JWK_HEADER"
1695 __CACHED_JWK_KEY_FILE="$keyfile"
1703 date -u "+%Y-
%m-
%d
%H
:%M
:%S
"
1707 if _exists mktemp; then
1708 if mktemp 2>/dev/null; then
1710 elif _contains "$
(mktemp
2>&1)" "-t prefix
" && mktemp -t "$PROJECT_NAME" 2>/dev/null; then
1715 if [ -d "/tmp
" ]; then
1716 echo "/tmp
/${PROJECT_NAME}wefADf24sf.$
(_time
).tmp
"
1718 elif [ "$LE_TEMP_DIR" ] && mkdir -p "$LE_TEMP_DIR"; then
1719 echo "/$LE_TEMP_DIR/wefADf24sf.$
(_time
).tmp
"
1722 _err "Can not create temp
file.
"
1727 if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
1728 HTTP_HEADER="$
(_mktemp
)"
1729 _debug2 HTTP_HEADER "$HTTP_HEADER"
1732 if [ "$__HTTP_INITIALIZED" ]; then
1733 if [ "$_ACME_CURL$_ACME_WGET" ]; then
1734 _debug2 "Http already initialized.
"
1739 if [ -z "$_ACME_CURL" ] && _exists "curl
"; then
1740 _ACME_CURL="curl
-L --silent --dump-header $HTTP_HEADER "
1741 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1742 _CURL_DUMP="$
(_mktemp
)"
1743 _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
1746 if [ "$CA_PATH" ]; then
1747 _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
1748 elif [ "$CA_BUNDLE" ]; then
1749 _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
1752 if _contains "$
(curl
--help 2>&1)" "--globoff"; then
1753 _ACME_CURL="$_ACME_CURL -g "
1757 if [ -z "$_ACME_WGET" ] && _exists "wget
"; then
1758 _ACME_WGET="wget
-q"
1759 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1760 _ACME_WGET="$_ACME_WGET -d "
1762 if [ "$CA_PATH" ]; then
1763 _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
1764 elif [ "$CA_BUNDLE" ]; then
1765 _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
1769 #from wget 1.14: do not skip body on 404 error
1770 if [ "$_ACME_WGET" ] && _contains "$
($_ACME_WGET --help 2>&1)" "--content-on-error"; then
1771 _ACME_WGET="$_ACME_WGET --content-on-error "
1774 __HTTP_INITIALIZED=1
1778 # body url [needbase64] [POST|PUT|DELETE] [ContentType]
1784 _postContentType="$5"
1786 if [ -z "$httpmethod" ]; then
1790 _debug "_post_url
" "$_post_url"
1791 _debug2 "body
" "$body"
1792 _debug2 "_postContentType
" "$_postContentType"
1796 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1798 if [ "$HTTPS_INSECURE" ]; then
1799 _CURL="$_CURL --insecure "
1801 if [ "$httpmethod" = "HEAD
" ]; then
1804 _debug "_CURL
" "$_CURL"
1805 if [ "$needbase64" ]; then
1806 if [ "$body" ]; then
1807 if [ "$_postContentType" ]; then
1808 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64
)"
1810 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64
)"
1813 if [ "$_postContentType" ]; then
1814 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64
)"
1816 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64
)"
1820 if [ "$body" ]; then
1821 if [ "$_postContentType" ]; then
1822 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
1824 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
1827 if [ "$_postContentType" ]; then
1828 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
1830 response="$
($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
1835 if [ "$_ret" != "0" ]; then
1836 _err "Please refer to https
://curl.haxx.se
/libcurl
/c
/libcurl-errors.html
for error code
: $_ret"
1837 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1838 _err "Here is the curl dump log
:"
1839 _err "$
(cat "$_CURL_DUMP")"
1842 elif [ "$_ACME_WGET" ]; then
1844 if [ "$HTTPS_INSECURE" ]; then
1845 _WGET="$_WGET --no-check-certificate "
1847 if [ "$httpmethod" = "HEAD
" ]; then
1848 _WGET="$_WGET --read-timeout=3.0 --tries=2 "
1850 _debug "_WGET
" "$_WGET"
1851 if [ "$needbase64" ]; then
1852 if [ "$httpmethod" = "POST
" ]; then
1853 if [ "$_postContentType" ]; then
1854 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64
)"
1856 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64
)"
1859 if [ "$_postContentType" ]; then
1860 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64
)"
1862 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64
)"
1866 if [ "$httpmethod" = "POST
" ]; then
1867 if [ "$_postContentType" ]; then
1868 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1870 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1872 elif [ "$httpmethod" = "HEAD
" ]; then
1873 if [ "$_postContentType" ]; then
1874 response="$
($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1876 response="$
($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1879 if [ "$_postContentType" ]; then
1880 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1882 response="$
($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1887 if [ "$_ret" = "8" ]; then
1889 _debug "wget returns
8, the server returns a
'Bad request' response
, lets process the response later.
"
1891 if [ "$_ret" != "0" ]; then
1892 _err "Please refer to https
://www.gnu.org
/software
/wget
/manual
/html_node
/Exit-Status.html
for error code
: $_ret"
1894 _sed_i "s
/^
*//g
" "$HTTP_HEADER"
1897 _err "Neither curl nor wget is found
, can not
do $httpmethod.
"
1899 _debug "_ret
" "$_ret"
1900 printf "%s
" "$response"
1904 # url getheader timeout
1915 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1917 if [ "$HTTPS_INSECURE" ]; then
1918 _CURL="$_CURL --insecure "
1921 _CURL="$_CURL --connect-timeout $t"
1923 _debug "_CURL
" "$_CURL"
1924 if [ "$onlyheader" ]; then
1925 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
1927 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
1930 if [ "$ret" != "0" ]; then
1931 _err "Please refer to https
://curl.haxx.se
/libcurl
/c
/libcurl-errors.html
for error code
: $ret"
1932 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1933 _err "Here is the curl dump log
:"
1934 _err "$
(cat "$_CURL_DUMP")"
1937 elif [ "$_ACME_WGET" ]; then
1939 if [ "$HTTPS_INSECURE" ]; then
1940 _WGET="$_WGET --no-check-certificate "
1943 _WGET="$_WGET --timeout=$t"
1945 _debug "_WGET
" "$_WGET"
1946 if [ "$onlyheader" ]; then
1947 $_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'
1949 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
1952 if [ "$ret" = "8" ]; then
1954 _debug "wget returns
8, the server returns a
'Bad request' response
, lets process the response later.
"
1956 if [ "$ret" != "0" ]; then
1957 _err "Please refer to https
://www.gnu.org
/software
/wget
/manual
/html_node
/Exit-Status.html
for error code
: $ret"
1961 _err "Neither curl nor wget is found
, can not
do GET.
"
1972 if ! tail -n "$1" 2>/dev/null; then
1978 # url payload needbase64 keyfile
1979 _send_signed_request() {
1984 if [ -z "$keyfile" ]; then
1985 keyfile="$ACCOUNT_KEY_PATH"
1988 _debug payload "$payload"
1990 if ! _calcjwk "$keyfile"; then
1994 __request_conent_type="$CONTENT_TYPE_JSON"
1996 payload64=$(printf "%s
" "$payload" | _base64 | _url_replace)
1997 _debug3 payload64 "$payload64"
1999 MAX_REQUEST_RETRY_TIMES=20
2001 _request_retry_times=0
2002 while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
2003 _request_retry_times=$(_math "$_request_retry_times" + 1)
2004 _debug3 _request_retry_times "$_request_retry_times"
2005 if [ -z "$_CACHED_NONCE" ]; then
2007 if [ "$ACME_NEW_NONCE" ]; then
2008 _debug2 "Get nonce with HEAD. ACME_NEW_NONCE
" "$ACME_NEW_NONCE"
2009 nonceurl="$ACME_NEW_NONCE"
2010 if _post "" "$nonceurl" "" "HEAD
" "$__request_conent_type" >/dev/null; then
2011 _headers="$
(cat "$HTTP_HEADER")"
2012 _debug2 _headers "$_headers"
2013 _CACHED_NONCE="$
(echo "$_headers" |
grep -i "Replay-Nonce:" | _head_n
1 |
tr -d "\r\n " | cut
-d ':' -f 2)"
2016 if [ -z "$_CACHED_NONCE" ]; then
2017 _debug2 "Get nonce with GET. ACME_DIRECTORY
" "$ACME_DIRECTORY"
2018 nonceurl="$ACME_DIRECTORY"
2019 _headers="$
(_get
"$nonceurl" "onlyheader")"
2020 _debug2 _headers "$_headers"
2021 _CACHED_NONCE="$
(echo "$_headers" |
grep -i "Replay-Nonce:" | _head_n
1 |
tr -d "\r\n " | cut
-d ':' -f 2)"
2023 if [ -z "$_CACHED_NONCE" ] && [ "$ACME_NEW_NONCE" ]; then
2024 _debug2 "Get nonce with GET. ACME_NEW_NONCE
" "$ACME_NEW_NONCE"
2025 nonceurl="$ACME_NEW_NONCE"
2026 _headers="$
(_get
"$nonceurl" "onlyheader")"
2027 _debug2 _headers "$_headers"
2028 _CACHED_NONCE="$
(echo "$_headers" |
grep -i "Replay-Nonce:" | _head_n
1 |
tr -d "\r\n " | cut
-d ':' -f 2)"
2030 _debug2 _CACHED_NONCE "$_CACHED_NONCE"
2031 if [ "$?
" != "0" ]; then
2032 _err "Can not connect to
$nonceurl to get nonce.
"
2036 _debug2 "Use _CACHED_NONCE
" "$_CACHED_NONCE"
2038 nonce="$_CACHED_NONCE"
2039 _debug2 nonce "$nonce"
2040 if [ -z "$nonce" ]; then
2041 _info "Could not get nonce
, let's try again."
2045 if [ "$ACME_VERSION" = "2" ]; then
2046 if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then
2047 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2048 elif [ "$url" = "$ACME_REVOKE_CERT" ] && [ "$keyfile" != "$ACCOUNT_KEY_PATH" ]; then
2049 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2051 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
2054 protected
="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2056 _debug3 protected
"$protected"
2058 protected64
="$(printf "%s
" "$protected" | _base64 | _url_replace)"
2059 _debug3 protected64
"$protected64"
2061 if ! _sig_t
="$(printf "%s
" "$protected64.
$payload64" | _sign "$keyfile" "sha256
")"; then
2062 _err
"Sign request failed."
2065 _debug3 _sig_t
"$_sig_t"
2067 sig
="$(printf "%s
" "$_sig_t" | _url_replace)"
2070 body
="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
2071 _debug3 body
"$body"
2073 response
="$(_post "$body" "$url" "$needbase64" "POST
" "$__request_conent_type")"
2076 if [ "$?" != "0" ]; then
2077 _err
"Can not post to $url"
2081 responseHeaders
="$(cat "$HTTP_HEADER")"
2082 _debug2 responseHeaders
"$responseHeaders"
2084 code
="$(grep "^HTTP
" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
2087 _debug2 original
"$response"
2088 if echo "$responseHeaders" |
grep -i "Content-Type: *application/json" >/dev
/null
2>&1; then
2089 response
="$(echo "$response" | _normalizeJson)"
2091 _debug2 response
"$response"
2093 _CACHED_NONCE
="$(echo "$responseHeaders" | grep -i "Replay-Nonce
:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
2095 if ! _startswith
"$code" "2"; then
2097 if [ "$needbase64" ]; then
2098 _body
="$(echo "$_body" | _dbase64 multiline)"
2099 _debug3 _body
"$_body"
2102 if _contains
"$_body" "JWS has invalid anti-replay nonce" || _contains
"$_body" "JWS has an invalid anti-replay nonce"; then
2103 _info
"It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds."
2105 _sleep
$_sleep_retry_sec
2111 _info
"Giving up sending to CA server after $MAX_REQUEST_RETRY_TIMES retries."
2116 #setopt "file" "opt" "=" "value" [";"]
2123 if [ -z "$__opt" ]; then
2124 _usage usage
: _setopt
'"file" "opt" "=" "value" [";"]'
2127 if [ ! -f "$__conf" ]; then
2131 if grep -n "^$__opt$__sep" "$__conf" >/dev
/null
; then
2133 if _contains
"$__val" "&"; then
2134 __val
="$(echo "$__val" | sed 's/&/\\&/g')"
2136 text
="$(cat "$__conf")"
2137 printf -- "%s\n" "$text" |
sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
2139 elif grep -n "^#$__opt$__sep" "$__conf" >/dev
/null
; then
2140 if _contains
"$__val" "&"; then
2141 __val
="$(echo "$__val" | sed 's/&/\\&/g')"
2143 text
="$(cat "$__conf")"
2144 printf -- "%s\n" "$text" |
sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
2148 echo "$__opt$__sep$__val$__end" >>"$__conf"
2150 _debug3
"$(grep -n "^
$__opt$__sep" "$__conf")"
2153 #_save_conf file key value base64encode
2160 if [ "$_sdvalue" ] && [ "$_b64encode" ]; then
2161 _sdvalue
="${B64CONF_START}$(printf "%s" "${_sdvalue}" | _base64)${B64CONF_END}"
2163 if [ "$_s_c_f" ]; then
2164 _setopt
"$_s_c_f" "$_sdkey" "=" "'$_sdvalue'"
2166 _err
"config file is empty, can not save $_sdkey=$_sdvalue"
2170 #_clear_conf file key
2174 if [ "$_c_c_f" ]; then
2175 _conf_data
="$(cat "$_c_c_f")"
2176 echo "$_conf_data" |
sed "s/^$_sdkey *=.*$//" >"$_c_c_f"
2178 _err
"config file is empty, can not clear"
2182 #_read_conf file key
2186 if [ -f "$_r_c_f" ]; then
2188 eval "$
(grep "^$_sdkey *=" "$_r_c_f")"
2189 eval "printf \"%s
\" \"\$
$_sdkey\""
2191 if _startswith
"$_sdv" "${B64CONF_START}" && _endswith
"$_sdv" "${B64CONF_END}"; then
2192 _sdv
="$(echo "$_sdv" | sed "s
/${B64CONF_START}//" | sed "s
/${B64CONF_END}//" | _dbase64)"
2196 _debug
"config file is empty, can not read $_sdkey"
2200 #_savedomainconf key value base64encode
2201 #save to domain.conf
2203 _save_conf
"$DOMAIN_CONF" "$@"
2206 #_cleardomainconf key
2207 _cleardomainconf
() {
2208 _clear_conf
"$DOMAIN_CONF" "$1"
2211 #_readdomainconf key
2213 _read_conf
"$DOMAIN_CONF" "$1"
2216 #key value base64encode
2218 _savedomainconf
"SAVED_$1" "$2" "$3"
2220 _cleardomainconf
"$1"
2226 _rac_value
="$(eval echo \$"$_rac_key")"
2227 if [ "$_rac_value" ]; then
2228 if _startswith
"$_rac_value" '"' && _endswith
"$_rac_value" '"'; then
2229 _debug2
"trim quotation marks"
2230 eval "export $_rac_key=$_rac_value"
2232 return 0 # do nothing
2234 _saved
=$
(_readdomainconf
"SAVED_$_rac_key")
2235 eval "export $_rac_key=\"$_saved\""
2238 #_saveaccountconf key value base64encode
2239 _saveaccountconf
() {
2240 _save_conf
"$ACCOUNT_CONF_PATH" "$@"
2243 #key value base64encode
2244 _saveaccountconf_mutable
() {
2245 _save_conf
"$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" "$3"
2247 _clearaccountconf
"$1"
2251 _readaccountconf
() {
2252 _read_conf
"$ACCOUNT_CONF_PATH" "$1"
2256 _readaccountconf_mutable
() {
2258 _readaccountconf
"SAVED_$_rac_key"
2261 #_clearaccountconf key
2262 _clearaccountconf
() {
2263 _clear_conf
"$ACCOUNT_CONF_PATH" "$1"
2266 #_savecaconf key value
2268 _save_conf
"$CA_CONF" "$1" "$2"
2273 _read_conf
"$CA_CONF" "$1"
2276 #_clearaccountconf key
2278 _clear_conf
"$CA_CONF" "$1"
2281 # content localaddress
2285 _debug
"content" "$content"
2286 _debug
"ncaddr" "$ncaddr"
2288 _debug
"startserver: $$"
2290 _debug Le_HTTPPort
"$Le_HTTPPort"
2291 _debug Le_Listen_V4
"$Le_Listen_V4"
2292 _debug Le_Listen_V6
"$Le_Listen_V6"
2295 if [ "$Le_Listen_V4" ]; then
2297 elif [ "$Le_Listen_V6" ]; then
2301 if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
2305 SOCAT_OPTIONS
=TCP-LISTEN
:$Le_HTTPPort,crlf
,reuseaddr
,fork
2307 #Adding bind to local-address
2308 if [ "$ncaddr" ]; then
2309 SOCAT_OPTIONS
="$SOCAT_OPTIONS,bind=${ncaddr}"
2312 _content_len
="$(printf "%s
" "$content" | wc -c)"
2313 _debug _content_len
"$_content_len"
2314 _debug
"_NC" "$_NC $SOCAT_OPTIONS"
2315 $_NC $SOCAT_OPTIONS SYSTEM
:"sleep 1; \
2316 echo 'HTTP/1.0 200 OK'; \
2317 echo 'Content-Length\: $_content_len'; \
2319 printf -- '$content';" &
2326 if [ -z "$pid" ]; then
2337 if [ "$__INTERACTIVE" ]; then
2338 _sleep_c
="$_sleep_sec"
2339 while [ "$_sleep_c" -ge "0" ]; do
2342 _sleep_c
="$(_math "$_sleep_c" - 1)"
2351 # _starttlsserver san_a san_b port content _ncaddr acmeValidationv1
2353 _info
"Starting tls server."
2359 acmeValidationv1
="$6"
2361 _debug san_a
"$san_a"
2362 _debug san_b
"$san_b"
2364 _debug acmeValidationv1
"$acmeValidationv1"
2367 if ! _createkey
"2048" "$TLS_KEY"; then
2368 _err
"Create tls validation key error."
2374 if [ "$san_b" ]; then
2377 if ! _createcsr
"tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$acmeValidationv1"; then
2378 _err
"Create tls validation csr error."
2383 if ! _signcsr
"$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT"; then
2384 _err
"Create tls validation cert error."
2388 __S_OPENSSL
="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
2389 if [ "$opaddr" ]; then
2390 __S_OPENSSL
="$__S_OPENSSL -accept $opaddr:$port"
2392 __S_OPENSSL
="$__S_OPENSSL -accept $port"
2395 _debug Le_Listen_V4
"$Le_Listen_V4"
2396 _debug Le_Listen_V6
"$Le_Listen_V6"
2397 if [ "$Le_Listen_V4" ]; then
2398 __S_OPENSSL
="$__S_OPENSSL -4"
2399 elif [ "$Le_Listen_V6" ]; then
2400 __S_OPENSSL
="$__S_OPENSSL -6"
2403 if [ "$acmeValidationv1" ]; then
2404 __S_OPENSSL
="$__S_OPENSSL -alpn acme-tls/1"
2407 _debug
"$__S_OPENSSL"
2408 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
2409 $__S_OPENSSL -tlsextdebug &
2411 $__S_OPENSSL >/dev
/null
2>&1 &
2416 _debug serverproc
"$serverproc"
2422 if ! readlink
-f "$_rf" 2>/dev
/null
; then
2423 if _startswith
"$_rf" "/"; then
2427 echo "$(pwd)/$_rf" | _conapath
2436 if [ -z "$_SCRIPT_HOME" ]; then
2437 if _exists readlink
&& _exists
dirname; then
2438 _debug
"Lets find script dir."
2439 _debug
"_SCRIPT_" "$_SCRIPT_"
2440 _script
="$(_readlink "$_SCRIPT_")"
2441 _debug
"_script" "$_script"
2442 _script_home
="$(dirname "$_script")"
2443 _debug
"_script_home" "$_script_home"
2444 if [ -d "$_script_home" ]; then
2445 _SCRIPT_HOME
="$_script_home"
2447 _err
"It seems the script home is not correct:$_script_home"
2452 # if [ -z "$LE_WORKING_DIR" ]; then
2453 # if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ]; then
2454 # _debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
2455 # LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
2457 # LE_WORKING_DIR="$_SCRIPT_HOME"
2461 if [ -z "$LE_WORKING_DIR" ]; then
2462 _debug
"Using default home:$DEFAULT_INSTALL_HOME"
2463 LE_WORKING_DIR
="$DEFAULT_INSTALL_HOME"
2465 export LE_WORKING_DIR
2467 if [ -z "$LE_CONFIG_HOME" ]; then
2468 LE_CONFIG_HOME
="$LE_WORKING_DIR"
2470 _debug
"Using config home:$LE_CONFIG_HOME"
2471 export LE_CONFIG_HOME
2473 _DEFAULT_ACCOUNT_CONF_PATH
="$LE_CONFIG_HOME/account.conf"
2475 if [ -z "$ACCOUNT_CONF_PATH" ]; then
2476 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]; then
2477 .
"$_DEFAULT_ACCOUNT_CONF_PATH"
2481 if [ -z "$ACCOUNT_CONF_PATH" ]; then
2482 ACCOUNT_CONF_PATH
="$_DEFAULT_ACCOUNT_CONF_PATH"
2484 _debug3 ACCOUNT_CONF_PATH
"$ACCOUNT_CONF_PATH"
2485 DEFAULT_LOG_FILE
="$LE_CONFIG_HOME/$PROJECT_NAME.log"
2487 DEFAULT_CA_HOME
="$LE_CONFIG_HOME/ca"
2489 if [ -z "$LE_TEMP_DIR" ]; then
2490 LE_TEMP_DIR
="$LE_CONFIG_HOME/tmp"
2496 _api_server
="${1:-$ACME_DIRECTORY}"
2497 _debug
"_init api for server: $_api_server"
2499 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2500 response
=$
(_get
"$_api_server")
2501 if [ "$?" != "0" ]; then
2502 _debug2
"response" "$response"
2503 _err
"Can not init api."
2506 _debug2
"response" "$response"
2508 ACME_KEY_CHANGE
=$
(echo "$response" | _egrep_o
'key-change" *: *"[^"]*"' | cut
-d '"' -f 3)
2509 if [ -z "$ACME_KEY_CHANGE" ]; then
2510 ACME_KEY_CHANGE
=$
(echo "$response" | _egrep_o
'keyChange" *: *"[^"]*"' | cut
-d '"' -f 3)
2512 export ACME_KEY_CHANGE
2514 ACME_NEW_AUTHZ
=$
(echo "$response" | _egrep_o
'new-authz" *: *"[^"]*"' | cut
-d '"' -f 3)
2515 if [ -z "$ACME_NEW_AUTHZ" ]; then
2516 ACME_NEW_AUTHZ
=$
(echo "$response" | _egrep_o
'newAuthz" *: *"[^"]*"' | cut
-d '"' -f 3)
2518 export ACME_NEW_AUTHZ
2520 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'new-cert" *: *"[^"]*"' | cut
-d '"' -f 3)
2521 ACME_NEW_ORDER_RES
="new-cert"
2522 if [ -z "$ACME_NEW_ORDER" ]; then
2523 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'new-order" *: *"[^"]*"' | cut
-d '"' -f 3)
2524 ACME_NEW_ORDER_RES
="new-order"
2525 if [ -z "$ACME_NEW_ORDER" ]; then
2526 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'newOrder" *: *"[^"]*"' | cut
-d '"' -f 3)
2529 export ACME_NEW_ORDER
2530 export ACME_NEW_ORDER_RES
2532 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'new-reg" *: *"[^"]*"' | cut
-d '"' -f 3)
2533 ACME_NEW_ACCOUNT_RES
="new-reg"
2534 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2535 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'new-account" *: *"[^"]*"' | cut
-d '"' -f 3)
2536 ACME_NEW_ACCOUNT_RES
="new-account"
2537 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2538 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'newAccount" *: *"[^"]*"' | cut
-d '"' -f 3)
2539 if [ "$ACME_NEW_ACCOUNT" ]; then
2540 export ACME_VERSION
=2
2544 export ACME_NEW_ACCOUNT
2545 export ACME_NEW_ACCOUNT_RES
2547 ACME_REVOKE_CERT
=$
(echo "$response" | _egrep_o
'revoke-cert" *: *"[^"]*"' | cut
-d '"' -f 3)
2548 if [ -z "$ACME_REVOKE_CERT" ]; then
2549 ACME_REVOKE_CERT
=$
(echo "$response" | _egrep_o
'revokeCert" *: *"[^"]*"' | cut
-d '"' -f 3)
2551 export ACME_REVOKE_CERT
2553 ACME_NEW_NONCE
=$
(echo "$response" | _egrep_o
'new-nonce" *: *"[^"]*"' | cut
-d '"' -f 3)
2554 if [ -z "$ACME_NEW_NONCE" ]; then
2555 ACME_NEW_NONCE
=$
(echo "$response" | _egrep_o
'newNonce" *: *"[^"]*"' | cut
-d '"' -f 3)
2557 export ACME_NEW_NONCE
2559 ACME_AGREEMENT
=$
(echo "$response" | _egrep_o
'terms-of-service" *: *"[^"]*"' | cut
-d '"' -f 3)
2560 if [ -z "$ACME_AGREEMENT" ]; then
2561 ACME_AGREEMENT
=$
(echo "$response" | _egrep_o
'termsOfService" *: *"[^"]*"' | cut
-d '"' -f 3)
2563 export ACME_AGREEMENT
2565 _debug
"ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
2566 _debug
"ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
2567 _debug
"ACME_NEW_ORDER" "$ACME_NEW_ORDER"
2568 _debug
"ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
2569 _debug
"ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
2570 _debug
"ACME_AGREEMENT" "$ACME_AGREEMENT"
2571 _debug
"ACME_NEW_NONCE" "$ACME_NEW_NONCE"
2572 _debug
"ACME_VERSION" "$ACME_VERSION"
2577 #[domain] [keylength or isEcc flag]
2584 if [ -f "$ACCOUNT_CONF_PATH" ]; then
2585 .
"$ACCOUNT_CONF_PATH"
2588 if [ "$_ACME_IN_CRON" ]; then
2589 if [ ! "$_USER_PATH_EXPORTED" ]; then
2590 _USER_PATH_EXPORTED
=1
2591 export PATH
="$USER_PATH:$PATH"
2595 if [ -z "$CA_HOME" ]; then
2596 CA_HOME
="$DEFAULT_CA_HOME"
2599 if [ -z "$ACME_DIRECTORY" ]; then
2600 if [ "$STAGE" ]; then
2601 ACME_DIRECTORY
="$DEFAULT_STAGING_CA"
2602 _info
"Using ACME_DIRECTORY: $ACME_DIRECTORY"
2604 default_acme_server
=$
(_readaccountconf
"DEFAULT_ACME_SERVER")
2605 _debug default_acme_server
"$default_acme_server"
2606 if [ "$default_acme_server" ]; then
2607 ACME_DIRECTORY
="$default_acme_server"
2609 ACME_DIRECTORY
="$DEFAULT_CA"
2614 _debug ACME_DIRECTORY
"$ACME_DIRECTORY"
2615 _ACME_SERVER_HOST
="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
2616 _debug2
"_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
2618 CA_DIR
="$CA_HOME/$_ACME_SERVER_HOST"
2620 _DEFAULT_CA_CONF
="$CA_DIR/ca.conf"
2622 if [ -z "$CA_CONF" ]; then
2623 CA_CONF
="$_DEFAULT_CA_CONF"
2625 _debug3 CA_CONF
"$CA_CONF"
2627 if [ -f "$CA_CONF" ]; then
2631 if [ -z "$ACME_DIR" ]; then
2632 ACME_DIR
="/home/.acme"
2635 if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then
2636 APACHE_CONF_BACKUP_DIR
="$LE_CONFIG_HOME"
2639 if [ -z "$USER_AGENT" ]; then
2640 USER_AGENT
="$DEFAULT_USER_AGENT"
2643 if [ -z "$HTTP_HEADER" ]; then
2644 HTTP_HEADER
="$LE_CONFIG_HOME/http.header"
2647 _OLD_ACCOUNT_KEY
="$LE_WORKING_DIR/account.key"
2648 _OLD_ACCOUNT_JSON
="$LE_WORKING_DIR/account.json"
2650 _DEFAULT_ACCOUNT_KEY_PATH
="$CA_DIR/account.key"
2651 _DEFAULT_ACCOUNT_JSON_PATH
="$CA_DIR/account.json"
2652 if [ -z "$ACCOUNT_KEY_PATH" ]; then
2653 ACCOUNT_KEY_PATH
="$_DEFAULT_ACCOUNT_KEY_PATH"
2656 if [ -z "$ACCOUNT_JSON_PATH" ]; then
2657 ACCOUNT_JSON_PATH
="$_DEFAULT_ACCOUNT_JSON_PATH"
2660 _DEFAULT_CERT_HOME
="$LE_CONFIG_HOME"
2661 if [ -z "$CERT_HOME" ]; then
2662 CERT_HOME
="$_DEFAULT_CERT_HOME"
2665 if [ -z "$ACME_OPENSSL_BIN" ] ||
[ ! -f "$ACME_OPENSSL_BIN" ] ||
[ ! -x "$ACME_OPENSSL_BIN" ]; then
2666 ACME_OPENSSL_BIN
="$DEFAULT_OPENSSL_BIN"
2669 if [ -z "$domain" ]; then
2673 if [ -z "$DOMAIN_PATH" ]; then
2674 domainhome
="$CERT_HOME/$domain"
2675 domainhomeecc
="$CERT_HOME/$domain$ECC_SUFFIX"
2677 DOMAIN_PATH
="$domainhome"
2679 if _isEccKey
"$_ilength"; then
2680 DOMAIN_PATH
="$domainhomeecc"
2682 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then
2683 _info
"The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
2686 _debug DOMAIN_PATH
"$DOMAIN_PATH"
2689 if [ -z "$DOMAIN_BACKUP_PATH" ]; then
2690 DOMAIN_BACKUP_PATH
="$DOMAIN_PATH/backup"
2693 if [ -z "$DOMAIN_CONF" ]; then
2694 DOMAIN_CONF
="$DOMAIN_PATH/$domain.conf"
2697 if [ -z "$DOMAIN_SSL_CONF" ]; then
2698 DOMAIN_SSL_CONF
="$DOMAIN_PATH/$domain.csr.conf"
2701 if [ -z "$CSR_PATH" ]; then
2702 CSR_PATH
="$DOMAIN_PATH/$domain.csr"
2704 if [ -z "$CERT_KEY_PATH" ]; then
2705 CERT_KEY_PATH
="$DOMAIN_PATH/$domain.key"
2707 if [ -z "$CERT_PATH" ]; then
2708 CERT_PATH
="$DOMAIN_PATH/$domain.cer"
2710 if [ -z "$CA_CERT_PATH" ]; then
2711 CA_CERT_PATH
="$DOMAIN_PATH/ca.cer"
2713 if [ -z "$CERT_FULLCHAIN_PATH" ]; then
2714 CERT_FULLCHAIN_PATH
="$DOMAIN_PATH/fullchain.cer"
2716 if [ -z "$CERT_PFX_PATH" ]; then
2717 CERT_PFX_PATH
="$DOMAIN_PATH/$domain.pfx"
2719 if [ -z "$CERT_PKCS8_PATH" ]; then
2720 CERT_PKCS8_PATH
="$DOMAIN_PATH/$domain.pkcs8"
2723 if [ -z "$TLS_CONF" ]; then
2724 TLS_CONF
="$DOMAIN_PATH/tls.validation.conf"
2726 if [ -z "$TLS_CERT" ]; then
2727 TLS_CERT
="$DOMAIN_PATH/tls.validation.cert"
2729 if [ -z "$TLS_KEY" ]; then
2730 TLS_KEY
="$DOMAIN_PATH/tls.validation.key"
2732 if [ -z "$TLS_CSR" ]; then
2733 TLS_CSR
="$DOMAIN_PATH/tls.validation.csr"
2739 if [ -z "$_EXEC_TEMP_ERR" ]; then
2740 _EXEC_TEMP_ERR
="$(_mktemp)"
2743 if [ "$_EXEC_TEMP_ERR" ]; then
2744 eval "$@ 2>>$_EXEC_TEMP_ERR"
2751 [ "$_EXEC_TEMP_ERR" ] && _err
"$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
2755 _APACHECTL
="apachectl"
2756 if ! _exists apachectl
; then
2757 if _exists apache2ctl
; then
2758 _APACHECTL
="apache2ctl"
2760 _err
"'apachectl not found. It seems that apache is not installed, or you are not root user.'"
2761 _err
"Please use webroot mode to try again."
2766 if ! _exec
$_APACHECTL -V >/dev
/null
; then
2771 if [ "$APACHE_HTTPD_CONF" ]; then
2772 _saveaccountconf APACHE_HTTPD_CONF
"$APACHE_HTTPD_CONF"
2773 httpdconf
="$APACHE_HTTPD_CONF"
2774 httpdconfname
="$(basename "$httpdconfname")"
2776 httpdconfname
="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"')"
2777 _debug httpdconfname "$httpdconfname"
2779 if [ -z "$httpdconfname" ]; then
2780 _err "Can not read apache config file."
2784 if _startswith "$httpdconfname" '/'; then
2785 httpdconf="$httpdconfname"
2786 httpdconfname="$(basename "$httpdconfname")"
2788 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"')"
2789 _debug httpdroot
"$httpdroot"
2790 httpdconf
="$httpdroot/$httpdconfname"
2791 httpdconfname
="$(basename "$httpdconfname")"
2794 _debug httpdconf
"$httpdconf"
2795 _debug httpdconfname
"$httpdconfname"
2796 if [ ! -f "$httpdconf" ]; then
2797 _err
"Apache Config file not found" "$httpdconf"
2804 if [ -z "$usingApache" ]; then
2808 if ! _apachePath
; then
2812 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]; then
2813 _debug
"No config file to restore."
2817 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
2818 _debug
"Restored: $httpdconf."
2819 if ! _exec
$_APACHECTL -t; then
2821 _err
"Sorry, restore apache config error, please contact me."
2824 _debug
"Restored successfully."
2825 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
2831 if ! _apachePath
; then
2835 #test the conf first
2836 _info
"Checking if there is an error in the apache config file before starting."
2838 if ! _exec
"$_APACHECTL" -t >/dev
/null
; then
2840 _err
"The apache config file has error, please fix it first, then try again."
2841 _err
"Don't worry, there is nothing changed to your system."
2848 _debug
"Backup apache config file" "$httpdconf"
2849 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then
2850 _err
"Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
2851 _err
"This might be a bug of $PROJECT_NAME , please report issue: $PROJECT"
2854 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
2855 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
2856 _info
"The backup file will be deleted on success, just forget it."
2860 apacheVer
="$($_APACHECTL -V | grep "Server version
:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2)"
2861 _debug
"apacheVer" "$apacheVer"
2862 apacheMajor
="$(echo "$apacheVer" | cut -d . -f 1)"
2863 apacheMinor
="$(echo "$apacheVer" | cut -d . -f 2)"
2865 if [ "$apacheVer" ] && [ "$apacheMajor$apacheMinor" -ge "24" ]; then
2867 Alias /.well-known/acme-challenge $ACME_DIR
2869 <Directory $ACME_DIR >
2875 Alias /.well-known/acme-challenge $ACME_DIR
2877 <Directory $ACME_DIR >
2884 _msg
="$($_APACHECTL -t 2>&1)"
2885 if [ "$?" != "0" ]; then
2886 _err
"Sorry, apache config error"
2887 if _restoreApache
; then
2888 _err
"The apache config file is restored."
2890 _err
"Sorry, The apache config file can not be restored, please report bug."
2895 if [ ! -d "$ACME_DIR" ]; then
2896 mkdir
-p "$ACME_DIR"
2897 chmod 755 "$ACME_DIR"
2900 if ! _exec
"$_APACHECTL" graceful
; then
2902 _err
"$_APACHECTL graceful error, please contact me."
2910 #find the real nginx conf file
2913 #returns the real nginx conf file
2919 FOUND_REAL_NGINX_CONF
=""
2920 FOUND_REAL_NGINX_CONF_LN
=""
2921 BACKUP_NGINX_CONF
=""
2922 _debug _croot
"$_croot"
2923 _start_f
="$(echo "$_croot" | cut -d : -f 2)"
2924 _debug _start_f
"$_start_f"
2925 if [ -z "$_start_f" ]; then
2926 _debug
"find start conf from nginx command"
2927 if [ -z "$NGINX_CONF" ]; then
2928 if ! _exists
"nginx"; then
2929 _err
"nginx command is not found."
2932 NGINX_CONF
="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^
]* " | tr -d " ")"
2933 _debug NGINX_CONF
"$NGINX_CONF"
2934 NGINX_CONF
="$(echo "$NGINX_CONF" | cut -d = -f 2)"
2935 _debug NGINX_CONF
"$NGINX_CONF"
2936 if [ -z "$NGINX_CONF" ]; then
2937 _err
"Can not find nginx conf."
2941 if [ ! -f "$NGINX_CONF" ]; then
2942 _err
"'$NGINX_CONF' doesn't exist."
2946 _debug
"Found nginx conf file:$NGINX_CONF"
2948 _start_f
="$NGINX_CONF"
2950 _debug
"Start detect nginx conf for $_d from:$_start_f"
2951 if ! _checkConf
"$_d" "$_start_f"; then
2952 _err
"Can not find conf file for domain $d"
2955 _info
"Found conf file: $FOUND_REAL_NGINX_CONF"
2957 _ln
=$FOUND_REAL_NGINX_CONF_LN
2960 _lnn
=$
(_math
$_ln + 1)
2962 _start_tag
="$(sed -n "$_lnn,${_lnn}p
" "$FOUND_REAL_NGINX_CONF")"
2963 _debug
"_start_tag" "$_start_tag"
2964 if [ "$_start_tag" = "$NGINX_START" ]; then
2965 _info
"The domain $_d is already configured, skip"
2966 FOUND_REAL_NGINX_CONF
=""
2970 mkdir
-p "$DOMAIN_BACKUP_PATH"
2971 _backup_conf
="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
2972 _debug _backup_conf
"$_backup_conf"
2973 BACKUP_NGINX_CONF
="$_backup_conf"
2974 _info
"Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
2975 if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
2976 _err
"backup error."
2977 FOUND_REAL_NGINX_CONF
=""
2981 if ! _exists
"nginx"; then
2982 _err
"nginx command is not found."
2985 _info
"Check the nginx conf before setting up."
2986 if ! _exec
"nginx -t" >/dev
/null
; then
2991 _info
"OK, Set up nginx config file"
2993 if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
2994 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2995 _err
"write nginx conf error, but don't worry, the file is restored to the original version."
3000 location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
3001 default_type text/plain;
3002 return 200 \"\$1.$_thumbpt\";
3005 " >>"$FOUND_REAL_NGINX_CONF"
3007 if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
3008 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3009 _err
"write nginx conf error, but don't worry, the file is restored."
3012 _debug3
"Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
3013 _info
"nginx conf is done, let's check it again."
3014 if ! _exec
"nginx -t" >/dev
/null
; then
3016 _err
"It seems that nginx conf was broken, let's restore."
3017 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3021 _info
"Reload nginx"
3022 if ! _exec
"nginx -s reload" >/dev
/null
; then
3024 _err
"It seems that nginx reload error, let's restore."
3025 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3036 _debug
"Start _checkConf from:$_c_file"
3037 if [ ! -f "$2" ] && ! echo "$2" |
grep '*$' >/dev
/null
&& echo "$2" |
grep '*' >/dev
/null
; then
3040 if [ -f "$_w_f" ] && _checkConf
"$1" "$_w_f"; then
3046 elif [ -f "$2" ]; then
3048 if _isRealNginxConf
"$1" "$2"; then
3049 _debug
"$2 is found."
3050 FOUND_REAL_NGINX_CONF
="$2"
3053 if cat "$2" |
tr "\t" " " |
grep "^ *include *.*;" >/dev
/null
; then
3054 _debug
"Try include files"
3055 for included
in $
(cat "$2" |
tr "\t" " " |
grep "^ *include *.*;" |
sed "s/include //" |
tr -d " ;"); do
3056 _debug
"check included $included"
3057 if _checkConf
"$1" "$included"; then
3064 _debug
"$2 not found."
3071 _isRealNginxConf
() {
3072 _debug
"_isRealNginxConf $1 $2"
3073 if [ -f "$2" ]; then
3074 for _fln
in $
(tr "\t" ' ' <"$2" |
grep -n "^ *server_name.* $1" | cut
-d : -f 1); do
3076 if [ "$_fln" ]; then
3077 _start
=$
(tr "\t" ' ' <"$2" | _head_n
"$_fln" |
grep -n "^ *server *" |
grep -v server_name | _tail_n
1)
3078 _debug
"_start" "$_start"
3079 _start_n
=$
(echo "$_start" | cut
-d : -f 1)
3080 _start_nn
=$
(_math
$_start_n + 1)
3081 _debug
"_start_n" "$_start_n"
3082 _debug
"_start_nn" "$_start_nn"
3084 _left
="$(sed -n "${_start_nn},99999p
" "$2")"
3085 _debug2 _left
"$_left"
3086 _end
="$(echo "$_left" | tr "\t" ' ' | grep -n "^
*server
*" | grep -v server_name | _head_n 1)"
3087 _debug
"_end" "$_end"
3088 if [ "$_end" ]; then
3089 _end_n
=$
(echo "$_end" | cut
-d : -f 1)
3090 _debug
"_end_n" "$_end_n"
3091 _seg_n
=$
(echo "$_left" |
sed -n "1,${_end_n}p")
3096 _debug
"_seg_n" "$_seg_n"
3099 for _listen_i
in $
(echo "$_seg_n" |
tr "\t" ' ' |
grep "^ *listen" |
tr -d " "); do
3100 if [ "$_listen_i" ]; then
3101 if [ "$(echo "$_listen_i" | _egrep_o "listen.
*ssl
")" ]; then
3102 _debug2
"$_listen_i is ssl"
3104 _debug2
"$_listen_i is plain text"
3111 if [ "$_skip_ssl" = "1" ]; then
3112 _debug
"ssl on, skip"
3114 FOUND_REAL_NGINX_CONF_LN
=$_fln
3115 _debug3
"found FOUND_REAL_NGINX_CONF_LN" "$FOUND_REAL_NGINX_CONF_LN"
3124 #restore all the nginx conf
3126 if [ -z "$NGINX_RESTORE_VLIST" ]; then
3127 _debug
"No need to restore nginx, skip."
3130 _debug
"_restoreNginx"
3131 _debug
"NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
3133 for ng_entry
in $
(echo "$NGINX_RESTORE_VLIST" |
tr "$dvsep" ' '); do
3134 _debug
"ng_entry" "$ng_entry"
3135 _nd
=$
(echo "$ng_entry" | cut
-d "$sep" -f 1)
3136 _ngconf
=$
(echo "$ng_entry" | cut
-d "$sep" -f 2)
3137 _ngbackupconf
=$
(echo "$ng_entry" | cut
-d "$sep" -f 3)
3138 _info
"Restoring from $_ngbackupconf to $_ngconf"
3139 cat "$_ngbackupconf" >"$_ngconf"
3142 _info
"Reload nginx"
3143 if ! _exec
"nginx -s reload" >/dev
/null
; then
3145 _err
"It seems that nginx reload error, please report bug."
3152 _stopserver
"$serverproc"
3157 if [ -z "$DEBUG" ]; then
3166 _debug
"_clearupdns"
3167 _debug
"dns_entries" "$dns_entries"
3169 if [ -z "$dns_entries" ]; then
3173 _info
"Removing DNS records."
3175 for entry
in $dns_entries; do
3176 d
=$
(_getfield
"$entry" 1)
3177 txtdomain
=$
(_getfield
"$entry" 2)
3178 aliasDomain
=$
(_getfield
"$entry" 3)
3179 _currentRoot
=$
(_getfield
"$entry" 4)
3180 txt
=$
(_getfield
"$entry" 5)
3181 d_api
=$
(_getfield
"$entry" 6)
3183 _debug
"txtdomain" "$txtdomain"
3184 _debug
"aliasDomain" "$aliasDomain"
3185 _debug
"_currentRoot" "$_currentRoot"
3187 _debug
"d_api" "$d_api"
3188 if [ "$d_api" = "$txt" ]; then
3192 if [ -z "$d_api" ]; then
3193 _info
"Not Found domain api file: $d_api"
3197 if [ "$aliasDomain" ]; then
3198 txtdomain
="$aliasDomain"
3202 if ! .
"$d_api"; then
3203 _err
"Load file $d_api error. Please check your api file and try again."
3207 rmcommand
="${_currentRoot}_rm"
3208 if ! _exists
"$rmcommand"; then
3209 _err
"It seems that your api file doesn't define $rmcommand"
3212 _info
"Removing txt: $txt for domain: $txtdomain"
3213 if ! $rmcommand "$txtdomain" "$txt"; then
3214 _err
"Error removing txt for domain:$txtdomain"
3217 _info
"Removed: Success"
3223 # webroot removelevel tokenfile
3224 _clearupwebbroot
() {
3226 if [ -z "$__webroot" ]; then
3227 _debug
"no webroot specified, skip"
3232 if [ "$2" = '1' ]; then
3233 _rmpath
="$__webroot/.well-known"
3234 elif [ "$2" = '2' ]; then
3235 _rmpath
="$__webroot/.well-known/acme-challenge"
3236 elif [ "$2" = '3' ]; then
3237 _rmpath
="$__webroot/.well-known/acme-challenge/$3"
3239 _debug
"Skip for removelevel:$2"
3242 if [ "$_rmpath" ]; then
3243 if [ "$DEBUG" ]; then
3244 _debug
"Debugging, skip removing: $_rmpath"
3254 _on_before_issue
() {
3256 _chk_main_domain
="$2"
3257 _chk_alt_domains
="$3"
3259 _chk_local_addr
="$5"
3260 _debug _on_before_issue
3261 _debug _chk_main_domain
"$_chk_main_domain"
3262 _debug _chk_alt_domains
"$_chk_alt_domains"
3264 if [ "$_chk_pre_hook" ]; then
3265 _info
"Run pre hook:'$_chk_pre_hook'"
3267 cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
3269 _err
"Error when run pre hook."
3274 if _hasfield
"$_chk_web_roots" "$NO_VALUE"; then
3275 if ! _exists
"socat"; then
3276 _err
"Please install socat tools first."
3281 _debug Le_LocalAddress
"$_chk_local_addr"
3288 d
="$(echo "$_chk_main_domain,$_chk_alt_domains," | cut -d , -f "$_w_index")"
3289 _w_index
="$(_math "$_w_index" + 1)"
3291 if [ -z "$d" ]; then
3294 _debug
"Check for domain" "$d"
3295 _currentRoot
="$(_getfield "$_chk_web_roots" $_index)"
3296 _debug
"_currentRoot" "$_currentRoot"
3297 _index
=$
(_math
$_index + 1)
3299 if [ "$_currentRoot" = "$NO_VALUE" ]; then
3300 _info
"Standalone mode."
3301 if [ -z "$Le_HTTPPort" ]; then
3303 _cleardomainconf
"Le_HTTPPort"
3305 _savedomainconf
"Le_HTTPPort" "$Le_HTTPPort"
3307 _checkport
="$Le_HTTPPort"
3308 elif [ "$_currentRoot" = "$W_ALPN" ]; then
3309 _info
"Standalone alpn mode."
3310 if [ -z "$Le_TLSPort" ]; then
3313 _savedomainconf
"Le_TLSPort" "$Le_TLSPort"
3315 _checkport
="$Le_TLSPort"
3318 if [ "$_checkport" ]; then
3319 _debug _checkport
"$_checkport"
3320 _checkaddr
="$(_getfield "$_chk_local_addr" $_addrIndex)"
3321 _debug _checkaddr
"$_checkaddr"
3323 _addrIndex
="$(_math $_addrIndex + 1)"
3325 _netprc
="$(_ss "$_checkport" | grep "$_checkport")"
3326 netprc
="$(echo "$_netprc" | grep "$_checkaddr")"
3327 if [ -z "$netprc" ]; then
3328 netprc
="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
3330 if [ "$netprc" ]; then
3332 _err
"tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
3333 _err
"Please stop it first"
3339 if _hasfield
"$_chk_web_roots" "apache"; then
3340 if ! _setApache
; then
3341 _err
"set up apache error. Report error to me."
3353 _debug _on_issue_err
3355 if [ "$LOG_FILE" ]; then
3356 _err
"Please check log file for more details: $LOG_FILE"
3358 _err
"Please add '--debug' or '--log' to check more details."
3359 _err
"See: $_DEBUG_WIKI"
3363 if [ "$_chk_post_hook" ]; then
3364 _info
"Run post hook:'$_chk_post_hook'"
3366 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3368 _err
"Error when run post hook."
3373 #trigger the validation to flush the pending authz
3374 _debug2
"_chk_vlist" "$_chk_vlist"
3375 if [ "$_chk_vlist" ]; then
3377 _debug2
"start to deactivate authz"
3378 ventries
=$
(echo "$_chk_vlist" |
tr "$dvsep" ' ')
3379 for ventry
in $ventries; do
3380 d
=$
(echo "$ventry" | cut
-d "$sep" -f 1)
3381 keyauthorization
=$
(echo "$ventry" | cut
-d "$sep" -f 2)
3382 uri
=$
(echo "$ventry" | cut
-d "$sep" -f 3)
3383 vtype
=$
(echo "$ventry" | cut
-d "$sep" -f 4)
3384 _currentRoot
=$
(echo "$ventry" | cut
-d "$sep" -f 5)
3385 __trigger_validation
"$uri" "$keyauthorization"
3390 if [ "$_ACME_IS_RENEW" = "1" ] && _hasfield
"$Le_Webroot" "$W_DNS"; then
3391 _err
"$_DNS_MANUAL_ERR"
3394 if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
3395 _debug
"$(_dlg_versions)"
3400 _on_issue_success
() {
3402 _chk_renew_hook
="$2"
3403 _debug _on_issue_success
3406 if [ "$_chk_post_hook" ]; then
3407 _info
"Run post hook:'$_chk_post_hook'"
3410 export CERT_KEY_PATH
3412 export CERT_FULLCHAIN_PATH
3413 export Le_Domain
="$_main_domain"
3414 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3416 _err
"Error when run post hook."
3422 if [ "$_ACME_IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
3423 _info
"Run renew hook:'$_chk_renew_hook'"
3426 export CERT_KEY_PATH
3428 export CERT_FULLCHAIN_PATH
3429 export Le_Domain
="$_main_domain"
3430 cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
3432 _err
"Error when run renew hook."
3437 if _hasfield
"$Le_Webroot" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
3438 _err
"$_DNS_MANUAL_WARN"
3443 #account_key_length eab-kid eab-hmac-key
3445 _account_key_length
="$1"
3449 _regAccount
"$_account_key_length" "$_eab_id" "$_eab_hmac_key"
3452 __calcAccountKeyHash
() {
3453 [ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256
<"$ACCOUNT_KEY_PATH"
3456 __calc_account_thumbprint
() {
3457 printf "%s" "$jwk" |
tr -d ' ' | _digest
"sha256" | _url_replace
3460 _getAccountEmail
() {
3461 if [ "$ACCOUNT_EMAIL" ]; then
3462 echo "$ACCOUNT_EMAIL"
3465 if [ -z "$CA_EMAIL" ]; then
3466 CA_EMAIL
="$(_readcaconf CA_EMAIL)"
3468 if [ "$CA_EMAIL" ]; then
3472 _readaccountconf
"ACCOUNT_EMAIL"
3481 _debug3 _regAccount
"$_regAccount"
3485 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3486 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3487 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3490 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3491 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3492 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3495 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3496 if ! _create_account_key
"$_reg_length"; then
3497 _err
"Create account key error."
3502 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3505 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3506 _savecaconf CA_EAB_KEY_ID
"$_eab_id"
3507 _savecaconf CA_EAB_HMAC_KEY
"$_eab_hmac_key"
3509 _eab_id
=$
(_readcaconf
"CA_EAB_KEY_ID")
3510 _eab_hmac_key
=$
(_readcaconf
"CA_EAB_HMAC_KEY")
3511 _secure_debug3 _eab_id
"$_eab_id"
3512 _secure_debug3 _eab_hmac_key
"$_eab_hmac_key"
3513 _email
="$(_getAccountEmail)"
3514 if [ "$_email" ]; then
3515 _savecaconf
"CA_EMAIL" "$_email"
3517 if [ "$ACME_VERSION" = "2" ]; then
3518 if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then
3519 if [ -z "$_eab_id" ] ||
[ -z "$_eab_hmac_key" ]; then
3520 _info
"No EAB credentials found for ZeroSSL, let's get one"
3521 if [ -z "$_email" ]; then
3522 _err
"Please provide a email address for ZeroSSL account."
3523 _err
"See ZeroSSL usage: $_ZEROSSL_WIKI"
3526 _eabresp
=$
(_post
"email=$_email" $_ZERO_EAB_ENDPOINT)
3527 if [ "$?" != "0" ]; then
3529 _err
"Can not get EAB credentials from ZeroSSL."
3532 _eab_id
="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_kid
"' | cut -d : -f 2 | tr -d '"')"
3533 if [ -z "$_eab_id" ]; then
3534 _err "Can not resolve _eab_id"
3537 _eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')"
3538 if [ -z "$_eab_hmac_key" ]; then
3539 _err
"Can not resolve _eab_hmac_key"
3542 _savecaconf CA_EAB_KEY_ID
"$_eab_id"
3543 _savecaconf CA_EAB_HMAC_KEY
"$_eab_hmac_key"
3546 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3547 eab_protected
="{\"alg\":\"HS256\",\"kid\":\"$_eab_id\",\"url\":\"${ACME_NEW_ACCOUNT}\"}"
3548 _debug3 eab_protected
"$eab_protected"
3550 eab_protected64
=$
(printf "%s" "$eab_protected" | _base64 | _url_replace
)
3551 _debug3 eab_protected64
"$eab_protected64"
3553 eab_payload64
=$
(printf "%s" "$jwk" | _base64 | _url_replace
)
3554 _debug3 eab_payload64
"$eab_payload64"
3556 eab_sign_t
="$eab_protected64.$eab_payload64"
3557 _debug3 eab_sign_t
"$eab_sign_t"
3559 key_hex
="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')"
3560 _debug3 key_hex
"$key_hex"
3562 eab_signature
=$
(printf "%s" "$eab_sign_t" | _hmac sha256
$key_hex | _base64 | _url_replace
)
3563 _debug3 eab_signature
"$eab_signature"
3565 externalBinding
=",\"externalAccountBinding\":{\"protected\":\"$eab_protected64\", \"payload\":\"$eab_payload64\", \"signature\":\"$eab_signature\"}"
3566 _debug3 externalBinding
"$externalBinding"
3568 if [ "$_email" ]; then
3569 email_sg
="\"contact\": [\"mailto:$_email\"], "
3571 regjson
="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}"
3573 _reg_res
="$ACME_NEW_ACCOUNT_RES"
3574 regjson
='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
3575 if [ "$_email" ]; then
3576 regjson
='{"resource": "'$_reg_res'", "contact": ["mailto:'$_email'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
3580 _info
"Registering account: $ACME_DIRECTORY"
3582 if ! _send_signed_request
"${ACME_NEW_ACCOUNT}" "$regjson"; then
3583 _err
"Register account Error: $response"
3588 if [ "$code" = "" ] ||
[ "$code" = '201' ]; then
3589 echo "$response" >"$ACCOUNT_JSON_PATH"
3591 elif [ "$code" = '409' ] ||
[ "$code" = '200' ]; then
3592 _info
"Already registered"
3593 elif [ "$code" = '400' ] && _contains
"$response" 'The account is not awaiting external account binding'; then
3594 _info
"Already register EAB."
3597 _err
"Register account Error: $response"
3601 if [ -z "$_eabAlreadyBound" ]; then
3602 _debug2 responseHeaders
"$responseHeaders"
3603 _accUri
="$(echo "$responseHeaders" | grep -i "^Location
:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
3604 _debug
"_accUri" "$_accUri"
3605 if [ -z "$_accUri" ]; then
3606 _err
"Can not find account id url."
3607 _err
"$responseHeaders"
3610 _savecaconf
"ACCOUNT_URL" "$_accUri"
3612 ACCOUNT_URL
="$(_readcaconf ACCOUNT_URL)"
3614 export ACCOUNT_URL
="$_accUri"
3616 CA_KEY_HASH
="$(__calcAccountKeyHash)"
3617 _debug
"Calc CA_KEY_HASH" "$CA_KEY_HASH"
3618 _savecaconf CA_KEY_HASH
"$CA_KEY_HASH"
3620 if [ "$code" = '403' ]; then
3621 _err
"It seems that the account key is already deactivated, please use a new account key."
3625 ACCOUNT_THUMBPRINT
="$(__calc_account_thumbprint)"
3626 _info
"ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
3629 #implement updateaccount
3633 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3634 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3635 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3638 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3639 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3640 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3643 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3644 _err
"Account key is not found at: $ACCOUNT_KEY_PATH"
3648 _accUri
=$
(_readcaconf
"ACCOUNT_URL")
3649 _debug _accUri
"$_accUri"
3651 if [ -z "$_accUri" ]; then
3652 _err
"The account url is empty, please run '--update-account' first to update the account info first,"
3653 _err
"Then try again."
3657 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3662 _email
="$(_getAccountEmail)"
3663 if [ "$ACME_VERSION" = "2" ]; then
3664 if [ "$ACCOUNT_EMAIL" ]; then
3665 updjson
='{"contact": ["mailto:'$_email'"]}'
3667 updjson
='{"contact": []}'
3670 # ACMEv1: Updates happen the same way a registration is done.
3671 # https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-6.3
3676 # this part handles ACMEv2 account updates.
3677 _send_signed_request
"$_accUri" "$updjson"
3679 if [ "$code" = '200' ]; then
3680 echo "$response" >"$ACCOUNT_JSON_PATH"
3681 _info
"account update success for $_accUri."
3683 _info
"Error. The account was not updated."
3688 #Implement deactivate account
3689 deactivateaccount
() {
3692 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3693 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3694 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3697 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3698 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3699 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3702 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3703 _err
"Account key is not found at: $ACCOUNT_KEY_PATH"
3707 _accUri
=$
(_readcaconf
"ACCOUNT_URL")
3708 _debug _accUri
"$_accUri"
3710 if [ -z "$_accUri" ]; then
3711 _err
"The account url is empty, please run '--update-account' first to update the account info first,"
3712 _err
"Then try again."
3716 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3721 if [ "$ACME_VERSION" = "2" ]; then
3722 _djson
="{\"status\":\"deactivated\"}"
3724 _djson
="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
3726 if _send_signed_request
"$_accUri" "$_djson" && _contains
"$response" '"deactivated"'; then
3727 _info
"Deactivate account success for $_accUri."
3728 _accid
=$
(echo "$response" | _egrep_o
"\"id\" *: *[^,]*," | cut
-d : -f 2 |
tr -d ' ,')
3729 elif [ "$code" = "403" ]; then
3730 _info
"The account is already deactivated."
3731 _accid
=$
(_getfield
"$_accUri" "999" "/")
3733 _err
"Deactivate: account failed for $_accUri."
3737 _debug
"Account id: $_accid"
3738 if [ "$_accid" ]; then
3739 _deactivated_account_path
="$CA_DIR/deactivated/$_accid"
3740 _debug _deactivated_account_path
"$_deactivated_account_path"
3741 if mkdir
-p "$_deactivated_account_path"; then
3742 _info
"Moving deactivated account info to $_deactivated_account_path/"
3743 mv "$CA_CONF" "$_deactivated_account_path/"
3744 mv "$ACCOUNT_JSON_PATH" "$_deactivated_account_path/"
3745 mv "$ACCOUNT_KEY_PATH" "$_deactivated_account_path/"
3747 _err
"Can not create dir: $_deactivated_account_path, try to remove the deactivated account key."
3749 rm -f "$ACCOUNT_JSON_PATH"
3750 rm -f "$ACCOUNT_KEY_PATH"
3755 # domain folder file
3761 if [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname" ]; then
3762 d_api
="$_SCRIPT_HOME/$_hookcat/$_hookname"
3763 elif [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname.sh" ]; then
3764 d_api
="$_SCRIPT_HOME/$_hookcat/$_hookname.sh"
3765 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then
3766 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname"
3767 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then
3768 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
3769 elif [ -f "$LE_WORKING_DIR/$_hookname" ]; then
3770 d_api
="$LE_WORKING_DIR/$_hookname"
3771 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ]; then
3772 d_api
="$LE_WORKING_DIR/$_hookname.sh"
3773 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ]; then
3774 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname"
3775 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ]; then
3776 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
3779 printf "%s" "$d_api"
3783 __get_domain_new_authz
() {
3785 _info
"Getting new-authz for domain" "$_gdnd"
3787 _Max_new_authz_retry_times
=5
3789 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do
3790 _debug
"Try new-authz for the $_authz_i time."
3791 if ! _send_signed_request
"${ACME_NEW_AUTHZ}" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then
3792 _err
"Can not get domain new authz."
3795 if _contains
"$response" "No registration exists matching provided key"; then
3796 _err
"It seems there is an error, but it's recovered now, please try again."
3797 _err
"If you see this message for a second time, please report bug: $(__green "$PROJECT")"
3798 _clearcaconf
"CA_KEY_HASH"
3801 if ! _contains
"$response" "An error occurred while processing your request"; then
3802 _info
"The new-authz request is ok."
3805 _authz_i
="$(_math "$_authz_i" + 1)"
3806 _info
"The server is busy, Sleep $_authz_i to retry."
3810 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ]; then
3811 _err
"new-authz retry reach the max $_Max_new_authz_retry_times times."
3814 if [ "$code" ] && [ "$code" != '201' ]; then
3815 _err
"new-authz error: $response"
3821 #uri keyAuthorization
3822 __trigger_validation
() {
3823 _debug2
"Trigger domain validation."
3825 _debug2 _t_url
"$_t_url"
3827 _debug2 _t_key_authz
"$_t_key_authz"
3829 _debug2 _t_vtype
"$_t_vtype"
3830 if [ "$ACME_VERSION" = "2" ]; then
3831 _send_signed_request
"$_t_url" "{}"
3833 _send_signed_request
"$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
3837 #endpoint domain type
3842 _debug2
"_ns_ep" "$_ns_ep"
3843 _debug2
"_ns_domain" "$_ns_domain"
3844 _debug2
"_ns_type" "$_ns_type"
3846 response
="$(_H1="accept
: application
/dns-json
" _get "$_ns_ep?name
=$_ns_domain&type=$_ns_type")"
3848 _debug2
"response" "$response"
3849 if [ "$_ret" != "0" ]; then
3852 _answers
="$(echo "$response" | tr '{}' '<>' | _egrep_o '"Answer
":\[[^]]*]' | tr '<>' '\n\n')"
3853 _debug2
"_answers" "$_answers"
3861 _cf_ep
="https://cloudflare-dns.com/dns-query"
3862 _ns_lookup_impl
"$_cf_ep" "$_cf_ld" "$_cf_ld_type"
3869 _debug
"Cloudflare purge $_cf_d_type record for domain $_cf_d"
3870 _cf_purl
="https://cloudflare-dns.com/api/v1/purge?domain=$_cf_d&type=$_cf_d_type"
3871 response
="$(_post "" "$_cf_purl")"
3872 _debug2 response
"$response"
3875 #checks if cf server is available
3876 _ns_is_available_cf
() {
3877 if _get
"https://cloudflare-dns.com" >/dev
/null
2>&1; then
3885 _ns_lookup_google
() {
3888 _cf_ep
="https://dns.google/resolve"
3889 _ns_lookup_impl
"$_cf_ep" "$_cf_ld" "$_cf_ld_type"
3894 if [ -z "$DOH_USE" ]; then
3895 _debug
"Detect dns server first."
3896 if _ns_is_available_cf
; then
3897 _debug
"Use cloudflare doh server"
3898 export DOH_USE
=$DOH_CLOUDFLARE
3900 _debug
"Use google doh server"
3901 export DOH_USE
=$DOH_GOOGLE
3905 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] ||
[ -z "$DOH_USE" ]; then
3908 _ns_lookup_google
"$@"
3913 #txtdomain, alias, txt
3918 _debug
"_c_txtdomain" "$_c_txtdomain"
3919 _debug
"_c_aliasdomain" "$_c_aliasdomain"
3920 _debug
"_c_txt" "$_c_txt"
3921 _answers
="$(_ns_lookup "$_c_aliasdomain" TXT)"
3922 _contains
"$_answers" "$_c_txt"
3929 _debug _p_txtdomain
"$_p_txtdomain"
3930 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] ||
[ -z "$DOH_USE" ]; then
3931 _ns_purge_cf
"$_p_txtdomain" "TXT"
3933 _debug
"no purge api for google dns api, just sleep 5 secs"
3939 #wait and check each dns entries
3940 _check_dns_entries
() {
3942 _end_time
="$(_time)"
3943 _end_time
="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes.
3945 while [ "$(_time)" -le "$_end_time" ]; do
3947 for entry
in $dns_entries; do
3948 d
=$
(_getfield
"$entry" 1)
3949 txtdomain
=$
(_getfield
"$entry" 2)
3950 txtdomain
=$
(_idn
"$txtdomain")
3951 aliasDomain
=$
(_getfield
"$entry" 3)
3952 aliasDomain
=$
(_idn
"$aliasDomain")
3953 txt
=$
(_getfield
"$entry" 5)
3954 d_api
=$
(_getfield
"$entry" 6)
3956 _debug
"txtdomain" "$txtdomain"
3957 _debug
"aliasDomain" "$aliasDomain"
3959 _debug
"d_api" "$d_api"
3960 _info
"Checking $d for $aliasDomain"
3961 if _contains
"$_success_txt" ",$txt,"; then
3962 _info
"Already success, continue next one."
3966 if __check_txt
"$txtdomain" "$aliasDomain" "$txt"; then
3967 _info
"Domain $d '$aliasDomain' success."
3968 _success_txt
="$_success_txt,$txt,"
3972 _info
"Not valid yet, let's wait 10 seconds and check next one."
3973 __purge_txt
"$txtdomain"
3974 if [ "$txtdomain" != "$aliasDomain" ]; then
3975 __purge_txt
"$aliasDomain"
3979 if [ "$_left" ]; then
3980 _info
"Let's wait 10 seconds and check again".
3983 _info
"All success, let's return"
3987 _info
"Timed out waiting for DNS."
3993 _get_cert_issuers
() {
3995 if _contains
"$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7"; then
3996 ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7
-nocrl -certfile $_cfile | openssl pkcs7
-print_certs -text -noout |
grep 'Issuer:' | _egrep_o
"CN *=[^,]*" | cut
-d = -f 2
3998 ${ACME_OPENSSL_BIN:-openssl} x509
-in $_cfile -text -noout |
grep 'Issuer:' | _egrep_o
"CN *=[^,]*" | cut
-d = -f 2
4006 _fissuers
="$(_get_cert_issuers $_cfile)"
4007 _debug2 _fissuers
"$_fissuers"
4008 _contains
"$_fissuers" "$_missuer"
4011 #webroot, domain domainlist keylength
4013 if [ -z "$2" ]; then
4014 _usage
"Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
4017 if [ -z "$1" ]; then
4018 _usage
"Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
4025 if _contains
"$_main_domain" ","; then
4026 _main_domain
=$
(echo "$2,$3" | cut
-d , -f 1)
4027 _alt_domains
=$
(echo "$2,$3" | cut
-d , -f 2- |
sed "s/,${NO_VALUE}$//")
4029 _debug _main_domain
"$_main_domain"
4030 _debug _alt_domains
"$_alt_domains"
4037 _real_fullchain
="$9"
4042 _challenge_alias
="${14}"
4043 _preferred_chain
="${15}"
4045 if [ -z "$_ACME_IS_RENEW" ]; then
4046 _initpath
"$_main_domain" "$_key_length"
4047 mkdir
-p "$DOMAIN_PATH"
4050 if _hasfield
"$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
4051 _err
"$_DNS_MANUAL_ERROR"
4055 _debug
"Using ACME_DIRECTORY: $ACME_DIRECTORY"
4059 if [ -f "$DOMAIN_CONF" ]; then
4060 Le_NextRenewTime
=$
(_readdomainconf Le_NextRenewTime
)
4061 _debug Le_NextRenewTime
"$Le_NextRenewTime"
4062 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
4063 _saved_domain
=$
(_readdomainconf Le_Domain
)
4064 _debug _saved_domain
"$_saved_domain"
4065 _saved_alt
=$
(_readdomainconf Le_Alt
)
4066 _debug _saved_alt
"$_saved_alt"
4067 if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
4068 _info
"Domains not changed."
4069 _info
"Skip, Next renewal time is: $(__green "$
(_readdomainconf Le_NextRenewTimeStr
)")"
4070 _info
"Add '$(__red '--force')' to force to renew."
4073 _info
"Domains have changed."
4078 _savedomainconf
"Le_Domain" "$_main_domain"
4079 _savedomainconf
"Le_Alt" "$_alt_domains"
4080 _savedomainconf
"Le_Webroot" "$_web_roots"
4082 _savedomainconf
"Le_PreHook" "$_pre_hook" "base64"
4083 _savedomainconf
"Le_PostHook" "$_post_hook" "base64"
4084 _savedomainconf
"Le_RenewHook" "$_renew_hook" "base64"
4086 if [ "$_local_addr" ]; then
4087 _savedomainconf
"Le_LocalAddress" "$_local_addr"
4089 _cleardomainconf
"Le_LocalAddress"
4091 if [ "$_challenge_alias" ]; then
4092 _savedomainconf
"Le_ChallengeAlias" "$_challenge_alias"
4094 _cleardomainconf
"Le_ChallengeAlias"
4096 if [ "$_preferred_chain" ]; then
4097 _savedomainconf
"Le_Preferred_Chain" "$_preferred_chain" "base64"
4099 _cleardomainconf
"Le_Preferred_Chain"
4102 Le_API
="$ACME_DIRECTORY"
4103 _savedomainconf
"Le_API" "$Le_API"
4105 _info
"Using CA: $ACME_DIRECTORY"
4106 if [ "$_alt_domains" = "$NO_VALUE" ]; then
4110 if [ "$_key_length" = "$NO_VALUE" ]; then
4114 if ! _on_before_issue
"$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
4115 _err
"_on_before_issue."
4119 _saved_account_key_hash
="$(_readcaconf "CA_KEY_HASH
")"
4120 _debug2 _saved_account_key_hash
"$_saved_account_key_hash"
4122 if [ -z "$ACCOUNT_URL" ] ||
[ -z "$_saved_account_key_hash" ] ||
[ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
4123 if ! _regAccount
"$_accountkeylength"; then
4124 _on_issue_err
"$_post_hook"
4128 _debug
"_saved_account_key_hash is not changed, skip register account."
4131 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
4132 _info
"Signing from existing CSR."
4134 _key
=$
(_readdomainconf Le_Keylength
)
4135 _debug
"Read key length:$_key"
4136 if [ ! -f "$CERT_KEY_PATH" ] ||
[ "$_key_length" != "$_key" ] ||
[ "$Le_ForceNewDomainKey" = "1" ]; then
4137 if ! createDomainKey
"$_main_domain" "$_key_length"; then
4138 _err
"Create domain key error."
4140 _on_issue_err
"$_post_hook"
4145 if ! _createcsr
"$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
4146 _err
"Create CSR error."
4148 _on_issue_err
"$_post_hook"
4153 _savedomainconf
"Le_Keylength" "$_key_length"
4156 _cleardomainconf
"Le_Vlist"
4157 _info
"Getting domain auth token for each domain"
4160 if [ -z "$vlist" ]; then
4161 if [ "$ACME_VERSION" = "2" ]; then
4162 #make new order request
4163 _identifiers
="{\"type\":\"dns\",\"value\":\"$(_idn "$_main_domain")\"}"
4166 d
="$(echo "$_alt_domains," | cut -d , -f "$_w_index")"
4167 _w_index
="$(_math "$_w_index" + 1)"
4169 if [ -z "$d" ]; then
4172 _identifiers
="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn "$d")\"}"
4174 _debug2 _identifiers
"$_identifiers"
4175 if ! _send_signed_request
"$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
4176 _err
"Create new order error."
4178 _on_issue_err
"$_post_hook"
4181 Le_LinkOrder
="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
4182 _debug Le_LinkOrder
"$Le_LinkOrder"
4183 Le_OrderFinalize
="$(echo "$response" | _egrep_o '"finalize
" *: *"[^
"]*"' | cut -d '"' -f 4)"
4184 _debug Le_OrderFinalize
"$Le_OrderFinalize"
4185 if [ -z "$Le_OrderFinalize" ]; then
4186 _err
"Create new order error. Le_OrderFinalize not found. $response"
4188 _on_issue_err
"$_post_hook"
4192 #for dns manual mode
4193 _savedomainconf
"Le_OrderFinalize" "$Le_OrderFinalize"
4195 _authorizations_seg
="$(echo "$response" | _json_decode | _egrep_o '"authorizations
" *: *\[[^\[]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
4196 _debug2 _authorizations_seg "$_authorizations_seg"
4197 if [ -z "$_authorizations_seg" ]; then
4198 _err "_authorizations_seg not found."
4200 _on_issue_err "$_post_hook"
4204 #domain and authz map
4205 _authorizations_map=""
4206 for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
4207 _debug2 "_authz_url" "$_authz_url"
4208 if ! _send_signed_request "$_authz_url"; then
4209 _err "get to authz error."
4210 _err "_authorizations_seg" "$_authorizations_seg"
4211 _err "_authz_url" "$_authz_url"
4213 _on_issue_err "$_post_hook"
4217 response="$(echo "$response" | _normalizeJson)"
4218 _debug2 response "$response"
4219 _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
4220 if _contains "$response" "\"wildcard\" *: *true"; then
4224 _authorizations_map="$_d,$response
4225 $_authorizations_map"
4227 _debug2 _authorizations_map "$_authorizations_map"
4234 d="$(echo "$_main_domain,$_alt_domains," | cut -d , -f "$_w_index")"
4235 _w_index="$(_math "$_w_index" + 1)"
4237 if [ -z "$d" ]; then
4240 _info "Getting webroot for domain" "$d"
4241 _index=$(_math $_index + 1)
4242 _w="$(echo $_web_roots | cut -d , -f $_index)"
4247 _debug "_currentRoot" "$_currentRoot"
4250 #todo, v2 wildcard force to use dns
4251 if _startswith "$_currentRoot" "$W_DNS"; then
4255 if [ "$_currentRoot" = "$W_ALPN" ]; then
4259 if [ "$ACME_VERSION" = "2" ]; then
4260 _idn_d="$(_idn "$d")"
4261 _candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
4262 _debug2 _candidates "$_candidates"
4263 if [ "$(echo "$_candidates" | wc -l)" -gt 1 ]; then
4264 for _can in $_candidates; do
4265 if _startswith "$(echo "$_can" | tr '.
' '|
')" "$(echo "$_idn_d" | tr '.
' '|
'),"; then
4271 response="$(echo "$_candidates" | sed "s/$_idn_d,//")"
4272 _debug2 "response" "$response"
4273 if [ -z "$response" ]; then
4274 _err "get to authz error."
4275 _err "_authorizations_map" "$_authorizations_map"
4277 _on_issue_err "$_post_hook"
4281 if ! __get_domain_new_authz "$d"; then
4283 _on_issue_err "$_post_hook"
4288 if [ -z "$thumbprint" ]; then
4289 thumbprint="$(__calc_account_thumbprint)"
4292 entry="$(echo "$response" | _egrep_o '[^\
{]*"type":"'$vtype'"[^\
}]*')"
4293 _debug entry "$entry"
4295 if [ -z "$entry" ]; then
4296 if ! _startswith "$d" '*.
'; then
4297 _debug "Not a wildcard domain, lets check whether the validation is already valid."
4298 if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4299 _debug "$d is already valid."
4300 keyauthorization="$STATE_VERIFIED"
4301 _debug keyauthorization "$keyauthorization"
4304 if [ -z "$keyauthorization" ]; then
4305 _err "Error, can not get domain token entry $d for $vtype"
4306 _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
4307 if [ "$_supported_vtypes" ]; then
4308 _err
"The supported validation types are: $_supported_vtypes, but you specified: $vtype"
4311 _on_issue_err
"$_post_hook"
4316 if [ -z "$keyauthorization" ]; then
4317 token
="$(echo "$entry" | _egrep_o '"token
":"[^
"]*' | cut -d : -f 2 | tr -d '"')"
4318 _debug token "$token"
4320 if [ -z "$token" ]; then
4321 _err "Error, can not get domain token $entry"
4323 _on_issue_err "$_post_hook"
4326 if [ "$ACME_VERSION" = "2" ]; then
4327 uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
4329 uri
="$(echo "$entry" | _egrep_o '"uri
":"[^
"]*' | cut -d '"' -f 4)"
4333 if [ -z "$uri" ]; then
4334 _err "Error, can not get domain uri. $entry"
4336 _on_issue_err "$_post_hook"
4339 keyauthorization="$token.$thumbprint"
4340 _debug keyauthorization "$keyauthorization"
4342 if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4343 _debug "$d is already verified."
4344 keyauthorization="$STATE_VERIFIED"
4345 _debug keyauthorization "$keyauthorization"
4349 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
4350 _debug dvlist "$dvlist"
4352 vlist="$vlist$dvlist$dvsep"
4355 _debug vlist "$vlist"
4359 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
4361 for ventry in $ventries; do
4362 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4363 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4364 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4365 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
4367 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
4368 _debug "$d is already verified, skip $vtype."
4369 _alias_index="$(_math "$_alias_index" + 1)"
4373 if [ "$vtype" = "$VTYPE_DNS" ]; then
4376 if _startswith "$_dns_root_d" "*."; then
4377 _dns_root_d="$(echo "$_dns_root_d" | sed 's
/*.
//')"
4379 _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
4380 _alias_index="$(_math "$_alias_index" + 1)"
4381 _debug "_d_alias" "$_d_alias"
4382 if [ "$_d_alias" ]; then
4383 if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
4384 txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
4386 txtdomain="_acme-challenge.$_d_alias"
4388 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot"
4390 txtdomain="_acme-challenge.$_dns_root_d"
4391 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot"
4394 _debug txtdomain "$txtdomain"
4395 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
4398 d_api="$(_findHook "$_dns_root_d" $_SUB_FOLDER_DNSAPI "$_currentRoot")"
4399 _debug d_api "$d_api"
4401 dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api"
4402 _debug2 dns_entry "$dns_entry"
4403 if [ "$d_api" ]; then
4404 _debug "Found domain api file: $d_api"
4406 if [ "$_currentRoot" != "$W_DNS" ]; then
4407 _err "Can not find dns api hook for: $_currentRoot"
4408 _info "You need to add the txt record manually."
4410 _info "$(__red "Add the following TXT record:")"
4411 _info "$(__red "Domain: '$
(__green
"$txtdomain")'")"
4412 _info "$(__red "TXT value: '$
(__green
"$txt")'")"
4413 _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
4414 _info "$(__red "so the resulting subdomain will be: $txtdomain")"
4419 if ! . "$d_api"; then
4420 _err "Load file $d_api error. Please check your api file and try again."
4424 addcommand="${_currentRoot}_add"
4425 if ! _exists "$addcommand"; then
4426 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
4429 _info "Adding txt value: $txt for domain: $txtdomain"
4430 if ! $addcommand "$txtdomain" "$txt"; then
4431 _err "Error add txt for domain:$txtdomain"
4434 _info "The txt record is added: Success."
4437 if [ "$?" != "0" ]; then
4438 _on_issue_err "$_post_hook" "$vlist"
4442 dns_entries="$dns_entries$dns_entry
4444 _debug2 "$dns_entries"
4449 if [ "$dnsadded" = '0' ]; then
4450 _savedomainconf "Le_Vlist" "$vlist"
4451 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
4452 _err "Please add the TXT records to the domains, and re-run with --renew."
4453 _on_issue_err "$_post_hook"
4460 if [ "$dns_entries" ]; then
4461 if [ -z "$Le_DNSSleep" ]; then
4462 _info "Let's check each DNS record now. Sleep
20 seconds first.
"
4464 if ! _check_dns_entries; then
4465 _err "check dns error.
"
4466 _on_issue_err "$_post_hook"
4471 _savedomainconf "Le_DNSSleep
" "$Le_DNSSleep"
4472 _info "Sleep $
(__green
$Le_DNSSleep) seconds
for the txt records to take effect
"
4473 _sleep "$Le_DNSSleep"
4477 NGINX_RESTORE_VLIST=""
4478 _debug "ok
, let's start to verify"
4481 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
4482 for ventry in $ventries; do
4483 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4484 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4485 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
4486 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4487 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
4489 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
4490 _info "$d is already verified, skip $vtype."
4494 _info "Verifying: $d"
4496 _debug "keyauthorization" "$keyauthorization"
4499 token="$(printf "%s" "$keyauthorization" | cut -d '.
' -f 1)"
4501 _debug "_currentRoot" "$_currentRoot"
4503 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4504 if [ "$_currentRoot" = "$NO_VALUE" ]; then
4505 _info "Standalone mode server"
4506 _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
4507 _ncIndex="$(_math $_ncIndex + 1)"
4508 _startserver "$keyauthorization" "$_ncaddr"
4509 if [ "$?" != "0" ]; then
4511 _on_issue_err "$_post_hook" "$vlist"
4515 _debug serverproc "$serverproc"
4516 elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
4517 _info "Stateless mode for domain:$d"
4519 elif _startswith "$_currentRoot" "$NGINX"; then
4520 _info "Nginx mode for domain:$d"
4521 #set up nginx server
4522 FOUND_REAL_NGINX_CONF=""
4523 BACKUP_NGINX_CONF=""
4524 if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
4526 _on_issue_err "$_post_hook" "$vlist"
4530 if [ "$FOUND_REAL_NGINX_CONF" ]; then
4531 _realConf="$FOUND_REAL_NGINX_CONF"
4532 _backup="$BACKUP_NGINX_CONF"
4533 _debug _realConf "$_realConf"
4534 NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
4538 if [ "$_currentRoot" = "apache" ]; then
4539 wellknown_path="$ACME_DIR"
4541 wellknown_path="$_currentRoot/.well-known/acme-challenge"
4542 if [ ! -d "$_currentRoot/.well-known" ]; then
4544 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ]; then
4551 _debug wellknown_path "$wellknown_path"
4553 _debug "writing token:$token to $wellknown_path/$token"
4555 mkdir -p "$wellknown_path"
4557 if ! printf "%s" "$keyauthorization" >"$wellknown_path/$token"; then
4558 _err "$d:Can not write token to file : $wellknown_path/$token"
4559 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4561 _on_issue_err "$_post_hook" "$vlist"
4565 if [ ! "$usingApache" ]; then
4566 if webroot_owner=$(_stat "$_currentRoot"); then
4567 _debug "Changing owner/group of .well-known to $webroot_owner"
4568 if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
4569 _debug "$(cat "$_EXEC_TEMP_ERR")"
4570 _exec_err >/dev/null 2>&1
4573 _debug "not changing owner/group of webroot"
4578 elif [ "$vtype" = "$VTYPE_ALPN" ]; then
4579 acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
4580 _debug acmevalidationv1 "$acmevalidationv1"
4581 if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then
4582 _err "Start tls server error."
4583 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4585 _on_issue_err "$_post_hook" "$vlist"
4590 if ! __trigger_validation "$uri" "$keyauthorization" "$vtype"; then
4591 _err "$d:Can not get challenge: $response"
4592 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4594 _on_issue_err "$_post_hook" "$vlist"
4598 if [ "$code" ] && [ "$code" != '202' ]; then
4599 if [ "$code" = '200' ]; then
4600 _debug "trigger validation code: $code"
4602 _err "$d:Challenge error: $response"
4603 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4605 _on_issue_err "$_post_hook" "$vlist"
4611 if [ -z "$MAX_RETRY_TIMES" ]; then
4616 waittimes=$(_math "$waittimes" + 1)
4617 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
4619 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4621 _on_issue_err "$_post_hook" "$vlist"
4625 _debug "sleep 2 secs to verify"
4628 if [ "$ACME_VERSION" = "2" ]; then
4629 _send_signed_request "$uri"
4631 response="$(_get "$uri")"
4633 if [ "$?" != "0" ]; then
4634 _err "$d:Verify error:$response"
4635 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4637 _on_issue_err "$_post_hook" "$vlist"
4640 _debug2 original "$response"
4642 response="$(echo "$response" | _normalizeJson)"
4643 _debug2 response "$response"
4645 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
4646 if [ "$status" = "valid
" ]; then
4647 _info "$
(__green Success
)"
4648 _stopserver "$serverproc"
4650 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4654 if [ "$status" = "invalid
" ]; then
4655 error="$
(echo "$response" | _egrep_o
'"error":\{[^\}]*')"
4656 _debug2 error "$error"
4657 errordetail="$
(echo "$error" | _egrep_o
'"detail": *"[^"]*' | cut
-d '"' -f 4)"
4658 _debug2 errordetail "$errordetail"
4659 if [ "$errordetail" ]; then
4660 _err "$d:Verify error
:$errordetail"
4662 _err "$d:Verify error
:$error"
4664 if [ "$DEBUG" ]; then
4665 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4666 _debug "Debug
: get token url.
"
4667 _get "http
://$d/.well-known
/acme-challenge
/$token" "" 1
4670 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4672 _on_issue_err "$_post_hook" "$vlist"
4676 if [ "$status" = "pending
" ]; then
4678 elif [ "$status" = "processing
" ]; then
4681 _err "$d:Verify error
:$response"
4682 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4684 _on_issue_err "$_post_hook" "$vlist"
4693 _info "Verify finished
, start to sign.
"
4694 der="$
(_getfile
"${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" |
tr -d "\r\n" | _url_replace
)"
4696 if [ "$ACME_VERSION" = "2" ]; then
4697 _info "Lets finalize the order.
"
4698 _info "Le_OrderFinalize
" "$Le_OrderFinalize"
4699 if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr
\": \"$der\"}"; then
4701 _on_issue_err "$_post_hook"
4704 if [ "$code" != "200" ]; then
4705 _err "Sign failed
, finalize code is not
200.
"
4707 _on_issue_err "$_post_hook"
4710 if [ -z "$Le_LinkOrder" ]; then
4711 Le_LinkOrder="$
(echo "$responseHeaders" |
grep -i '^Location.*$' | _tail_n
1 |
tr -d "\r\n" | cut
-d ":" -f 2-)"
4714 _savedomainconf "Le_LinkOrder
" "$Le_LinkOrder"
4718 while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do
4719 if _contains "$response" "\"status
\":\"valid
\""; then
4720 _debug "Order status is valid.
"
4721 Le_LinkCert="$
(echo "$response" | _egrep_o
'"certificate" *: *"[^"]*"' | cut
-d '"' -f 4)"
4722 _debug Le_LinkCert "$Le_LinkCert"
4723 if [ -z "$Le_LinkCert" ]; then
4724 _err "Sign error
, can not
find Le_LinkCert
"
4726 _on_issue_err "$_post_hook"
4730 elif _contains "$response" "\"processing
\""; then
4731 _info "Order status is processing
, lets
sleep and retry.
"
4732 _retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After
*:" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
4733 _debug "_retryafter
" "$_retryafter"
4734 if [ "$_retryafter" ]; then
4735 _info "Retry after
: $_retryafter"
4741 _err "Sign error
, wrong status
"
4743 _on_issue_err "$_post_hook"
4746 #the order is processing, so we are going to poll order status
4747 if [ -z "$Le_LinkOrder" ]; then
4748 _err "Sign error
, can not get order link location header
"
4749 _err "responseHeaders
" "$responseHeaders"
4750 _on_issue_err "$_post_hook"
4753 _info "Polling order status
: $Le_LinkOrder"
4754 if ! _send_signed_request "$Le_LinkOrder"; then
4755 _err "Sign failed
, can not post to Le_LinkOrder cert
:$Le_LinkOrder.
"
4757 _on_issue_err "$_post_hook"
4760 _link_cert_retry="$
(_math
$_link_cert_retry + 1)"
4763 if [ -z "$Le_LinkCert" ]; then
4764 _err "Sign failed
, can not get Le_LinkCert
, retry
time limit.
"
4766 _on_issue_err "$_post_hook"
4769 _info "Downloading cert.
"
4770 _info "Le_LinkCert
" "$Le_LinkCert"
4771 if ! _send_signed_request "$Le_LinkCert"; then
4772 _err "Sign failed
, can not download cert
:$Le_LinkCert.
"
4774 _on_issue_err "$_post_hook"
4778 echo "$response" >"$CERT_PATH"
4779 _split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH"
4781 if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then
4782 if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then
4783 rels="$
(echo "$responseHeaders" |
tr -d ' <>' |
grep -i "^link:" |
grep -i 'rel="alternate"' | cut
-d : -f 2- | cut
-d ';' -f 1)"
4784 _debug2 "rels
" "$rels"
4785 for rel in $rels; do
4786 _info "Try rel
: $rel"
4787 if ! _send_signed_request "$rel"; then
4788 _err "Sign failed
, can not download cert
:$rel"
4792 _relcert="$CERT_PATH.alt
"
4793 _relfullchain="$CERT_FULLCHAIN_PATH.alt
"
4794 _relca="$CA_CERT_PATH.alt
"
4795 echo "$response" >"$_relcert"
4796 _split_cert_chain "$_relcert" "$_relfullchain" "$_relca"
4797 if _match_issuer "$_relfullchain" "$_preferred_chain"; then
4798 _info "Matched issuer
in: $rel"
4799 cat $_relcert >"$CERT_PATH"
4800 cat $_relfullchain >"$CERT_FULLCHAIN_PATH"
4801 cat $_relca >"$CA_CERT_PATH"
4808 if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource
\": \"$ACME_NEW_ORDER_RES\", \"csr
\": \"$der\"}" "needbase64
"; then
4809 _err "Sign failed.
$response"
4810 _on_issue_err "$_post_hook"
4814 Le_LinkCert="$
(grep -i '^Location.*$' "$HTTP_HEADER" | _tail_n
1 |
tr -d "\r\n" | cut
-d " " -f 2)"
4815 echo "$BEGIN_CERT" >"$CERT_PATH"
4817 #if ! _get "$Le_LinkCert" | _base64 "multiline
" >> "$CERT_PATH" ; then
4818 # _debug "Get cert failed. Let
's try last response."
4819 # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
4822 if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
4823 _debug "Try cert link."
4824 _get "$Le_LinkCert" | _base64 "multiline" >>"$CERT_PATH"
4827 echo "$END_CERT" >>"$CERT_PATH"
4830 _debug "Le_LinkCert" "$Le_LinkCert"
4831 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
4833 if [ -z "$Le_LinkCert" ] || ! _checkcert "$CERT_PATH"; then
4834 response="$(echo "$response" | _dbase64 "multiline" | tr -d '\
0' | _normalizeJson)"
4835 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
4836 _on_issue_err
"$_post_hook"
4840 if [ "$Le_LinkCert" ]; then
4841 _info
"$(__green "Cert success.
")"
4844 _info
"Your cert is in $(__green " $CERT_PATH ")"
4846 if [ -f "$CERT_KEY_PATH" ]; then
4847 _info
"Your cert key is in $(__green " $CERT_KEY_PATH ")"
4850 if [ ! "$USER_PATH" ] ||
[ ! "$_ACME_IN_CRON" ]; then
4852 _saveaccountconf
"USER_PATH" "$USER_PATH"
4856 if [ "$ACME_VERSION" = "2" ]; then
4859 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
4860 Le_LinkIssuer
=$
(grep -i '^Link' "$HTTP_HEADER" | _head_n
1 | cut
-d " " -f 2 | cut
-d ';' -f 1 |
tr -d '<>')
4862 if [ "$Le_LinkIssuer" ]; then
4863 if ! _contains
"$Le_LinkIssuer" ":"; then
4864 _info
"$(__red "Relative issuer link found.
")"
4865 Le_LinkIssuer
="$_ACME_SERVER_HOST$Le_LinkIssuer"
4867 _debug Le_LinkIssuer
"$Le_LinkIssuer"
4868 _savedomainconf
"Le_LinkIssuer" "$Le_LinkIssuer"
4870 _link_issuer_retry
=0
4872 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
4873 _debug _link_issuer_retry
"$_link_issuer_retry"
4874 if [ "$ACME_VERSION" = "2" ]; then
4875 if _send_signed_request
"$Le_LinkIssuer"; then
4876 echo "$response" >"$CA_CERT_PATH"
4880 if _get
"$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
4881 echo "$BEGIN_CERT" >"$CA_CERT_PATH"
4882 _base64
"multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
4883 echo "$END_CERT" >>"$CA_CERT_PATH"
4884 if ! _checkcert
"$CA_CERT_PATH"; then
4885 _err
"Can not get the ca cert."
4888 cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
4889 rm -f "$CA_CERT_PATH.der"
4893 _link_issuer_retry
=$
(_math
$_link_issuer_retry + 1)
4894 _sleep
"$_link_issuer_retry"
4896 if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
4897 _err
"Max retry for issuer ca cert is reached."
4900 _debug
"No Le_LinkIssuer header found."
4903 [ -f "$CA_CERT_PATH" ] && _info
"The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
4904 [ -f "$CERT_FULLCHAIN_PATH" ] && _info
"And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
4906 Le_CertCreateTime
=$
(_time
)
4907 _savedomainconf
"Le_CertCreateTime" "$Le_CertCreateTime"
4909 Le_CertCreateTimeStr
=$
(date -u)
4910 _savedomainconf
"Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
4912 if [ -z "$Le_RenewalDays" ] ||
[ "$Le_RenewalDays" -lt "0" ]; then
4913 Le_RenewalDays
="$DEFAULT_RENEW"
4915 _savedomainconf
"Le_RenewalDays" "$Le_RenewalDays"
4918 if [ "$CA_BUNDLE" ]; then
4919 _saveaccountconf CA_BUNDLE
"$CA_BUNDLE"
4921 _clearaccountconf
"CA_BUNDLE"
4924 if [ "$CA_PATH" ]; then
4925 _saveaccountconf CA_PATH
"$CA_PATH"
4927 _clearaccountconf
"CA_PATH"
4930 if [ "$HTTPS_INSECURE" ]; then
4931 _saveaccountconf HTTPS_INSECURE
"$HTTPS_INSECURE"
4933 _clearaccountconf
"HTTPS_INSECURE"
4936 if [ "$Le_Listen_V4" ]; then
4937 _savedomainconf
"Le_Listen_V4" "$Le_Listen_V4"
4938 _cleardomainconf Le_Listen_V6
4939 elif [ "$Le_Listen_V6" ]; then
4940 _savedomainconf
"Le_Listen_V6" "$Le_Listen_V6"
4941 _cleardomainconf Le_Listen_V4
4944 if [ "$Le_ForceNewDomainKey" = "1" ]; then
4945 _savedomainconf
"Le_ForceNewDomainKey" "$Le_ForceNewDomainKey"
4947 _cleardomainconf Le_ForceNewDomainKey
4950 Le_NextRenewTime
=$
(_math
"$Le_CertCreateTime" + "$Le_RenewalDays" \
* 24 \
* 60 \
* 60)
4952 Le_NextRenewTimeStr
=$
(_time2str
"$Le_NextRenewTime")
4953 _savedomainconf
"Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
4955 Le_NextRenewTime
=$
(_math
"$Le_NextRenewTime" - 86400)
4956 _savedomainconf
"Le_NextRenewTime" "$Le_NextRenewTime"
4958 if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
4959 _savedomainconf
"Le_RealCertPath" "$_real_cert"
4960 _savedomainconf
"Le_RealCACertPath" "$_real_ca"
4961 _savedomainconf
"Le_RealKeyPath" "$_real_key"
4962 _savedomainconf
"Le_ReloadCmd" "$_reload_cmd" "base64"
4963 _savedomainconf
"Le_RealFullChainPath" "$_real_fullchain"
4964 if ! _installcert
"$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"; then
4969 if ! _on_issue_success
"$_post_hook" "$_renew_hook"; then
4970 _err
"Call hook error."
4975 #in_out_cert out_fullchain out out_ca
4976 _split_cert_chain
() {
4980 if [ "$(grep -- "$BEGIN_CERT" "$_certf" | wc -l)" -gt "1" ]; then
4981 _debug
"Found cert chain"
4982 cat "$_certf" >"$_fullchainf"
4983 _end_n
="$(grep -n -- "$END_CERT" "$_fullchainf" | _head_n 1 | cut -d : -f 1)"
4984 _debug _end_n
"$_end_n"
4985 sed -n "1,${_end_n}p" "$_fullchainf" >"$_certf"
4986 _end_n
="$(_math $_end_n + 1)"
4987 sed -n "${_end_n},9999p" "$_fullchainf" >"$_caf"
4994 if [ -z "$Le_Domain" ]; then
4995 _usage
"Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
5001 _initpath
"$Le_Domain" "$_isEcc"
5003 _info
"$(__green "Renew
: '$Le_Domain'")"
5004 if [ ! -f "$DOMAIN_CONF" ]; then
5005 _info
"'$Le_Domain' is not a issued domain, skip."
5009 if [ "$Le_RenewalDays" ]; then
5010 _savedomainconf Le_RenewalDays
"$Le_RenewalDays"
5014 _debug Le_API
"$Le_API"
5016 if [ "$Le_API" = "$LETSENCRYPT_CA_V1" ]; then
5017 _cleardomainconf Le_API
5018 Le_API
="$DEFAULT_CA"
5020 if [ "$Le_API" = "$LETSENCRYPT_STAGING_CA_V1" ]; then
5021 _cleardomainconf Le_API
5022 Le_API
="$DEFAULT_STAGING_CA"
5025 if [ "$Le_API" ]; then
5026 export ACME_DIRECTORY
="$Le_API"
5029 ACCOUNT_JSON_PATH
=""
5031 _debug3
"initpath again."
5032 _initpath
"$Le_Domain" "$_isEcc"
5035 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
5036 _info
"Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
5037 _info
"Add '$(__red '--force')' to force to renew."
5038 return "$RENEW_SKIP"
5041 if [ "$_ACME_IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
5042 _info
"Skip invalid cert for: $Le_Domain"
5047 Le_ReloadCmd
="$(_readdomainconf Le_ReloadCmd)"
5048 Le_PreHook
="$(_readdomainconf Le_PreHook)"
5049 Le_PostHook
="$(_readdomainconf Le_PostHook)"
5050 Le_RenewHook
="$(_readdomainconf Le_RenewHook)"
5051 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" "$Le_ChallengeAlias" "$Le_Preferred_Chain"
5053 if [ "$res" != "0" ]; then
5057 if [ "$Le_DeployHook" ]; then
5058 _deploy
"$Le_Domain" "$Le_DeployHook"
5067 #renewAll [stopRenewOnError]
5070 _stopRenewOnError
="$1"
5071 _debug
"_stopRenewOnError" "$_stopRenewOnError"
5076 _error_level
=$NOTIFY_LEVEL_SKIP
5077 _notify_code
=$RENEW_SKIP
5078 _set_level
=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
5079 _debug
"_set_level" "$_set_level"
5080 for di
in "${CERT_HOME}"/*.
*/; do
5082 if ! [ -d "$di" ]; then
5083 _debug
"Not directory, skip: $di"
5089 if _endswith
"$d" "$ECC_SUFFIX"; then
5090 _isEcc
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 2)
5091 d
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 1)
5093 renew
"$d" "$_isEcc"
5096 _debug
"Return code: $rc"
5097 if [ "$rc" = "0" ]; then
5098 if [ $_error_level -gt $NOTIFY_LEVEL_RENEW ]; then
5099 _error_level
="$NOTIFY_LEVEL_RENEW"
5102 if [ "$_ACME_IN_CRON" ]; then
5103 if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then
5104 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5105 _send_notify
"Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0
5109 _success_msg
="${_success_msg} $d
5111 elif [ "$rc" = "$RENEW_SKIP" ]; then
5112 if [ $_error_level -gt $NOTIFY_LEVEL_SKIP ]; then
5113 _error_level
="$NOTIFY_LEVEL_SKIP"
5114 _notify_code
=$RENEW_SKIP
5116 if [ "$_ACME_IN_CRON" ]; then
5117 if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then
5118 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5119 _send_notify
"Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP"
5124 _skipped_msg
="${_skipped_msg} $d
5127 if [ $_error_level -gt $NOTIFY_LEVEL_ERROR ]; then
5128 _error_level
="$NOTIFY_LEVEL_ERROR"
5131 if [ "$_ACME_IN_CRON" ]; then
5132 if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then
5133 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5134 _send_notify
"Renew $d error" "There is an error." "$NOTIFY_HOOK" 1
5138 _error_msg
="${_error_msg} $d
5140 if [ "$_stopRenewOnError" ]; then
5141 _err
"Error renew $d, stop now."
5146 _err
"Error renew $d."
5150 _debug _error_level
"$_error_level"
5151 _debug _set_level
"$_set_level"
5152 if [ "$_ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then
5153 if [ -z "$NOTIFY_MODE" ] ||
[ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then
5154 _msg_subject
="Renew"
5155 if [ "$_error_msg" ]; then
5156 _msg_subject
="${_msg_subject} Error"
5157 _msg_data
="Error certs:
5161 if [ "$_success_msg" ]; then
5162 _msg_subject
="${_msg_subject} Success"
5163 _msg_data
="${_msg_data}Success certs:
5167 if [ "$_skipped_msg" ]; then
5168 _msg_subject
="${_msg_subject} Skipped"
5169 _msg_data
="${_msg_data}Skipped certs:
5174 _send_notify
"$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" "$_notify_code"
5185 if [ -z "$_csrfile" ] ||
[ -z "$_csrW" ]; then
5186 _usage
"Usage: $PROJECT_ENTRY --signcsr --csr mycsr.csr -w /path/to/webroot/a.com/ "
5194 _real_fullchain
="$7"
5199 _challenge_alias
="${12}"
5201 _csrsubj
=$
(_readSubjectFromCSR
"$_csrfile")
5202 if [ "$?" != "0" ]; then
5203 _err
"Can not read subject from csr: $_csrfile"
5206 _debug _csrsubj
"$_csrsubj"
5207 if _contains
"$_csrsubj" ' ' ||
! _contains
"$_csrsubj" '.'; then
5208 _info
"It seems that the subject: $_csrsubj is not a valid domain name. Drop it."
5212 _csrdomainlist
=$
(_readSubjectAltNamesFromCSR
"$_csrfile")
5213 if [ "$?" != "0" ]; then
5214 _err
"Can not read domain list from csr: $_csrfile"
5217 _debug
"_csrdomainlist" "$_csrdomainlist"
5219 if [ -z "$_csrsubj" ]; then
5220 _csrsubj
="$(_getfield "$_csrdomainlist" 1)"
5221 _debug _csrsubj
"$_csrsubj"
5222 _csrdomainlist
="$(echo "$_csrdomainlist" | cut -d , -f 2-)"
5223 _debug
"_csrdomainlist" "$_csrdomainlist"
5226 if [ -z "$_csrsubj" ]; then
5227 _err
"Can not read subject from csr: $_csrfile"
5231 _csrkeylength
=$
(_readKeyLengthFromCSR
"$_csrfile")
5232 if [ "$?" != "0" ] ||
[ -z "$_csrkeylength" ]; then
5233 _err
"Can not read key length from csr: $_csrfile"
5237 if [ -z "$ACME_VERSION" ] && _contains
"$_csrsubj,$_csrdomainlist" "*."; then
5238 export ACME_VERSION
=2
5240 _initpath
"$_csrsubj" "$_csrkeylength"
5241 mkdir
-p "$DOMAIN_PATH"
5243 _info
"Copy csr to: $CSR_PATH"
5244 cp "$_csrfile" "$CSR_PATH"
5246 issue
"$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias"
5253 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
5254 _usage
"Usage: $PROJECT_ENTRY --showcsr --csr mycsr.csr"
5260 _csrsubj
=$
(_readSubjectFromCSR
"$_csrfile")
5261 if [ "$?" != "0" ] ||
[ -z "$_csrsubj" ]; then
5262 _err
"Can not read subject from csr: $_csrfile"
5266 _info
"Subject=$_csrsubj"
5268 _csrdomainlist
=$
(_readSubjectAltNamesFromCSR
"$_csrfile")
5269 if [ "$?" != "0" ]; then
5270 _err
"Can not read domain list from csr: $_csrfile"
5273 _debug
"_csrdomainlist" "$_csrdomainlist"
5275 _info
"SubjectAltNames=$_csrdomainlist"
5277 _csrkeylength
=$
(_readKeyLengthFromCSR
"$_csrfile")
5278 if [ "$?" != "0" ] ||
[ -z "$_csrkeylength" ]; then
5279 _err
"Can not read key length from csr: $_csrfile"
5282 _info
"KeyLength=$_csrkeylength"
5292 if [ "$_raw" ]; then
5293 if [ -z "$_domain" ]; then
5294 printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}CA${_sep}Created${_sep}Renew"
5296 for di
in "${CERT_HOME}"/*.
*/; do
5300 if _endswith
"$d" "$ECC_SUFFIX"; then
5302 d
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 1)
5304 DOMAIN_CONF
="$di/$d.conf"
5305 if [ -f "$DOMAIN_CONF" ]; then
5307 _ca
="$(_getCAShortName "$Le_API")"
5308 if [ -z "$_domain" ]; then
5309 printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
5311 if [ "$_domain" = "$d" ]; then
5319 if _exists
column; then
5320 list
"raw" "$_domain" |
column -t -s "$_sep"
5322 list
"raw" "$_domain" |
tr "$_sep" '\t'
5332 for _d_api
in $
(echo "$_hooks" |
tr ',' " "); do
5333 _deployApi
="$(_findHook "$_d" $_SUB_FOLDER_DEPLOY "$_d_api")"
5334 if [ -z "$_deployApi" ]; then
5335 _err
"The deploy hook $_d_api is not found."
5338 _debug _deployApi
"$_deployApi"
5341 if ! .
"$_deployApi"; then
5342 _err
"Load file $_deployApi error. Please check your api file and try again."
5346 d_command
="${_d_api}_deploy"
5347 if ! _exists
"$d_command"; then
5348 _err
"It seems that your api file is not correct, it must have a function named: $d_command"
5352 if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
5353 _err
"Error deploy for domain:$_d"
5357 _err
"Deploy error."
5360 _info
"$(__green Success)"
5370 if [ -z "$_hooks" ]; then
5371 _usage
"Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
5375 _initpath
"$_d" "$_isEcc"
5376 if [ ! -d "$DOMAIN_PATH" ]; then
5377 _err
"The domain '$_d' is not a cert name. You must use the cert name to specify the cert to install."
5378 _err
"Can not find path:'$DOMAIN_PATH'"
5384 _savedomainconf Le_DeployHook
"$_hooks"
5386 _deploy
"$_d" "$_hooks"
5391 if [ -z "$_main_domain" ]; then
5392 _usage
"Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--cert-file cert-file-path] [--key-file key-file-path] [--ca-file ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchain-file fullchain-path]"
5400 _real_fullchain
="$6"
5403 _initpath
"$_main_domain" "$_isEcc"
5404 if [ ! -d "$DOMAIN_PATH" ]; then
5405 _err
"The domain '$_main_domain' is not a cert name. You must use the cert name to specify the cert to install."
5406 _err
"Can not find path:'$DOMAIN_PATH'"
5410 _savedomainconf
"Le_RealCertPath" "$_real_cert"
5411 _savedomainconf
"Le_RealCACertPath" "$_real_ca"
5412 _savedomainconf
"Le_RealKeyPath" "$_real_key"
5413 _savedomainconf
"Le_ReloadCmd" "$_reload_cmd" "base64"
5414 _savedomainconf
"Le_RealFullChainPath" "$_real_fullchain"
5416 _installcert
"$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
5419 #domain cert key ca fullchain reloadcmd backup-prefix
5425 _real_fullchain
="$5"
5429 if [ "$_real_cert" = "$NO_VALUE" ]; then
5432 if [ "$_real_key" = "$NO_VALUE" ]; then
5435 if [ "$_real_ca" = "$NO_VALUE" ]; then
5438 if [ "$_reload_cmd" = "$NO_VALUE" ]; then
5441 if [ "$_real_fullchain" = "$NO_VALUE" ]; then
5445 _backup_path
="$DOMAIN_BACKUP_PATH/$_backup_prefix"
5446 mkdir
-p "$_backup_path"
5448 if [ "$_real_cert" ]; then
5449 _info
"Installing cert to:$_real_cert"
5450 if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then
5451 cp "$_real_cert" "$_backup_path/cert.bak"
5453 cat "$CERT_PATH" >"$_real_cert" ||
return 1
5456 if [ "$_real_ca" ]; then
5457 _info
"Installing CA to:$_real_ca"
5458 if [ "$_real_ca" = "$_real_cert" ]; then
5459 echo "" >>"$_real_ca"
5460 cat "$CA_CERT_PATH" >>"$_real_ca" ||
return 1
5462 if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then
5463 cp "$_real_ca" "$_backup_path/ca.bak"
5465 cat "$CA_CERT_PATH" >"$_real_ca" ||
return 1
5469 if [ "$_real_key" ]; then
5470 _info
"Installing key to:$_real_key"
5471 if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then
5472 cp "$_real_key" "$_backup_path/key.bak"
5474 if [ -f "$_real_key" ]; then
5475 cat "$CERT_KEY_PATH" >"$_real_key" ||
return 1
5477 cat "$CERT_KEY_PATH" >"$_real_key" ||
return 1
5478 chmod 600 "$_real_key"
5482 if [ "$_real_fullchain" ]; then
5483 _info
"Installing full chain to:$_real_fullchain"
5484 if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then
5485 cp "$_real_fullchain" "$_backup_path/fullchain.bak"
5487 cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" ||
return 1
5490 if [ "$_reload_cmd" ]; then
5491 _info
"Run reload cmd: $_reload_cmd"
5494 export CERT_KEY_PATH
5496 export CERT_FULLCHAIN_PATH
5497 export Le_Domain
="$_main_domain"
5498 cd "$DOMAIN_PATH" && eval "$_reload_cmd"
5500 _info
"$(__green "Reload success
")"
5502 _err
"Reload error for :$Le_Domain"
5510 prompt
="Enter Password:"
5511 while IFS
= read -p "$prompt" -r -s -n 1 char
; do
5512 if [ "$char" = $
'\0' ]; then
5521 _install_win_taskscheduler
() {
5525 if ! _exists cygpath
; then
5526 _err
"cygpath not found"
5529 if ! _exists schtasks
; then
5530 _err
"schtasks.exe is not found, are you on Windows?"
5533 _winbash
="$(cygpath -w $(which bash))"
5534 _debug _winbash
"$_winbash"
5535 if [ -z "$_winbash" ]; then
5536 _err
"can not find bash path"
5540 _debug
"_myname" "$_myname"
5541 if [ -z "$_myname" ]; then
5542 _err
"can not find my user name"
5545 _debug
"_lesh" "$_lesh"
5547 _info
"To install scheduler task in your Windows account, you must input your windows password."
5548 _info
"$PROJECT_NAME doesn't save your password."
5549 _info
"Please input your Windows password for: $(__green "$_myname")"
5550 _password
="$(__read_password)"
5551 #SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'" >/dev/null
5552 echo SCHTASKS.exe
'/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "\"$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'\"" | cmd.exe
>/dev
/null
5557 _uninstall_win_taskscheduler
() {
5558 if ! _exists schtasks
; then
5559 _err
"schtasks.exe is not found, are you on Windows?"
5562 if ! echo SCHTASKS
/query
/tn
"$_WINDOWS_SCHEDULER_NAME" | cmd.exe
>/dev
/null
; then
5563 _debug
"scheduler $_WINDOWS_SCHEDULER_NAME is not found."
5565 _info
"Removing $_WINDOWS_SCHEDULER_NAME"
5566 echo SCHTASKS
/delete
/f
/tn
"$_WINDOWS_SCHEDULER_NAME" | cmd.exe
>/dev
/null
5575 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
5576 lesh
="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
5578 _err
"Can not install cronjob, $PROJECT_ENTRY not found."
5581 if [ "$_c_home" ]; then
5582 _c_entry
="--config-home \"$_c_home\" "
5585 random_minute
=$
(_math
$_t % 60)
5587 if ! _exists
"$_CRONTAB" && _exists
"fcrontab"; then
5591 if ! _exists
"$_CRONTAB"; then
5592 if _exists cygpath
&& _exists schtasks.exe
; then
5593 _info
"It seems you are on Windows, let's install Windows scheduler task."
5594 if _install_win_taskscheduler
"$lesh" "$_c_entry" "$random_minute"; then
5595 _info
"Install Windows scheduler task success."
5598 _err
"Install Windows scheduler task failed."
5602 _err
"crontab/fcrontab doesn't exist, so, we can not install cron jobs."
5603 _err
"All your certs will not be renewed automatically."
5604 _err
"You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
5607 _info
"Installing cron job"
5608 if ! $_CRONTAB -l |
grep "$PROJECT_ENTRY --cron"; then
5609 if _exists uname
&& uname
-a |
grep SunOS
>/dev
/null
; then
5612 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
5617 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
5621 if [ "$?" != "0" ]; then
5622 _err
"Install cron job failed. You need to manually renew your certs."
5623 _err
"Or you can add cronjob by yourself:"
5624 _err
"$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
5629 uninstallcronjob
() {
5631 if ! _exists
"$_CRONTAB" && _exists
"fcrontab"; then
5635 if ! _exists
"$_CRONTAB"; then
5636 if _exists cygpath
&& _exists schtasks.exe
; then
5637 _info
"It seems you are on Windows, let's uninstall Windows scheduler task."
5638 if _uninstall_win_taskscheduler
; then
5639 _info
"Uninstall Windows scheduler task success."
5642 _err
"Uninstall Windows scheduler task failed."
5648 _info
"Removing cron job"
5649 cr
="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
5651 if _exists uname
&& uname
-a |
grep solaris
>/dev
/null
; then
5652 $_CRONTAB -l |
sed "/$PROJECT_ENTRY --cron/d" |
$_CRONTAB --
5654 $_CRONTAB -l |
sed "/$PROJECT_ENTRY --cron/d" |
$_CRONTAB -
5656 LE_WORKING_DIR
="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
5657 _info LE_WORKING_DIR "$LE_WORKING_DIR"
5658 if _contains "$cr" "--config-home"; then
5659 LE_CONFIG_HOME="$(echo "$cr" | cut -d ' ' -f 11 | tr -d '"')"
5660 _debug LE_CONFIG_HOME
"$LE_CONFIG_HOME"
5667 #domain isECC revokeReason
5670 if [ -z "$Le_Domain" ]; then
5671 _usage
"Usage: $PROJECT_ENTRY --revoke -d domain.com [--ecc]"
5677 if [ -z "$_reason" ]; then
5680 _initpath
"$Le_Domain" "$_isEcc"
5681 if [ ! -f "$DOMAIN_CONF" ]; then
5682 _err
"$Le_Domain is not a issued domain, skip."
5686 if [ ! -f "$CERT_PATH" ]; then
5687 _err
"Cert for $Le_Domain $CERT_PATH is not found, skip."
5691 cert
="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
5693 if [ -z "$cert" ]; then
5694 _err
"Cert for $Le_Domain is empty found, skip."
5700 if [ "$ACME_VERSION" = "2" ]; then
5701 data
="{\"certificate\": \"$cert\",\"reason\":$_reason}"
5703 data
="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
5705 uri
="${ACME_REVOKE_CERT}"
5707 if [ -f "$CERT_KEY_PATH" ]; then
5708 _info
"Try domain key first."
5709 if _send_signed_request
"$uri" "$data" "" "$CERT_KEY_PATH"; then
5710 if [ -z "$response" ]; then
5711 _info
"Revoke success."
5715 _err
"Revoke error by domain key."
5720 _info
"Domain key file doesn't exist."
5723 _info
"Try account key."
5725 if _send_signed_request
"$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then
5726 if [ -z "$response" ]; then
5727 _info
"Revoke success."
5731 _err
"Revoke error."
5741 if [ -z "$Le_Domain" ]; then
5742 _usage
"Usage: $PROJECT_ENTRY --remove -d domain.com [--ecc]"
5748 _initpath
"$Le_Domain" "$_isEcc"
5749 _removed_conf
="$DOMAIN_CONF.removed"
5750 if [ ! -f "$DOMAIN_CONF" ]; then
5751 if [ -f "$_removed_conf" ]; then
5752 _err
"$Le_Domain is already removed, You can remove the folder by yourself: $DOMAIN_PATH"
5754 _err
"$Le_Domain is not a issued domain, skip."
5759 if mv "$DOMAIN_CONF" "$_removed_conf"; then
5760 _info
"$Le_Domain is removed, the key and cert files are in $(__green $DOMAIN_PATH)"
5761 _info
"You can remove them by yourself."
5764 _err
"Remove $Le_Domain failed."
5775 if [ "$ACME_VERSION" = "2" ]; then
5776 _identifiers
="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
5777 if ! _send_signed_request
"$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
5778 _err
"Can not get domain new order."
5781 _authorizations_seg
="$(echo "$response" | _egrep_o '"authorizations
" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
5782 _debug2 _authorizations_seg "$_authorizations_seg"
5783 if [ -z "$_authorizations_seg" ]; then
5784 _err "_authorizations_seg not found."
5786 _on_issue_err "$_post_hook"
5790 authzUri="$_authorizations_seg"
5791 _debug2 "authzUri" "$authzUri"
5792 if ! _send_signed_request "$authzUri"; then
5793 _err "get to authz error."
5794 _err "_authorizations_seg" "$_authorizations_seg"
5795 _err "authzUri" "$authzUri"
5797 _on_issue_err "$_post_hook"
5801 response="$(echo "$response" | _normalizeJson)"
5802 _debug2 response "$response"
5805 if ! __get_domain_new_authz "$_d_domain"; then
5806 _err "Can not get domain new authz token."
5810 authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
5811 _debug "authzUri" "$authzUri"
5812 if [ "$code" ] && [ ! "$code" = '201' ]; then
5813 _err "new-authz error: $response"
5819 entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
5820 if [ -z "$entries" ]; then
5821 _info "No valid entries found."
5822 if [ -z "$thumbprint" ]; then
5823 thumbprint="$(__calc_account_thumbprint)"
5825 _debug "Trigger validation."
5827 entry="$(echo "$response" | _egrep_o '[^\
{]*"type":"'$vtype'"[^\
}]*')"
5828 _debug entry "$entry"
5829 if [ -z "$entry" ]; then
5830 _err "Error, can not get domain token $d"
5833 token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
5834 _debug token
"$token"
5836 uri
="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^
\"]*" | cut -d : -f 2,3 | tr -d '"')"
5839 keyauthorization="$token.$thumbprint"
5840 _debug keyauthorization "$keyauthorization"
5841 __trigger_validation "$uri" "$keyauthorization"
5846 _d_max_retry=$(echo "$entries" | wc -l)
5847 while [ "$_d_i" -lt "$_d_max_retry" ]; do
5848 _info "Deactivate: $_d_domain"
5849 _d_i="$(_math $_d_i + 1)"
5850 entry="$(echo "$entries" | sed -n "${_d_i}p")"
5851 _debug entry "$entry"
5853 if [ -z "$entry" ]; then
5854 _info "No more valid entry found."
5858 _vtype="$(echo "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')"
5859 _debug _vtype "$_vtype"
5860 _info "Found $_vtype"
5862 uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
5865 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
5866 _info
"Skip $_vtype"
5870 _info
"Deactivate: $_vtype"
5872 if [ "$ACME_VERSION" = "2" ]; then
5873 _djson
="{\"status\":\"deactivated\"}"
5875 _djson
="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
5878 if _send_signed_request
"$authzUri" "$_djson" && _contains
"$response" '"deactivated"'; then
5879 _info
"Deactivate: $_vtype success."
5881 _err
"Can not deactivate $_vtype."
5887 if [ "$_d_i" -eq "$_d_max_retry" ]; then
5888 _info
"Deactivated success!"
5890 _err
"Deactivate failed."
5900 _debug _d_domain_list
"$_d_domain_list"
5901 if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then
5902 _usage
"Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
5905 for _d_dm
in $
(echo "$_d_domain_list" |
tr ',' ' '); do
5906 if [ -z "$_d_dm" ] ||
[ "$_d_dm" = "$NO_VALUE" ]; then
5909 if ! _deactivate
"$_d_dm" "$_d_type"; then
5915 # Detect profile file if not specified as environment variable
5917 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
5923 SHELLTYPE
="$(basename "/$SHELL")"
5925 if [ "$SHELLTYPE" = "bash" ]; then
5926 if [ -f "$HOME/.bashrc" ]; then
5927 DETECTED_PROFILE
="$HOME/.bashrc"
5928 elif [ -f "$HOME/.bash_profile" ]; then
5929 DETECTED_PROFILE
="$HOME/.bash_profile"
5931 elif [ "$SHELLTYPE" = "zsh" ]; then
5932 DETECTED_PROFILE
="$HOME/.zshrc"
5935 if [ -z "$DETECTED_PROFILE" ]; then
5936 if [ -f "$HOME/.profile" ]; then
5937 DETECTED_PROFILE
="$HOME/.profile"
5938 elif [ -f "$HOME/.bashrc" ]; then
5939 DETECTED_PROFILE
="$HOME/.bashrc"
5940 elif [ -f "$HOME/.bash_profile" ]; then
5941 DETECTED_PROFILE
="$HOME/.bash_profile"
5942 elif [ -f "$HOME/.zshrc" ]; then
5943 DETECTED_PROFILE
="$HOME/.zshrc"
5947 echo "$DETECTED_PROFILE"
5952 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
5955 #LOG_FILE=\"$DEFAULT_LOG_FILE\"
5962 " >"$ACCOUNT_CONF_PATH"
5970 if ! _exists
"curl" && ! _exists
"wget"; then
5971 _err
"Please install curl or wget first, we need to access http resources."
5975 if [ -z "$_nocron" ]; then
5976 if ! _exists
"crontab" && ! _exists
"fcrontab"; then
5977 if _exists cygpath
&& _exists schtasks.exe
; then
5978 _info
"It seems you are on Windows, we will install Windows scheduler task."
5980 _err
"It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
5981 _err
"We need to set cron job to renew the certs automatically."
5982 _err
"Otherwise, your certs will not be able to be renewed automatically."
5983 if [ -z "$FORCE" ]; then
5984 _err
"Please add '--force' and try install again to go without crontab."
5985 _err
"./$PROJECT_ENTRY --install --force"
5992 if ! _exists
"${ACME_OPENSSL_BIN:-openssl}"; then
5993 _err
"Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
5994 _err
"We need openssl to generate keys."
5998 if ! _exists
"socat"; then
5999 _err
"It is recommended to install socat first."
6000 _err
"We use socat for standalone server if you use standalone mode."
6001 _err
"If you don't use standalone mode, just ignore this warning."
6010 if [ -z "$_shebang" ]; then
6011 _usage
"Usage: file shebang"
6014 cp "$_file" "$_file.tmp"
6015 echo "$_shebang" >"$_file"
6016 sed -n 2,99999p
"$_file.tmp" >>"$_file"
6025 _envfile
="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
6026 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ]; then
6027 echo "$(cat "$_envfile")" |
sed "s|^LE_WORKING_DIR.*$||" >"$_envfile"
6028 echo "$(cat "$_envfile")" |
sed "s|^alias le.*$||" >"$_envfile"
6029 echo "$(cat "$_envfile")" |
sed "s|^alias le.sh.*$||" >"$_envfile"
6032 if [ "$_c_home" ]; then
6033 _c_entry
=" --config-home '$_c_home'"
6036 _setopt
"$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
6037 if [ "$_c_home" ]; then
6038 _setopt
"$_envfile" "export LE_CONFIG_HOME" "=" "\"$LE_CONFIG_HOME\""
6040 _sed_i
"/^export LE_CONFIG_HOME/d" "$_envfile"
6042 _setopt
"$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6044 _profile
="$(_detect_profile)"
6045 if [ "$_profile" ]; then
6046 _debug
"Found profile: $_profile"
6047 _info
"Installing alias to '$_profile'"
6048 _setopt
"$_profile" ". \"$_envfile\""
6049 _info
"OK, Close and reopen your terminal to start using $PROJECT_NAME"
6051 _info
"No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
6055 _cshfile
="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
6056 _csh_profile
="$HOME/.cshrc"
6057 if [ -f "$_csh_profile" ]; then
6058 _info
"Installing alias to '$_csh_profile'"
6059 _setopt
"$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
6060 if [ "$_c_home" ]; then
6061 _setopt
"$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
6063 _sed_i
"/^setenv LE_CONFIG_HOME/d" "$_cshfile"
6065 _setopt
"$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6066 _setopt
"$_csh_profile" "source \"$_cshfile\""
6070 _tcsh_profile
="$HOME/.tcshrc"
6071 if [ -f "$_tcsh_profile" ]; then
6072 _info
"Installing alias to '$_tcsh_profile'"
6073 _setopt
"$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
6074 if [ "$_c_home" ]; then
6075 _setopt
"$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
6077 _setopt
"$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6078 _setopt
"$_tcsh_profile" "source \"$_cshfile\""
6083 # nocron confighome noprofile
6086 if [ -z "$LE_WORKING_DIR" ]; then
6087 LE_WORKING_DIR
="$DEFAULT_INSTALL_HOME"
6093 if ! _initpath
; then
6094 _err
"Install failed."
6097 if [ "$_nocron" ]; then
6098 _debug
"Skip install cron job"
6101 if [ "$_ACME_IN_CRON" != "1" ]; then
6102 if ! _precheck
"$_nocron"; then
6103 _err
"Pre-check failed, can not install."
6108 if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
6109 _info
"Using config home: $LE_CONFIG_HOME"
6110 _c_home
="$LE_CONFIG_HOME"
6114 if [ -d "$HOME/.le" ]; then
6115 for envfile
in "le.env" "le.sh.env"; do
6116 if [ -f "$HOME/.le/$envfile" ]; then
6117 if grep "le.sh" "$HOME/.le/$envfile" >/dev
/null
; then
6119 _info
"You are upgrading from le.sh"
6120 _info
"Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
6121 mv "$HOME/.le" "$LE_WORKING_DIR"
6122 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
6129 _info
"Installing to $LE_WORKING_DIR"
6131 if [ ! -d "$LE_WORKING_DIR" ]; then
6132 if ! mkdir
-p "$LE_WORKING_DIR"; then
6133 _err
"Can not create working dir: $LE_WORKING_DIR"
6137 chmod 700 "$LE_WORKING_DIR"
6140 if [ ! -d "$LE_CONFIG_HOME" ]; then
6141 if ! mkdir
-p "$LE_CONFIG_HOME"; then
6142 _err
"Can not create config dir: $LE_CONFIG_HOME"
6146 chmod 700 "$LE_CONFIG_HOME"
6149 cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x
"$LE_WORKING_DIR/$PROJECT_ENTRY"
6151 if [ "$?" != "0" ]; then
6152 _err
"Install failed, can not copy $PROJECT_ENTRY"
6156 _info
"Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
6158 if [ "$_ACME_IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
6159 _installalias
"$_c_home"
6162 for subf
in $_SUB_FOLDERS; do
6163 if [ -d "$subf" ]; then
6164 mkdir
-p "$LE_WORKING_DIR/$subf"
6165 cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
6169 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
6173 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ]; then
6174 _setopt
"$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
6177 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]; then
6178 _saveaccountconf
"CERT_HOME" "$CERT_HOME"
6181 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]; then
6182 _saveaccountconf
"ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
6185 if [ -z "$_nocron" ]; then
6186 installcronjob
"$_c_home"
6189 if [ -z "$NO_DETECT_SH" ]; then
6191 if _exists bash
; then
6192 _bash_path
="$(bash -c "command -v bash
2>/dev
/null
")"
6193 if [ -z "$_bash_path" ]; then
6194 _bash_path
="$(bash -c 'echo $SHELL')"
6197 if [ "$_bash_path" ]; then
6198 _info
"Good, bash is found, so change the shebang to use bash as preferred."
6199 _shebang
='#!'"$_bash_path"
6200 _setShebang
"$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
6201 for subf
in $_SUB_FOLDERS; do
6202 if [ -d "$LE_WORKING_DIR/$subf" ]; then
6203 for _apifile
in "$LE_WORKING_DIR/$subf/"*.sh
; do
6204 _setShebang
"$_apifile" "$_shebang"
6217 if [ -z "$_nocron" ]; then
6224 rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
6225 _info
"The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\", you can remove them by yourself."
6232 _profile
="$(_detect_profile)"
6233 if [ "$_profile" ]; then
6234 _info
"Uninstalling alias from: '$_profile'"
6235 text
="$(cat "$_profile")"
6236 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" >"$_profile"
6239 _csh_profile
="$HOME/.cshrc"
6240 if [ -f "$_csh_profile" ]; then
6241 _info
"Uninstalling alias from: '$_csh_profile'"
6242 text
="$(cat "$_csh_profile")"
6243 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_csh_profile"
6246 _tcsh_profile
="$HOME/.tcshrc"
6247 if [ -f "$_tcsh_profile" ]; then
6248 _info
"Uninstalling alias from: '$_csh_profile'"
6249 text
="$(cat "$_tcsh_profile")"
6250 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_tcsh_profile"
6256 export _ACME_IN_CRON
=1
6258 _info
"$(__green "===Starting cron
===")"
6259 if [ "$AUTO_UPGRADE" = "1" ]; then
6260 export LE_WORKING_DIR
6263 _err
"Cron:Upgrade failed!"
6267 .
"$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev
/null
6273 _info
"Auto upgraded to: $VER"
6278 _info
"$(__green "===End cron
===")"
6287 # subject content hooks code
6294 if [ "$NOTIFY_LEVEL" = "$NOTIFY_LEVEL_DISABLE" ]; then
6295 _debug
"The NOTIFY_LEVEL is $NOTIFY_LEVEL, disabled, just return."
6299 if [ -z "$_nhooks" ]; then
6300 _debug
"The NOTIFY_HOOK is empty, just return."
6305 for _n_hook
in $
(echo "$_nhooks" |
tr ',' " "); do
6306 _n_hook_file
="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")"
6307 _info
"Sending via: $_n_hook"
6308 _debug
"Found $_n_hook_file for $_n_hook"
6309 if [ -z "$_n_hook_file" ]; then
6310 _err
"Can not find the hook file for $_n_hook"
6314 if ! .
"$_n_hook_file"; then
6315 _err
"Load file $_n_hook_file error. Please check your api file and try again."
6319 d_command
="${_n_hook}_send"
6320 if ! _exists
"$d_command"; then
6321 _err
"It seems that your api file is not correct, it must have a function named: $d_command"
6325 if ! $d_command "$_nsubject" "$_ncontent" "$_nerror"; then
6326 _err
"Error send message by $d_command"
6332 _err
"Set $_n_hook_file error."
6335 _info
"$_n_hook $(__green Success)"
6343 _set_notify_hook
() {
6346 _test_subject
="Hello, this is a notification from $PROJECT_NAME"
6347 _test_content
="If you receive this message, your notification works."
6349 _send_notify
"$_test_subject" "$_test_content" "$_nhooks" 0
6353 #[hook] [level] [mode]
6361 if [ -z "$_nhook$_nlevel$_nmode" ]; then
6362 _usage
"Usage: $PROJECT_ENTRY --set-notify [--notify-hook mailgun] [--notify-level $NOTIFY_LEVEL_DEFAULT] [--notify-mode $NOTIFY_MODE_DEFAULT]"
6363 _usage
"$_NOTIFY_WIKI"
6367 if [ "$_nlevel" ]; then
6368 _info
"Set notify level to: $_nlevel"
6369 export "NOTIFY_LEVEL=$_nlevel"
6370 _saveaccountconf
"NOTIFY_LEVEL" "$NOTIFY_LEVEL"
6373 if [ "$_nmode" ]; then
6374 _info
"Set notify mode to: $_nmode"
6375 export "NOTIFY_MODE=$_nmode"
6376 _saveaccountconf
"NOTIFY_MODE" "$NOTIFY_MODE"
6379 if [ "$_nhook" ]; then
6380 _info
"Set notify hook to: $_nhook"
6381 if [ "$_nhook" = "$NO_VALUE" ]; then
6382 _info
"Clear notify hook"
6383 _clearaccountconf
"NOTIFY_HOOK"
6385 if _set_notify_hook
"$_nhook"; then
6386 export NOTIFY_HOOK
="$_nhook"
6387 _saveaccountconf
"NOTIFY_HOOK" "$NOTIFY_HOOK"
6390 _err
"Can not set notify hook to: $_nhook"
6401 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
6403 --help, -h Show this help message.
6404 --version, -v Show version info.
6405 --install Install $PROJECT_NAME to your system.
6406 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
6407 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
6408 --issue Issue a cert.
6409 --signcsr Issue a cert from an existing csr.
6410 --deploy Deploy the cert to your server.
6411 --install-cert Install the issued cert to apache/nginx or any other server.
6412 --renew, -r Renew a cert.
6413 --renew-all Renew all the certs.
6414 --revoke Revoke a cert.
6415 --remove Remove the cert from list of certs known to $PROJECT_NAME.
6416 --list List all the certs.
6417 --showcsr Show the content of a csr.
6418 --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
6419 --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
6420 --cron Run cron job to renew all the certs.
6421 --toPkcs Export the certificate and key to a pfx file.
6422 --toPkcs8 Convert to pkcs8 format.
6423 --update-account Update account info.
6424 --register-account Register account key.
6425 --deactivate-account Deactivate the account.
6426 --create-account-key Create an account private key, professional use.
6427 --create-domain-key Create an domain private key, professional use.
6428 --createCSR, -ccsr Create CSR , professional use.
6429 --deactivate Deactivate the domain authz, professional use.
6430 --set-notify Set the cron notification hook, level or mode.
6431 --set-default-ca Used with '--server' , to set the default CA to use to use.
6435 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
6436 --challenge-alias domain.tld The challenge domain alias for DNS alias mode.
6437 See: $_DNS_ALIAS_WIKI
6439 --domain-alias domain.tld The domain alias for DNS alias mode.
6440 See: $_DNS_ALIAS_WIKI
6442 --preferred-chain CHAIN If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
6443 If no match, the default offered chain will be used. (default: empty)
6444 See: $_PREFERRED_CHAIN_WIKI
6446 --force, -f Used to force to install or force to renew a cert immediately.
6447 --staging, --test Use staging server, just for test.
6448 --debug Output debug info.
6449 --output-insecure Output all the sensitive messages.
6450 By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
6452 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
6453 --standalone Use standalone mode.
6454 --alpn Use standalone alpn mode.
6455 --stateless Use stateless mode.
6456 See: $_STATELESS_WIKI
6458 --apache Use apache mode.
6459 --dns [dns_hook] Use dns mode or dns api.
6462 --dnssleep 300 The time in seconds to wait for all the txt records to propagate in dns api mode.
6463 It's not necessary to use this by default, $PROJECT_NAME polls dns status by DOH automatically.
6465 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
6466 --accountkeylength, -ak [2048] Specifies the account key length: 2048, 3072, 4096
6467 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
6468 --log-level 1|2 Specifies the log level, default is 1.
6469 --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
6471 --eab-kid EAB_KID Key Identifier for External Account Binding.
6472 --eab-hmac-key EAB_HMAC_KEY HMAC key for External Account Binding.
6475 These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
6477 --cert-file After issue/renew, the cert will be copied to this path.
6478 --key-file After issue/renew, the key will be copied to this path.
6479 --ca-file After issue/renew, the intermediate cert will be copied to this path.
6480 --fullchain-file After issue/renew, the fullchain cert will be copied to this path.
6482 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
6484 --server SERVER ACME Directory Resource URI. (default: $DEFAULT_CA)
6487 --accountconf Specifies a customized account config file.
6488 --home Specifies the home dir for $PROJECT_NAME.
6489 --cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
6490 --config-home Specifies the home dir to save all the configurations.
6491 --useragent Specifies the user agent string. it will be saved for future use too.
6492 --accountemail, -m Specifies the account email, only valid for the '--install' and '--update-account' command.
6493 --accountkey Specifies the account key path, only valid for the '--install' command.
6494 --days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
6495 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
6496 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
6497 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
6498 --listraw Only used for '--list' command, list the certs in raw format.
6499 --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal.
6500 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
6501 --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate.
6502 --ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl.
6503 --nocron Only valid for '--install' command, which means: do not install the default cron job.
6504 In this case, the certs will not be renewed automatically.
6506 --noprofile Only valid for '--install' command, which means: do not install aliases to user profile.
6507 --no-color Do not output color text.
6508 --force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
6509 --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
6510 --csr Specifies the input csr.
6511 --pre-hook Command to be run before obtaining any certificates.
6512 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed.
6513 --renew-hook Command to be run once for each successfully renewed certificate.
6514 --deploy-hook The hook file to deploy cert
6515 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
6516 --always-force-new-domain-key Generate new domain key when renewal. Otherwise, the domain key is not changed by default.
6517 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
6518 --listen-v4 Force standalone/tls server to listen at ipv4.
6519 --listen-v6 Force standalone/tls server to listen at ipv6.
6520 --openssl-bin Specifies a custom openssl bin location.
6521 --use-wget Force to use wget, if you have both curl and wget installed.
6522 --yes-I-know-dns-manual-mode-enough-go-ahead-please Force to use dns manual mode.
6523 See: $_DNS_MANUAL_WIKI
6525 --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to.
6527 --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT.
6528 0: disabled, no notification will be sent.
6529 1: send notifications only when there is an error.
6530 2: send notifications when a cert is successfully renewed, or there is an error.
6531 3: send notifications when a cert is skipped, renewed, or error.
6533 --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT.
6534 0: Bulk mode. Send all the domain's notifications in one message(mail).
6535 1: Cert mode. Send a message for every single cert.
6537 --notify-hook [hookname] Set the notify hook
6538 --revoke-reason [0-10] The reason for '--revoke' command.
6547 _info
"Installing from online archive."
6550 if [ ! "$BRANCH" ]; then
6554 target
="$PROJECT/archive/$BRANCH.tar.gz"
6555 _info
"Downloading $target"
6556 localname
="$BRANCH.tar.gz"
6557 if ! _get
"$target" >$localname; then
6558 _err
"Download error."
6562 _info
"Extracting $localname"
6563 if ! (tar xzf
$localname || gtar xzf
$localname); then
6564 _err
"Extraction error."
6568 cd "$PROJECT_NAME-$BRANCH"
6569 chmod +x
$PROJECT_ENTRY
6570 if .
/$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
6571 _info
"Install success!"
6573 _saveaccountconf
"UPGRADE_HASH" "$(_getUpgradeHash)"
6578 rm -rf "$PROJECT_NAME-$BRANCH"
6586 _hash_url
="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path"
6587 _get
$_hash_url |
tr -d "\r\n" |
tr '{},' '\n' |
grep '"sha":' | cut
-d '"' -f 4
6592 if [ -z "$_b" ]; then
6595 _hash
=$
(_getRepoHash
"heads/$_b")
6596 if [ -z "$_hash" ]; then _hash
=$
(_getRepoHash
"tags/$_b"); fi
6603 [ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH
")" ] && _info
"Already uptodate!" && exit 0
6604 export LE_WORKING_DIR
6605 cd "$LE_WORKING_DIR"
6606 _installOnline
"nocron" "noprofile"
6608 _info
"Upgrade success!"
6611 _err
"Upgrade failed!"
6616 _processAccountConf
() {
6617 if [ "$_useragent" ]; then
6618 _saveaccountconf
"USER_AGENT" "$_useragent"
6619 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ]; then
6620 _saveaccountconf
"USER_AGENT" "$USER_AGENT"
6623 if [ "$_openssl_bin" ]; then
6624 _saveaccountconf
"ACME_OPENSSL_BIN" "$_openssl_bin"
6625 elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
6626 _saveaccountconf
"ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
6629 if [ "$_auto_upgrade" ]; then
6630 _saveaccountconf
"AUTO_UPGRADE" "$_auto_upgrade"
6631 elif [ "$AUTO_UPGRADE" ]; then
6632 _saveaccountconf
"AUTO_UPGRADE" "$AUTO_UPGRADE"
6635 if [ "$_use_wget" ]; then
6636 _saveaccountconf
"ACME_USE_WGET" "$_use_wget"
6637 elif [ "$ACME_USE_WGET" ]; then
6638 _saveaccountconf
"ACME_USE_WGET" "$ACME_USE_WGET"
6644 if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
6645 if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
6646 #it's root using sudo, no matter it's using sudo or not, just fine
6649 if [ -n "$SUDO_COMMAND" ]; then
6650 #it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
6651 _endswith
"$SUDO_COMMAND" /bin
/su ||
grep "^$SUDO_COMMAND\$" /etc
/shells
>/dev
/null
2>&1
6663 _server_lower
="$(echo "$_server" | _lower_case)"
6665 for snames
in $CA_NAMES; do
6666 snames
="$(echo "$snames" | _lower_case)"
6667 _sindex
="$(_math $_sindex + 1)"
6668 _debug2
"_selectServer try snames" "$snames"
6669 for sname
in $
(echo "$snames" |
tr ',' ' '); do
6670 if [ "$_server_lower" = "$sname" ]; then
6671 _debug2
"_selectServer match $sname"
6672 _serverdir
="$(_getfield "$CA_SERVERS" $_sindex)"
6673 _debug
"Selected server: $_serverdir"
6674 ACME_DIRECTORY
="$_serverdir"
6675 export ACME_DIRECTORY
6680 ACME_DIRECTORY
="$_server"
6681 export ACME_DIRECTORY
6687 if [ -z "$caurl" ]; then
6690 caurl_lower
="$(echo $caurl | _lower_case)"
6692 for surl
in $
(echo "$CA_SERVERS" | _lower_case |
tr , ' '); do
6693 _sindex
="$(_math $_sindex + 1)"
6694 if [ "$caurl_lower" = "$surl" ]; then
6696 for snames
in $CA_NAMES; do
6697 _nindex
="$(_math $_nindex + 1)"
6698 if [ $_nindex -ge $_sindex ]; then
6699 _getfield
"$snames" 1
6708 #set default ca to $ACME_DIRECTORY
6710 if [ -z "$ACME_DIRECTORY" ]; then
6711 _err
"Please give a --server parameter."
6714 _saveaccountconf
"DEFAULT_ACME_SERVER" "$ACME_DIRECTORY"
6715 _info
"Changed default CA to: $(__green "$ACME_DIRECTORY")"
6721 _altdomains
="$NO_VALUE"
6725 _accountkeylength
=""
6742 _stopRenewOnError
=""
6772 while [ ${#} -gt 0 ]; do
6804 --installcert |
-i |
--install-cert)
6810 --renewAll |
--renewall |
--renew-all)
6822 --installcronjob |
--install-cronjob)
6823 _CMD
="installcronjob"
6825 --uninstallcronjob |
--uninstall-cronjob)
6826 _CMD
="uninstallcronjob"
6837 --createAccountKey |
--createaccountkey |
-cak |
--create-account-key)
6838 _CMD
="createAccountKey"
6840 --createDomainKey |
--createdomainkey |
-cdk |
--create-domain-key)
6841 _CMD
="createDomainKey"
6843 --createCSR |
--createcsr |
-ccr)
6849 --updateaccount |
--update-account)
6850 _CMD
="updateaccount"
6852 --registeraccount |
--register-account)
6853 _CMD
="registeraccount"
6855 --deactivate-account)
6856 _CMD
="deactivateaccount"
6867 if [ "$_dvalue" ]; then
6868 if _startswith
"$_dvalue" "-"; then
6869 _err
"'$_dvalue' is not a valid domain for parameter '$1'"
6872 if _is_idn
"$_dvalue" && ! _exists idn
; then
6873 _err
"It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first."
6877 if _startswith
"$_dvalue" "*."; then
6878 _debug
"Wildcard domain"
6879 export ACME_VERSION
=2
6881 if [ -z "$_domain" ]; then
6884 if [ "$_altdomains" = "$NO_VALUE" ]; then
6885 _altdomains
="$_dvalue"
6887 _altdomains
="$_altdomains,$_dvalue"
6903 _selectServer
"$_server"
6907 if [ -z "$2" ] || _startswith
"$2" "-"; then
6908 DEBUG
="$DEBUG_LEVEL_DEFAULT"
6915 export OUTPUT_INSECURE
=1
6919 if [ -z "$_webroot" ]; then
6922 _webroot
="$_webroot,$wvalue"
6928 _challenge_alias
="$_challenge_alias$cvalue,"
6932 cvalue
="$DNS_ALIAS_PREFIX$2"
6933 _challenge_alias
="$_challenge_alias$cvalue,"
6938 if [ -z "$_webroot" ]; then
6941 _webroot
="$_webroot,$wvalue"
6946 if [ -z "$_webroot" ]; then
6949 _webroot
="$_webroot,$wvalue"
6953 wvalue
="$MODE_STATELESS"
6954 if [ -z "$_webroot" ]; then
6957 _webroot
="$_webroot,$wvalue"
6962 _local_address
="$_local_address$lvalue,"
6967 if [ -z "$_webroot" ]; then
6970 _webroot
="$_webroot,$wvalue"
6975 if [ "$2" ] && ! _startswith
"$2" "-"; then
6979 if [ -z "$_webroot" ]; then
6982 _webroot
="$_webroot,$wvalue"
6987 if [ "$2" ] && ! _startswith
"$2" "-"; then
6991 if [ -z "$_webroot" ]; then
6994 _webroot
="$_webroot,$wvalue"
6999 Le_DNSSleep
="$_dnssleep"
7007 --accountkeylength |
-ak)
7008 _accountkeylength
="$2"
7012 --cert-file |
--certpath)
7016 --key-file |
--keypath)
7020 --ca-file |
--capath)
7024 --fullchain-file |
--fullchainpath)
7025 _fullchain_file
="$2"
7028 --reloadcmd |
--reloadCmd)
7038 ACCOUNT_CONF_PATH
="$_accountconf"
7045 --certhome |
--cert-home)
7047 CERT_HOME
="$_certhome"
7052 LE_CONFIG_HOME
="$_confighome"
7057 USER_AGENT
="$_useragent"
7060 --accountemail |
-m)
7062 ACCOUNT_EMAIL
="$_accountemail"
7067 ACCOUNT_KEY_PATH
="$_accountkey"
7072 Le_RenewalDays
="$_days"
7077 Le_HTTPPort
="$_httpport"
7082 Le_TLSPort
="$_tlsport"
7088 --stopRenewOnError |
--stoprenewonerror |
-se)
7089 _stopRenewOnError
="1"
7096 _ca_bundle
="$(_readlink "$2")"
7097 CA_BUNDLE
="$_ca_bundle"
7112 export ACME_NO_COLOR
=1
7115 export ACME_FORCE_COLOR
=1
7137 if [ -z "$2" ] || _startswith
"$2" "-"; then
7138 _usage
"Please specify a value for '--deploy-hook'"
7141 _deploy_hook
="$_deploy_hook$2,"
7144 --ocsp-must-staple |
--ocsp)
7147 --always-force-new-domain-key)
7148 if [ -z "$2" ] || _startswith
"$2" "-"; then
7149 Le_ForceNewDomainKey
=1
7151 Le_ForceNewDomainKey
="$2"
7155 --yes-I-know-dns-manual-mode-enough-go-ahead-please)
7156 export FORCE_DNS_MANUAL
=1
7161 if _startswith
"$_logfile" '-'; then
7166 LOG_FILE
="$_logfile"
7167 if [ -z "$LOG_LEVEL" ]; then
7168 LOG_LEVEL
="$DEFAULT_LOG_LEVEL"
7173 LOG_LEVEL
="$_log_level"
7177 if ! _startswith
"$2" '-'; then
7181 if [ -z "$_syslog" ]; then
7182 _syslog
="$SYSLOG_LEVEL_DEFAULT"
7187 if [ -z "$_auto_upgrade" ] || _startswith
"$_auto_upgrade" '-'; then
7192 AUTO_UPGRADE
="$_auto_upgrade"
7196 Le_Listen_V4
="$_listen_v4"
7200 Le_Listen_V6
="$_listen_v6"
7204 ACME_OPENSSL_BIN
="$_openssl_bin"
7217 if _startswith
"$_nhook" "-"; then
7218 _err
"'$_nhook' is not a hook name for '$1'"
7221 if [ "$_notify_hook" ]; then
7222 _notify_hook
="$_notify_hook,$_nhook"
7224 _notify_hook
="$_nhook"
7230 if _startswith
"$_nlevel" "-"; then
7231 _err
"'$_nlevel' is not a integer for '$1'"
7234 _notify_level
="$_nlevel"
7239 if _startswith
"$_nmode" "-"; then
7240 _err
"'$_nmode' is not a integer for '$1'"
7243 _notify_mode
="$_nmode"
7248 if _startswith
"$_revoke_reason" "-"; then
7249 _err
"'$_revoke_reason' is not a integer for '$1'"
7263 _preferred_chain
="$2"
7267 _err
"Unknown parameter : $1"
7275 if [ "${_CMD}" != "install" ]; then
7276 if [ "$__INTERACTIVE" ] && ! _checkSudo
; then
7277 if [ -z "$FORCE" ]; then
7278 #Use "echo" here, instead of _info. it's too early
7279 echo "It seems that you are using sudo, please read this link first:"
7285 if [ "$_log" ]; then
7286 if [ -z "$_logfile" ]; then
7287 _logfile
="$DEFAULT_LOG_FILE"
7290 if [ "$_logfile" ]; then
7291 _saveaccountconf
"LOG_FILE" "$_logfile"
7292 LOG_FILE
="$_logfile"
7295 if [ "$_log_level" ]; then
7296 _saveaccountconf
"LOG_LEVEL" "$_log_level"
7297 LOG_LEVEL
="$_log_level"
7300 if [ "$_syslog" ]; then
7301 if _exists logger
; then
7302 if [ "$_syslog" = "0" ]; then
7303 _clearaccountconf
"SYS_LOG"
7305 _saveaccountconf
"SYS_LOG" "$_syslog"
7309 _err
"The 'logger' command is not found, can not enable syslog."
7310 _clearaccountconf
"SYS_LOG"
7318 _debug2 LE_WORKING_DIR
"$LE_WORKING_DIR"
7320 if [ "$DEBUG" ]; then
7322 if [ "$_server" ]; then
7323 _debug
"Using server: $_server"
7326 _debug
"Running cmd: ${_CMD}"
7328 install) install "$_nocron" "$_confighome" "$_noprofile" ;;
7329 uninstall
) uninstall
"$_nocron" ;;
7332 issue
"$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain"
7335 deploy
"$_domain" "$_deploy_hook" "$_ecc"
7338 signcsr
"$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
7341 showcsr
"$_csr" "$_domain"
7344 installcert
"$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
7347 renew
"$_domain" "$_ecc"
7350 renewAll
"$_stopRenewOnError"
7353 revoke
"$_domain" "$_ecc" "$_revoke_reason"
7356 remove
"$_domain" "$_ecc"
7359 deactivate
"$_domain,$_altdomains"
7362 registeraccount
"$_accountkeylength" "$_eab_kid" "$_eab_hmac_key"
7371 list
"$_listraw" "$_domain"
7373 installcronjob
) installcronjob
"$_confighome" ;;
7374 uninstallcronjob
) uninstallcronjob
;;
7377 toPkcs
"$_domain" "$_password" "$_ecc"
7380 toPkcs8
"$_domain" "$_ecc"
7383 createAccountKey
"$_accountkeylength"
7386 createDomainKey
"$_domain" "$_keylength"
7389 createCSR
"$_domain" "$_altdomains" "$_ecc"
7392 setnotify
"$_notify_hook" "$_notify_level" "$_notify_mode"
7398 if [ "$_CMD" ]; then
7399 _err
"Invalid command: $_CMD"
7406 if [ "$_ret" != "0" ]; then
7410 if [ "${_CMD}" = "install" ]; then
7411 if [ "$_log" ]; then
7412 if [ -z "$LOG_FILE" ]; then
7413 LOG_FILE
="$DEFAULT_LOG_FILE"
7415 _saveaccountconf
"LOG_FILE" "$LOG_FILE"
7418 if [ "$_log_level" ]; then
7419 _saveaccountconf
"LOG_LEVEL" "$_log_level"
7422 if [ "$_syslog" ]; then
7423 if _exists logger
; then
7424 if [ "$_syslog" = "0" ]; then
7425 _clearaccountconf
"SYS_LOG"
7427 _saveaccountconf
"SYS_LOG" "$_syslog"
7430 _err
"The 'logger' command is not found, can not enable syslog."
7431 _clearaccountconf
"SYS_LOG"
7441 if [ "$INSTALLONLINE" ]; then
7448 [ -z "$1" ] && showhelp
&& return
7449 if _startswith
"$1" '-'; then _process
"$@"; else "$@"; fi