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 | _json_decode)"
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 response
=$
(echo "$response" | _json_decode
)
2507 _debug2
"response" "$response"
2509 ACME_KEY_CHANGE
=$
(echo "$response" | _egrep_o
'key-change" *: *"[^"]*"' | cut
-d '"' -f 3)
2510 if [ -z "$ACME_KEY_CHANGE" ]; then
2511 ACME_KEY_CHANGE
=$
(echo "$response" | _egrep_o
'keyChange" *: *"[^"]*"' | cut
-d '"' -f 3)
2513 export ACME_KEY_CHANGE
2515 ACME_NEW_AUTHZ
=$
(echo "$response" | _egrep_o
'new-authz" *: *"[^"]*"' | cut
-d '"' -f 3)
2516 if [ -z "$ACME_NEW_AUTHZ" ]; then
2517 ACME_NEW_AUTHZ
=$
(echo "$response" | _egrep_o
'newAuthz" *: *"[^"]*"' | cut
-d '"' -f 3)
2519 export ACME_NEW_AUTHZ
2521 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'new-cert" *: *"[^"]*"' | cut
-d '"' -f 3)
2522 ACME_NEW_ORDER_RES
="new-cert"
2523 if [ -z "$ACME_NEW_ORDER" ]; then
2524 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'new-order" *: *"[^"]*"' | cut
-d '"' -f 3)
2525 ACME_NEW_ORDER_RES
="new-order"
2526 if [ -z "$ACME_NEW_ORDER" ]; then
2527 ACME_NEW_ORDER
=$
(echo "$response" | _egrep_o
'newOrder" *: *"[^"]*"' | cut
-d '"' -f 3)
2530 export ACME_NEW_ORDER
2531 export ACME_NEW_ORDER_RES
2533 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'new-reg" *: *"[^"]*"' | cut
-d '"' -f 3)
2534 ACME_NEW_ACCOUNT_RES
="new-reg"
2535 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2536 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'new-account" *: *"[^"]*"' | cut
-d '"' -f 3)
2537 ACME_NEW_ACCOUNT_RES
="new-account"
2538 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2539 ACME_NEW_ACCOUNT
=$
(echo "$response" | _egrep_o
'newAccount" *: *"[^"]*"' | cut
-d '"' -f 3)
2540 if [ "$ACME_NEW_ACCOUNT" ]; then
2541 export ACME_VERSION
=2
2545 export ACME_NEW_ACCOUNT
2546 export ACME_NEW_ACCOUNT_RES
2548 ACME_REVOKE_CERT
=$
(echo "$response" | _egrep_o
'revoke-cert" *: *"[^"]*"' | cut
-d '"' -f 3)
2549 if [ -z "$ACME_REVOKE_CERT" ]; then
2550 ACME_REVOKE_CERT
=$
(echo "$response" | _egrep_o
'revokeCert" *: *"[^"]*"' | cut
-d '"' -f 3)
2552 export ACME_REVOKE_CERT
2554 ACME_NEW_NONCE
=$
(echo "$response" | _egrep_o
'new-nonce" *: *"[^"]*"' | cut
-d '"' -f 3)
2555 if [ -z "$ACME_NEW_NONCE" ]; then
2556 ACME_NEW_NONCE
=$
(echo "$response" | _egrep_o
'newNonce" *: *"[^"]*"' | cut
-d '"' -f 3)
2558 export ACME_NEW_NONCE
2560 ACME_AGREEMENT
=$
(echo "$response" | _egrep_o
'terms-of-service" *: *"[^"]*"' | cut
-d '"' -f 3)
2561 if [ -z "$ACME_AGREEMENT" ]; then
2562 ACME_AGREEMENT
=$
(echo "$response" | _egrep_o
'termsOfService" *: *"[^"]*"' | cut
-d '"' -f 3)
2564 export ACME_AGREEMENT
2566 _debug
"ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
2567 _debug
"ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
2568 _debug
"ACME_NEW_ORDER" "$ACME_NEW_ORDER"
2569 _debug
"ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
2570 _debug
"ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
2571 _debug
"ACME_AGREEMENT" "$ACME_AGREEMENT"
2572 _debug
"ACME_NEW_NONCE" "$ACME_NEW_NONCE"
2573 _debug
"ACME_VERSION" "$ACME_VERSION"
2578 #[domain] [keylength or isEcc flag]
2585 if [ -f "$ACCOUNT_CONF_PATH" ]; then
2586 .
"$ACCOUNT_CONF_PATH"
2589 if [ "$_ACME_IN_CRON" ]; then
2590 if [ ! "$_USER_PATH_EXPORTED" ]; then
2591 _USER_PATH_EXPORTED
=1
2592 export PATH
="$USER_PATH:$PATH"
2596 if [ -z "$CA_HOME" ]; then
2597 CA_HOME
="$DEFAULT_CA_HOME"
2600 if [ -z "$ACME_DIRECTORY" ]; then
2601 if [ "$STAGE" ]; then
2602 ACME_DIRECTORY
="$DEFAULT_STAGING_CA"
2603 _info
"Using ACME_DIRECTORY: $ACME_DIRECTORY"
2605 default_acme_server
=$
(_readaccountconf
"DEFAULT_ACME_SERVER")
2606 _debug default_acme_server
"$default_acme_server"
2607 if [ "$default_acme_server" ]; then
2608 ACME_DIRECTORY
="$default_acme_server"
2610 ACME_DIRECTORY
="$DEFAULT_CA"
2615 _debug ACME_DIRECTORY
"$ACME_DIRECTORY"
2616 _ACME_SERVER_HOST
="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
2617 _debug2
"_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
2619 CA_DIR
="$CA_HOME/$_ACME_SERVER_HOST"
2621 _DEFAULT_CA_CONF
="$CA_DIR/ca.conf"
2623 if [ -z "$CA_CONF" ]; then
2624 CA_CONF
="$_DEFAULT_CA_CONF"
2626 _debug3 CA_CONF
"$CA_CONF"
2628 if [ -f "$CA_CONF" ]; then
2632 if [ -z "$ACME_DIR" ]; then
2633 ACME_DIR
="/home/.acme"
2636 if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then
2637 APACHE_CONF_BACKUP_DIR
="$LE_CONFIG_HOME"
2640 if [ -z "$USER_AGENT" ]; then
2641 USER_AGENT
="$DEFAULT_USER_AGENT"
2644 if [ -z "$HTTP_HEADER" ]; then
2645 HTTP_HEADER
="$LE_CONFIG_HOME/http.header"
2648 _OLD_ACCOUNT_KEY
="$LE_WORKING_DIR/account.key"
2649 _OLD_ACCOUNT_JSON
="$LE_WORKING_DIR/account.json"
2651 _DEFAULT_ACCOUNT_KEY_PATH
="$CA_DIR/account.key"
2652 _DEFAULT_ACCOUNT_JSON_PATH
="$CA_DIR/account.json"
2653 if [ -z "$ACCOUNT_KEY_PATH" ]; then
2654 ACCOUNT_KEY_PATH
="$_DEFAULT_ACCOUNT_KEY_PATH"
2657 if [ -z "$ACCOUNT_JSON_PATH" ]; then
2658 ACCOUNT_JSON_PATH
="$_DEFAULT_ACCOUNT_JSON_PATH"
2661 _DEFAULT_CERT_HOME
="$LE_CONFIG_HOME"
2662 if [ -z "$CERT_HOME" ]; then
2663 CERT_HOME
="$_DEFAULT_CERT_HOME"
2666 if [ -z "$ACME_OPENSSL_BIN" ] ||
[ ! -f "$ACME_OPENSSL_BIN" ] ||
[ ! -x "$ACME_OPENSSL_BIN" ]; then
2667 ACME_OPENSSL_BIN
="$DEFAULT_OPENSSL_BIN"
2670 if [ -z "$domain" ]; then
2674 if [ -z "$DOMAIN_PATH" ]; then
2675 domainhome
="$CERT_HOME/$domain"
2676 domainhomeecc
="$CERT_HOME/$domain$ECC_SUFFIX"
2678 DOMAIN_PATH
="$domainhome"
2680 if _isEccKey
"$_ilength"; then
2681 DOMAIN_PATH
="$domainhomeecc"
2683 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then
2684 _info
"The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
2687 _debug DOMAIN_PATH
"$DOMAIN_PATH"
2690 if [ -z "$DOMAIN_BACKUP_PATH" ]; then
2691 DOMAIN_BACKUP_PATH
="$DOMAIN_PATH/backup"
2694 if [ -z "$DOMAIN_CONF" ]; then
2695 DOMAIN_CONF
="$DOMAIN_PATH/$domain.conf"
2698 if [ -z "$DOMAIN_SSL_CONF" ]; then
2699 DOMAIN_SSL_CONF
="$DOMAIN_PATH/$domain.csr.conf"
2702 if [ -z "$CSR_PATH" ]; then
2703 CSR_PATH
="$DOMAIN_PATH/$domain.csr"
2705 if [ -z "$CERT_KEY_PATH" ]; then
2706 CERT_KEY_PATH
="$DOMAIN_PATH/$domain.key"
2708 if [ -z "$CERT_PATH" ]; then
2709 CERT_PATH
="$DOMAIN_PATH/$domain.cer"
2711 if [ -z "$CA_CERT_PATH" ]; then
2712 CA_CERT_PATH
="$DOMAIN_PATH/ca.cer"
2714 if [ -z "$CERT_FULLCHAIN_PATH" ]; then
2715 CERT_FULLCHAIN_PATH
="$DOMAIN_PATH/fullchain.cer"
2717 if [ -z "$CERT_PFX_PATH" ]; then
2718 CERT_PFX_PATH
="$DOMAIN_PATH/$domain.pfx"
2720 if [ -z "$CERT_PKCS8_PATH" ]; then
2721 CERT_PKCS8_PATH
="$DOMAIN_PATH/$domain.pkcs8"
2724 if [ -z "$TLS_CONF" ]; then
2725 TLS_CONF
="$DOMAIN_PATH/tls.validation.conf"
2727 if [ -z "$TLS_CERT" ]; then
2728 TLS_CERT
="$DOMAIN_PATH/tls.validation.cert"
2730 if [ -z "$TLS_KEY" ]; then
2731 TLS_KEY
="$DOMAIN_PATH/tls.validation.key"
2733 if [ -z "$TLS_CSR" ]; then
2734 TLS_CSR
="$DOMAIN_PATH/tls.validation.csr"
2740 if [ -z "$_EXEC_TEMP_ERR" ]; then
2741 _EXEC_TEMP_ERR
="$(_mktemp)"
2744 if [ "$_EXEC_TEMP_ERR" ]; then
2745 eval "$@ 2>>$_EXEC_TEMP_ERR"
2752 [ "$_EXEC_TEMP_ERR" ] && _err
"$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
2756 _APACHECTL
="apachectl"
2757 if ! _exists apachectl
; then
2758 if _exists apache2ctl
; then
2759 _APACHECTL
="apache2ctl"
2761 _err
"'apachectl not found. It seems that apache is not installed, or you are not root user.'"
2762 _err
"Please use webroot mode to try again."
2767 if ! _exec
$_APACHECTL -V >/dev
/null
; then
2772 if [ "$APACHE_HTTPD_CONF" ]; then
2773 _saveaccountconf APACHE_HTTPD_CONF
"$APACHE_HTTPD_CONF"
2774 httpdconf
="$APACHE_HTTPD_CONF"
2775 httpdconfname
="$(basename "$httpdconfname")"
2777 httpdconfname
="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"')"
2778 _debug httpdconfname "$httpdconfname"
2780 if [ -z "$httpdconfname" ]; then
2781 _err "Can not read apache config file."
2785 if _startswith "$httpdconfname" '/'; then
2786 httpdconf="$httpdconfname"
2787 httpdconfname="$(basename "$httpdconfname")"
2789 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"')"
2790 _debug httpdroot
"$httpdroot"
2791 httpdconf
="$httpdroot/$httpdconfname"
2792 httpdconfname
="$(basename "$httpdconfname")"
2795 _debug httpdconf
"$httpdconf"
2796 _debug httpdconfname
"$httpdconfname"
2797 if [ ! -f "$httpdconf" ]; then
2798 _err
"Apache Config file not found" "$httpdconf"
2805 if [ -z "$usingApache" ]; then
2809 if ! _apachePath
; then
2813 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]; then
2814 _debug
"No config file to restore."
2818 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
2819 _debug
"Restored: $httpdconf."
2820 if ! _exec
$_APACHECTL -t; then
2822 _err
"Sorry, restore apache config error, please contact me."
2825 _debug
"Restored successfully."
2826 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
2832 if ! _apachePath
; then
2836 #test the conf first
2837 _info
"Checking if there is an error in the apache config file before starting."
2839 if ! _exec
"$_APACHECTL" -t >/dev
/null
; then
2841 _err
"The apache config file has error, please fix it first, then try again."
2842 _err
"Don't worry, there is nothing changed to your system."
2849 _debug
"Backup apache config file" "$httpdconf"
2850 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then
2851 _err
"Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
2852 _err
"This might be a bug of $PROJECT_NAME , please report issue: $PROJECT"
2855 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
2856 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
2857 _info
"The backup file will be deleted on success, just forget it."
2861 apacheVer
="$($_APACHECTL -V | grep "Server version
:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2)"
2862 _debug
"apacheVer" "$apacheVer"
2863 apacheMajor
="$(echo "$apacheVer" | cut -d . -f 1)"
2864 apacheMinor
="$(echo "$apacheVer" | cut -d . -f 2)"
2866 if [ "$apacheVer" ] && [ "$apacheMajor$apacheMinor" -ge "24" ]; then
2868 Alias /.well-known/acme-challenge $ACME_DIR
2870 <Directory $ACME_DIR >
2876 Alias /.well-known/acme-challenge $ACME_DIR
2878 <Directory $ACME_DIR >
2885 _msg
="$($_APACHECTL -t 2>&1)"
2886 if [ "$?" != "0" ]; then
2887 _err
"Sorry, apache config error"
2888 if _restoreApache
; then
2889 _err
"The apache config file is restored."
2891 _err
"Sorry, The apache config file can not be restored, please report bug."
2896 if [ ! -d "$ACME_DIR" ]; then
2897 mkdir
-p "$ACME_DIR"
2898 chmod 755 "$ACME_DIR"
2901 if ! _exec
"$_APACHECTL" graceful
; then
2903 _err
"$_APACHECTL graceful error, please contact me."
2911 #find the real nginx conf file
2914 #returns the real nginx conf file
2920 FOUND_REAL_NGINX_CONF
=""
2921 FOUND_REAL_NGINX_CONF_LN
=""
2922 BACKUP_NGINX_CONF
=""
2923 _debug _croot
"$_croot"
2924 _start_f
="$(echo "$_croot" | cut -d : -f 2)"
2925 _debug _start_f
"$_start_f"
2926 if [ -z "$_start_f" ]; then
2927 _debug
"find start conf from nginx command"
2928 if [ -z "$NGINX_CONF" ]; then
2929 if ! _exists
"nginx"; then
2930 _err
"nginx command is not found."
2933 NGINX_CONF
="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^
]* " | tr -d " ")"
2934 _debug NGINX_CONF
"$NGINX_CONF"
2935 NGINX_CONF
="$(echo "$NGINX_CONF" | cut -d = -f 2)"
2936 _debug NGINX_CONF
"$NGINX_CONF"
2937 if [ -z "$NGINX_CONF" ]; then
2938 _err
"Can not find nginx conf."
2942 if [ ! -f "$NGINX_CONF" ]; then
2943 _err
"'$NGINX_CONF' doesn't exist."
2947 _debug
"Found nginx conf file:$NGINX_CONF"
2949 _start_f
="$NGINX_CONF"
2951 _debug
"Start detect nginx conf for $_d from:$_start_f"
2952 if ! _checkConf
"$_d" "$_start_f"; then
2953 _err
"Can not find conf file for domain $d"
2956 _info
"Found conf file: $FOUND_REAL_NGINX_CONF"
2958 _ln
=$FOUND_REAL_NGINX_CONF_LN
2961 _lnn
=$
(_math
$_ln + 1)
2963 _start_tag
="$(sed -n "$_lnn,${_lnn}p
" "$FOUND_REAL_NGINX_CONF")"
2964 _debug
"_start_tag" "$_start_tag"
2965 if [ "$_start_tag" = "$NGINX_START" ]; then
2966 _info
"The domain $_d is already configured, skip"
2967 FOUND_REAL_NGINX_CONF
=""
2971 mkdir
-p "$DOMAIN_BACKUP_PATH"
2972 _backup_conf
="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
2973 _debug _backup_conf
"$_backup_conf"
2974 BACKUP_NGINX_CONF
="$_backup_conf"
2975 _info
"Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
2976 if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
2977 _err
"backup error."
2978 FOUND_REAL_NGINX_CONF
=""
2982 if ! _exists
"nginx"; then
2983 _err
"nginx command is not found."
2986 _info
"Check the nginx conf before setting up."
2987 if ! _exec
"nginx -t" >/dev
/null
; then
2992 _info
"OK, Set up nginx config file"
2994 if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
2995 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
2996 _err
"write nginx conf error, but don't worry, the file is restored to the original version."
3001 location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
3002 default_type text/plain;
3003 return 200 \"\$1.$_thumbpt\";
3006 " >>"$FOUND_REAL_NGINX_CONF"
3008 if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
3009 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3010 _err
"write nginx conf error, but don't worry, the file is restored."
3013 _debug3
"Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
3014 _info
"nginx conf is done, let's check it again."
3015 if ! _exec
"nginx -t" >/dev
/null
; then
3017 _err
"It seems that nginx conf was broken, let's restore."
3018 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3022 _info
"Reload nginx"
3023 if ! _exec
"nginx -s reload" >/dev
/null
; then
3025 _err
"It seems that nginx reload error, let's restore."
3026 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
3037 _debug
"Start _checkConf from:$_c_file"
3038 if [ ! -f "$2" ] && ! echo "$2" |
grep '*$' >/dev
/null
&& echo "$2" |
grep '*' >/dev
/null
; then
3041 if [ -f "$_w_f" ] && _checkConf
"$1" "$_w_f"; then
3047 elif [ -f "$2" ]; then
3049 if _isRealNginxConf
"$1" "$2"; then
3050 _debug
"$2 is found."
3051 FOUND_REAL_NGINX_CONF
="$2"
3054 if cat "$2" |
tr "\t" " " |
grep "^ *include *.*;" >/dev
/null
; then
3055 _debug
"Try include files"
3056 for included
in $
(cat "$2" |
tr "\t" " " |
grep "^ *include *.*;" |
sed "s/include //" |
tr -d " ;"); do
3057 _debug
"check included $included"
3058 if _checkConf
"$1" "$included"; then
3065 _debug
"$2 not found."
3072 _isRealNginxConf
() {
3073 _debug
"_isRealNginxConf $1 $2"
3074 if [ -f "$2" ]; then
3075 for _fln
in $
(tr "\t" ' ' <"$2" |
grep -n "^ *server_name.* $1" | cut
-d : -f 1); do
3077 if [ "$_fln" ]; then
3078 _start
=$
(tr "\t" ' ' <"$2" | _head_n
"$_fln" |
grep -n "^ *server *" |
grep -v server_name | _tail_n
1)
3079 _debug
"_start" "$_start"
3080 _start_n
=$
(echo "$_start" | cut
-d : -f 1)
3081 _start_nn
=$
(_math
$_start_n + 1)
3082 _debug
"_start_n" "$_start_n"
3083 _debug
"_start_nn" "$_start_nn"
3085 _left
="$(sed -n "${_start_nn},99999p
" "$2")"
3086 _debug2 _left
"$_left"
3087 _end
="$(echo "$_left" | tr "\t" ' ' | grep -n "^
*server
*" | grep -v server_name | _head_n 1)"
3088 _debug
"_end" "$_end"
3089 if [ "$_end" ]; then
3090 _end_n
=$
(echo "$_end" | cut
-d : -f 1)
3091 _debug
"_end_n" "$_end_n"
3092 _seg_n
=$
(echo "$_left" |
sed -n "1,${_end_n}p")
3097 _debug
"_seg_n" "$_seg_n"
3100 for _listen_i
in $
(echo "$_seg_n" |
tr "\t" ' ' |
grep "^ *listen" |
tr -d " "); do
3101 if [ "$_listen_i" ]; then
3102 if [ "$(echo "$_listen_i" | _egrep_o "listen.
*ssl
")" ]; then
3103 _debug2
"$_listen_i is ssl"
3105 _debug2
"$_listen_i is plain text"
3112 if [ "$_skip_ssl" = "1" ]; then
3113 _debug
"ssl on, skip"
3115 FOUND_REAL_NGINX_CONF_LN
=$_fln
3116 _debug3
"found FOUND_REAL_NGINX_CONF_LN" "$FOUND_REAL_NGINX_CONF_LN"
3125 #restore all the nginx conf
3127 if [ -z "$NGINX_RESTORE_VLIST" ]; then
3128 _debug
"No need to restore nginx, skip."
3131 _debug
"_restoreNginx"
3132 _debug
"NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
3134 for ng_entry
in $
(echo "$NGINX_RESTORE_VLIST" |
tr "$dvsep" ' '); do
3135 _debug
"ng_entry" "$ng_entry"
3136 _nd
=$
(echo "$ng_entry" | cut
-d "$sep" -f 1)
3137 _ngconf
=$
(echo "$ng_entry" | cut
-d "$sep" -f 2)
3138 _ngbackupconf
=$
(echo "$ng_entry" | cut
-d "$sep" -f 3)
3139 _info
"Restoring from $_ngbackupconf to $_ngconf"
3140 cat "$_ngbackupconf" >"$_ngconf"
3143 _info
"Reload nginx"
3144 if ! _exec
"nginx -s reload" >/dev
/null
; then
3146 _err
"It seems that nginx reload error, please report bug."
3153 _stopserver
"$serverproc"
3158 if [ -z "$DEBUG" ]; then
3167 _debug
"_clearupdns"
3168 _debug
"dns_entries" "$dns_entries"
3170 if [ -z "$dns_entries" ]; then
3174 _info
"Removing DNS records."
3176 for entry
in $dns_entries; do
3177 d
=$
(_getfield
"$entry" 1)
3178 txtdomain
=$
(_getfield
"$entry" 2)
3179 aliasDomain
=$
(_getfield
"$entry" 3)
3180 _currentRoot
=$
(_getfield
"$entry" 4)
3181 txt
=$
(_getfield
"$entry" 5)
3182 d_api
=$
(_getfield
"$entry" 6)
3184 _debug
"txtdomain" "$txtdomain"
3185 _debug
"aliasDomain" "$aliasDomain"
3186 _debug
"_currentRoot" "$_currentRoot"
3188 _debug
"d_api" "$d_api"
3189 if [ "$d_api" = "$txt" ]; then
3193 if [ -z "$d_api" ]; then
3194 _info
"Not Found domain api file: $d_api"
3198 if [ "$aliasDomain" ]; then
3199 txtdomain
="$aliasDomain"
3203 if ! .
"$d_api"; then
3204 _err
"Load file $d_api error. Please check your api file and try again."
3208 rmcommand
="${_currentRoot}_rm"
3209 if ! _exists
"$rmcommand"; then
3210 _err
"It seems that your api file doesn't define $rmcommand"
3213 _info
"Removing txt: $txt for domain: $txtdomain"
3214 if ! $rmcommand "$txtdomain" "$txt"; then
3215 _err
"Error removing txt for domain:$txtdomain"
3218 _info
"Removed: Success"
3224 # webroot removelevel tokenfile
3225 _clearupwebbroot
() {
3227 if [ -z "$__webroot" ]; then
3228 _debug
"no webroot specified, skip"
3233 if [ "$2" = '1' ]; then
3234 _rmpath
="$__webroot/.well-known"
3235 elif [ "$2" = '2' ]; then
3236 _rmpath
="$__webroot/.well-known/acme-challenge"
3237 elif [ "$2" = '3' ]; then
3238 _rmpath
="$__webroot/.well-known/acme-challenge/$3"
3240 _debug
"Skip for removelevel:$2"
3243 if [ "$_rmpath" ]; then
3244 if [ "$DEBUG" ]; then
3245 _debug
"Debugging, skip removing: $_rmpath"
3255 _on_before_issue
() {
3257 _chk_main_domain
="$2"
3258 _chk_alt_domains
="$3"
3260 _chk_local_addr
="$5"
3261 _debug _on_before_issue
3262 _debug _chk_main_domain
"$_chk_main_domain"
3263 _debug _chk_alt_domains
"$_chk_alt_domains"
3265 if [ "$_chk_pre_hook" ]; then
3266 _info
"Run pre hook:'$_chk_pre_hook'"
3268 cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
3270 _err
"Error when run pre hook."
3275 if _hasfield
"$_chk_web_roots" "$NO_VALUE"; then
3276 if ! _exists
"socat"; then
3277 _err
"Please install socat tools first."
3282 _debug Le_LocalAddress
"$_chk_local_addr"
3289 d
="$(echo "$_chk_main_domain,$_chk_alt_domains," | cut -d , -f "$_w_index")"
3290 _w_index
="$(_math "$_w_index" + 1)"
3292 if [ -z "$d" ]; then
3295 _debug
"Check for domain" "$d"
3296 _currentRoot
="$(_getfield "$_chk_web_roots" $_index)"
3297 _debug
"_currentRoot" "$_currentRoot"
3298 _index
=$
(_math
$_index + 1)
3300 if [ "$_currentRoot" = "$NO_VALUE" ]; then
3301 _info
"Standalone mode."
3302 if [ -z "$Le_HTTPPort" ]; then
3304 _cleardomainconf
"Le_HTTPPort"
3306 _savedomainconf
"Le_HTTPPort" "$Le_HTTPPort"
3308 _checkport
="$Le_HTTPPort"
3309 elif [ "$_currentRoot" = "$W_ALPN" ]; then
3310 _info
"Standalone alpn mode."
3311 if [ -z "$Le_TLSPort" ]; then
3314 _savedomainconf
"Le_TLSPort" "$Le_TLSPort"
3316 _checkport
="$Le_TLSPort"
3319 if [ "$_checkport" ]; then
3320 _debug _checkport
"$_checkport"
3321 _checkaddr
="$(_getfield "$_chk_local_addr" $_addrIndex)"
3322 _debug _checkaddr
"$_checkaddr"
3324 _addrIndex
="$(_math $_addrIndex + 1)"
3326 _netprc
="$(_ss "$_checkport" | grep "$_checkport")"
3327 netprc
="$(echo "$_netprc" | grep "$_checkaddr")"
3328 if [ -z "$netprc" ]; then
3329 netprc
="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
3331 if [ "$netprc" ]; then
3333 _err
"tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
3334 _err
"Please stop it first"
3340 if _hasfield
"$_chk_web_roots" "apache"; then
3341 if ! _setApache
; then
3342 _err
"set up apache error. Report error to me."
3354 _debug _on_issue_err
3356 if [ "$LOG_FILE" ]; then
3357 _err
"Please check log file for more details: $LOG_FILE"
3359 _err
"Please add '--debug' or '--log' to check more details."
3360 _err
"See: $_DEBUG_WIKI"
3364 if [ "$_chk_post_hook" ]; then
3365 _info
"Run post hook:'$_chk_post_hook'"
3367 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3369 _err
"Error when run post hook."
3374 #trigger the validation to flush the pending authz
3375 _debug2
"_chk_vlist" "$_chk_vlist"
3376 if [ "$_chk_vlist" ]; then
3378 _debug2
"start to deactivate authz"
3379 ventries
=$
(echo "$_chk_vlist" |
tr "$dvsep" ' ')
3380 for ventry
in $ventries; do
3381 d
=$
(echo "$ventry" | cut
-d "$sep" -f 1)
3382 keyauthorization
=$
(echo "$ventry" | cut
-d "$sep" -f 2)
3383 uri
=$
(echo "$ventry" | cut
-d "$sep" -f 3)
3384 vtype
=$
(echo "$ventry" | cut
-d "$sep" -f 4)
3385 _currentRoot
=$
(echo "$ventry" | cut
-d "$sep" -f 5)
3386 __trigger_validation
"$uri" "$keyauthorization"
3391 if [ "$_ACME_IS_RENEW" = "1" ] && _hasfield
"$Le_Webroot" "$W_DNS"; then
3392 _err
"$_DNS_MANUAL_ERR"
3395 if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
3396 _debug
"$(_dlg_versions)"
3401 _on_issue_success
() {
3403 _chk_renew_hook
="$2"
3404 _debug _on_issue_success
3407 if [ "$_chk_post_hook" ]; then
3408 _info
"Run post hook:'$_chk_post_hook'"
3411 export CERT_KEY_PATH
3413 export CERT_FULLCHAIN_PATH
3414 export Le_Domain
="$_main_domain"
3415 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
3417 _err
"Error when run post hook."
3423 if [ "$_ACME_IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
3424 _info
"Run renew hook:'$_chk_renew_hook'"
3427 export CERT_KEY_PATH
3429 export CERT_FULLCHAIN_PATH
3430 export Le_Domain
="$_main_domain"
3431 cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
3433 _err
"Error when run renew hook."
3438 if _hasfield
"$Le_Webroot" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
3439 _err
"$_DNS_MANUAL_WARN"
3444 #account_key_length eab-kid eab-hmac-key
3446 _account_key_length
="$1"
3450 _regAccount
"$_account_key_length" "$_eab_id" "$_eab_hmac_key"
3453 __calcAccountKeyHash
() {
3454 [ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256
<"$ACCOUNT_KEY_PATH"
3457 __calc_account_thumbprint
() {
3458 printf "%s" "$jwk" |
tr -d ' ' | _digest
"sha256" | _url_replace
3461 _getAccountEmail
() {
3462 if [ "$ACCOUNT_EMAIL" ]; then
3463 echo "$ACCOUNT_EMAIL"
3466 if [ -z "$CA_EMAIL" ]; then
3467 CA_EMAIL
="$(_readcaconf CA_EMAIL)"
3469 if [ "$CA_EMAIL" ]; then
3473 _readaccountconf
"ACCOUNT_EMAIL"
3482 _debug3 _regAccount
"$_regAccount"
3486 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3487 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3488 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3491 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3492 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3493 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3496 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3497 if ! _create_account_key
"$_reg_length"; then
3498 _err
"Create account key error."
3503 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3506 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3507 _savecaconf CA_EAB_KEY_ID
"$_eab_id"
3508 _savecaconf CA_EAB_HMAC_KEY
"$_eab_hmac_key"
3510 _eab_id
=$
(_readcaconf
"CA_EAB_KEY_ID")
3511 _eab_hmac_key
=$
(_readcaconf
"CA_EAB_HMAC_KEY")
3512 _secure_debug3 _eab_id
"$_eab_id"
3513 _secure_debug3 _eab_hmac_key
"$_eab_hmac_key"
3514 _email
="$(_getAccountEmail)"
3515 if [ "$_email" ]; then
3516 _savecaconf
"CA_EMAIL" "$_email"
3518 if [ "$ACME_VERSION" = "2" ]; then
3519 if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then
3520 if [ -z "$_eab_id" ] ||
[ -z "$_eab_hmac_key" ]; then
3521 _info
"No EAB credentials found for ZeroSSL, let's get one"
3522 if [ -z "$_email" ]; then
3523 _err
"Please provide a email address for ZeroSSL account."
3524 _err
"See ZeroSSL usage: $_ZEROSSL_WIKI"
3527 _eabresp
=$
(_post
"email=$_email" $_ZERO_EAB_ENDPOINT)
3528 if [ "$?" != "0" ]; then
3530 _err
"Can not get EAB credentials from ZeroSSL."
3533 _eab_id
="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_kid
"' | cut -d : -f 2 | tr -d '"')"
3534 if [ -z "$_eab_id" ]; then
3535 _err "Can not resolve _eab_id"
3538 _eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')"
3539 if [ -z "$_eab_hmac_key" ]; then
3540 _err
"Can not resolve _eab_hmac_key"
3543 _savecaconf CA_EAB_KEY_ID
"$_eab_id"
3544 _savecaconf CA_EAB_HMAC_KEY
"$_eab_hmac_key"
3547 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3548 eab_protected
="{\"alg\":\"HS256\",\"kid\":\"$_eab_id\",\"url\":\"${ACME_NEW_ACCOUNT}\"}"
3549 _debug3 eab_protected
"$eab_protected"
3551 eab_protected64
=$
(printf "%s" "$eab_protected" | _base64 | _url_replace
)
3552 _debug3 eab_protected64
"$eab_protected64"
3554 eab_payload64
=$
(printf "%s" "$jwk" | _base64 | _url_replace
)
3555 _debug3 eab_payload64
"$eab_payload64"
3557 eab_sign_t
="$eab_protected64.$eab_payload64"
3558 _debug3 eab_sign_t
"$eab_sign_t"
3560 key_hex
="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')"
3561 _debug3 key_hex
"$key_hex"
3563 eab_signature
=$
(printf "%s" "$eab_sign_t" | _hmac sha256
$key_hex | _base64 | _url_replace
)
3564 _debug3 eab_signature
"$eab_signature"
3566 externalBinding
=",\"externalAccountBinding\":{\"protected\":\"$eab_protected64\", \"payload\":\"$eab_payload64\", \"signature\":\"$eab_signature\"}"
3567 _debug3 externalBinding
"$externalBinding"
3569 if [ "$_email" ]; then
3570 email_sg
="\"contact\": [\"mailto:$_email\"], "
3572 regjson
="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}"
3574 _reg_res
="$ACME_NEW_ACCOUNT_RES"
3575 regjson
='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
3576 if [ "$_email" ]; then
3577 regjson
='{"resource": "'$_reg_res'", "contact": ["mailto:'$_email'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
3581 _info
"Registering account: $ACME_DIRECTORY"
3583 if ! _send_signed_request
"${ACME_NEW_ACCOUNT}" "$regjson"; then
3584 _err
"Register account Error: $response"
3589 if [ "$code" = "" ] ||
[ "$code" = '201' ]; then
3590 echo "$response" >"$ACCOUNT_JSON_PATH"
3592 elif [ "$code" = '409' ] ||
[ "$code" = '200' ]; then
3593 _info
"Already registered"
3594 elif [ "$code" = '400' ] && _contains
"$response" 'The account is not awaiting external account binding'; then
3595 _info
"Already register EAB."
3598 _err
"Register account Error: $response"
3602 if [ -z "$_eabAlreadyBound" ]; then
3603 _debug2 responseHeaders
"$responseHeaders"
3604 _accUri
="$(echo "$responseHeaders" | grep -i "^Location
:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
3605 _debug
"_accUri" "$_accUri"
3606 if [ -z "$_accUri" ]; then
3607 _err
"Can not find account id url."
3608 _err
"$responseHeaders"
3611 _savecaconf
"ACCOUNT_URL" "$_accUri"
3613 ACCOUNT_URL
="$(_readcaconf ACCOUNT_URL)"
3615 export ACCOUNT_URL
="$_accUri"
3617 CA_KEY_HASH
="$(__calcAccountKeyHash)"
3618 _debug
"Calc CA_KEY_HASH" "$CA_KEY_HASH"
3619 _savecaconf CA_KEY_HASH
"$CA_KEY_HASH"
3621 if [ "$code" = '403' ]; then
3622 _err
"It seems that the account key is already deactivated, please use a new account key."
3626 ACCOUNT_THUMBPRINT
="$(__calc_account_thumbprint)"
3627 _info
"ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
3630 #implement updateaccount
3634 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3635 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3636 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3639 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3640 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3641 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3644 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3645 _err
"Account key is not found at: $ACCOUNT_KEY_PATH"
3649 _accUri
=$
(_readcaconf
"ACCOUNT_URL")
3650 _debug _accUri
"$_accUri"
3652 if [ -z "$_accUri" ]; then
3653 _err
"The account url is empty, please run '--update-account' first to update the account info first,"
3654 _err
"Then try again."
3658 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3663 _email
="$(_getAccountEmail)"
3664 if [ "$ACME_VERSION" = "2" ]; then
3665 if [ "$ACCOUNT_EMAIL" ]; then
3666 updjson
='{"contact": ["mailto:'$_email'"]}'
3668 updjson
='{"contact": []}'
3671 # ACMEv1: Updates happen the same way a registration is done.
3672 # https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-6.3
3677 # this part handles ACMEv2 account updates.
3678 _send_signed_request
"$_accUri" "$updjson"
3680 if [ "$code" = '200' ]; then
3681 echo "$response" >"$ACCOUNT_JSON_PATH"
3682 _info
"account update success for $_accUri."
3684 _info
"Error. The account was not updated."
3689 #Implement deactivate account
3690 deactivateaccount
() {
3693 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3694 _info
"mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3695 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3698 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3699 _info
"mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3700 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3703 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3704 _err
"Account key is not found at: $ACCOUNT_KEY_PATH"
3708 _accUri
=$
(_readcaconf
"ACCOUNT_URL")
3709 _debug _accUri
"$_accUri"
3711 if [ -z "$_accUri" ]; then
3712 _err
"The account url is empty, please run '--update-account' first to update the account info first,"
3713 _err
"Then try again."
3717 if ! _calcjwk
"$ACCOUNT_KEY_PATH"; then
3722 if [ "$ACME_VERSION" = "2" ]; then
3723 _djson
="{\"status\":\"deactivated\"}"
3725 _djson
="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
3727 if _send_signed_request
"$_accUri" "$_djson" && _contains
"$response" '"deactivated"'; then
3728 _info
"Deactivate account success for $_accUri."
3729 _accid
=$
(echo "$response" | _egrep_o
"\"id\" *: *[^,]*," | cut
-d : -f 2 |
tr -d ' ,')
3730 elif [ "$code" = "403" ]; then
3731 _info
"The account is already deactivated."
3732 _accid
=$
(_getfield
"$_accUri" "999" "/")
3734 _err
"Deactivate: account failed for $_accUri."
3738 _debug
"Account id: $_accid"
3739 if [ "$_accid" ]; then
3740 _deactivated_account_path
="$CA_DIR/deactivated/$_accid"
3741 _debug _deactivated_account_path
"$_deactivated_account_path"
3742 if mkdir
-p "$_deactivated_account_path"; then
3743 _info
"Moving deactivated account info to $_deactivated_account_path/"
3744 mv "$CA_CONF" "$_deactivated_account_path/"
3745 mv "$ACCOUNT_JSON_PATH" "$_deactivated_account_path/"
3746 mv "$ACCOUNT_KEY_PATH" "$_deactivated_account_path/"
3748 _err
"Can not create dir: $_deactivated_account_path, try to remove the deactivated account key."
3750 rm -f "$ACCOUNT_JSON_PATH"
3751 rm -f "$ACCOUNT_KEY_PATH"
3756 # domain folder file
3762 if [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname" ]; then
3763 d_api
="$_SCRIPT_HOME/$_hookcat/$_hookname"
3764 elif [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname.sh" ]; then
3765 d_api
="$_SCRIPT_HOME/$_hookcat/$_hookname.sh"
3766 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then
3767 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname"
3768 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then
3769 d_api
="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
3770 elif [ -f "$LE_WORKING_DIR/$_hookname" ]; then
3771 d_api
="$LE_WORKING_DIR/$_hookname"
3772 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ]; then
3773 d_api
="$LE_WORKING_DIR/$_hookname.sh"
3774 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ]; then
3775 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname"
3776 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ]; then
3777 d_api
="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
3780 printf "%s" "$d_api"
3784 __get_domain_new_authz
() {
3786 _info
"Getting new-authz for domain" "$_gdnd"
3788 _Max_new_authz_retry_times
=5
3790 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do
3791 _debug
"Try new-authz for the $_authz_i time."
3792 if ! _send_signed_request
"${ACME_NEW_AUTHZ}" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then
3793 _err
"Can not get domain new authz."
3796 if _contains
"$response" "No registration exists matching provided key"; then
3797 _err
"It seems there is an error, but it's recovered now, please try again."
3798 _err
"If you see this message for a second time, please report bug: $(__green "$PROJECT")"
3799 _clearcaconf
"CA_KEY_HASH"
3802 if ! _contains
"$response" "An error occurred while processing your request"; then
3803 _info
"The new-authz request is ok."
3806 _authz_i
="$(_math "$_authz_i" + 1)"
3807 _info
"The server is busy, Sleep $_authz_i to retry."
3811 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ]; then
3812 _err
"new-authz retry reach the max $_Max_new_authz_retry_times times."
3815 if [ "$code" ] && [ "$code" != '201' ]; then
3816 _err
"new-authz error: $response"
3822 #uri keyAuthorization
3823 __trigger_validation
() {
3824 _debug2
"Trigger domain validation."
3826 _debug2 _t_url
"$_t_url"
3828 _debug2 _t_key_authz
"$_t_key_authz"
3830 _debug2 _t_vtype
"$_t_vtype"
3831 if [ "$ACME_VERSION" = "2" ]; then
3832 _send_signed_request
"$_t_url" "{}"
3834 _send_signed_request
"$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
3838 #endpoint domain type
3843 _debug2
"_ns_ep" "$_ns_ep"
3844 _debug2
"_ns_domain" "$_ns_domain"
3845 _debug2
"_ns_type" "$_ns_type"
3847 response
="$(_H1="accept
: application
/dns-json
" _get "$_ns_ep?name
=$_ns_domain&type=$_ns_type")"
3849 _debug2
"response" "$response"
3850 if [ "$_ret" != "0" ]; then
3853 _answers
="$(echo "$response" | tr '{}' '<>' | _egrep_o '"Answer
":\[[^]]*]' | tr '<>' '\n\n')"
3854 _debug2
"_answers" "$_answers"
3862 _cf_ep
="https://cloudflare-dns.com/dns-query"
3863 _ns_lookup_impl
"$_cf_ep" "$_cf_ld" "$_cf_ld_type"
3870 _debug
"Cloudflare purge $_cf_d_type record for domain $_cf_d"
3871 _cf_purl
="https://cloudflare-dns.com/api/v1/purge?domain=$_cf_d&type=$_cf_d_type"
3872 response
="$(_post "" "$_cf_purl")"
3873 _debug2 response
"$response"
3876 #checks if cf server is available
3877 _ns_is_available_cf
() {
3878 if _get
"https://cloudflare-dns.com" >/dev
/null
2>&1; then
3886 _ns_lookup_google
() {
3889 _cf_ep
="https://dns.google/resolve"
3890 _ns_lookup_impl
"$_cf_ep" "$_cf_ld" "$_cf_ld_type"
3895 if [ -z "$DOH_USE" ]; then
3896 _debug
"Detect dns server first."
3897 if _ns_is_available_cf
; then
3898 _debug
"Use cloudflare doh server"
3899 export DOH_USE
=$DOH_CLOUDFLARE
3901 _debug
"Use google doh server"
3902 export DOH_USE
=$DOH_GOOGLE
3906 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] ||
[ -z "$DOH_USE" ]; then
3909 _ns_lookup_google
"$@"
3914 #txtdomain, alias, txt
3919 _debug
"_c_txtdomain" "$_c_txtdomain"
3920 _debug
"_c_aliasdomain" "$_c_aliasdomain"
3921 _debug
"_c_txt" "$_c_txt"
3922 _answers
="$(_ns_lookup "$_c_aliasdomain" TXT)"
3923 _contains
"$_answers" "$_c_txt"
3930 _debug _p_txtdomain
"$_p_txtdomain"
3931 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] ||
[ -z "$DOH_USE" ]; then
3932 _ns_purge_cf
"$_p_txtdomain" "TXT"
3934 _debug
"no purge api for google dns api, just sleep 5 secs"
3940 #wait and check each dns entries
3941 _check_dns_entries
() {
3943 _end_time
="$(_time)"
3944 _end_time
="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes.
3946 while [ "$(_time)" -le "$_end_time" ]; do
3948 for entry
in $dns_entries; do
3949 d
=$
(_getfield
"$entry" 1)
3950 txtdomain
=$
(_getfield
"$entry" 2)
3951 txtdomain
=$
(_idn
"$txtdomain")
3952 aliasDomain
=$
(_getfield
"$entry" 3)
3953 aliasDomain
=$
(_idn
"$aliasDomain")
3954 txt
=$
(_getfield
"$entry" 5)
3955 d_api
=$
(_getfield
"$entry" 6)
3957 _debug
"txtdomain" "$txtdomain"
3958 _debug
"aliasDomain" "$aliasDomain"
3960 _debug
"d_api" "$d_api"
3961 _info
"Checking $d for $aliasDomain"
3962 if _contains
"$_success_txt" ",$txt,"; then
3963 _info
"Already success, continue next one."
3967 if __check_txt
"$txtdomain" "$aliasDomain" "$txt"; then
3968 _info
"Domain $d '$aliasDomain' success."
3969 _success_txt
="$_success_txt,$txt,"
3973 _info
"Not valid yet, let's wait 10 seconds and check next one."
3974 __purge_txt
"$txtdomain"
3975 if [ "$txtdomain" != "$aliasDomain" ]; then
3976 __purge_txt
"$aliasDomain"
3980 if [ "$_left" ]; then
3981 _info
"Let's wait 10 seconds and check again".
3984 _info
"All success, let's return"
3988 _info
"Timed out waiting for DNS."
3994 _get_cert_issuers
() {
3996 if _contains
"$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7"; then
3997 ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7
-nocrl -certfile $_cfile | openssl pkcs7
-print_certs -text -noout |
grep 'Issuer:' | _egrep_o
"CN *=[^,]*" | cut
-d = -f 2
3999 ${ACME_OPENSSL_BIN:-openssl} x509
-in $_cfile -text -noout |
grep 'Issuer:' | _egrep_o
"CN *=[^,]*" | cut
-d = -f 2
4007 _fissuers
="$(_get_cert_issuers $_cfile)"
4008 _debug2 _fissuers
"$_fissuers"
4009 _contains
"$_fissuers" "$_missuer"
4012 #webroot, domain domainlist keylength
4014 if [ -z "$2" ]; then
4015 _usage
"Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
4018 if [ -z "$1" ]; then
4019 _usage
"Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
4026 if _contains
"$_main_domain" ","; then
4027 _main_domain
=$
(echo "$2,$3" | cut
-d , -f 1)
4028 _alt_domains
=$
(echo "$2,$3" | cut
-d , -f 2- |
sed "s/,${NO_VALUE}$//")
4030 _debug _main_domain
"$_main_domain"
4031 _debug _alt_domains
"$_alt_domains"
4038 _real_fullchain
="$9"
4043 _challenge_alias
="${14}"
4044 _preferred_chain
="${15}"
4046 if [ -z "$_ACME_IS_RENEW" ]; then
4047 _initpath
"$_main_domain" "$_key_length"
4048 mkdir
-p "$DOMAIN_PATH"
4051 if _hasfield
"$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
4052 _err
"$_DNS_MANUAL_ERROR"
4056 _debug
"Using ACME_DIRECTORY: $ACME_DIRECTORY"
4060 if [ -f "$DOMAIN_CONF" ]; then
4061 Le_NextRenewTime
=$
(_readdomainconf Le_NextRenewTime
)
4062 _debug Le_NextRenewTime
"$Le_NextRenewTime"
4063 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
4064 _saved_domain
=$
(_readdomainconf Le_Domain
)
4065 _debug _saved_domain
"$_saved_domain"
4066 _saved_alt
=$
(_readdomainconf Le_Alt
)
4067 _debug _saved_alt
"$_saved_alt"
4068 if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
4069 _info
"Domains not changed."
4070 _info
"Skip, Next renewal time is: $(__green "$
(_readdomainconf Le_NextRenewTimeStr
)")"
4071 _info
"Add '$(__red '--force')' to force to renew."
4074 _info
"Domains have changed."
4079 _savedomainconf
"Le_Domain" "$_main_domain"
4080 _savedomainconf
"Le_Alt" "$_alt_domains"
4081 _savedomainconf
"Le_Webroot" "$_web_roots"
4083 _savedomainconf
"Le_PreHook" "$_pre_hook" "base64"
4084 _savedomainconf
"Le_PostHook" "$_post_hook" "base64"
4085 _savedomainconf
"Le_RenewHook" "$_renew_hook" "base64"
4087 if [ "$_local_addr" ]; then
4088 _savedomainconf
"Le_LocalAddress" "$_local_addr"
4090 _cleardomainconf
"Le_LocalAddress"
4092 if [ "$_challenge_alias" ]; then
4093 _savedomainconf
"Le_ChallengeAlias" "$_challenge_alias"
4095 _cleardomainconf
"Le_ChallengeAlias"
4097 if [ "$_preferred_chain" ]; then
4098 _savedomainconf
"Le_Preferred_Chain" "$_preferred_chain" "base64"
4100 _cleardomainconf
"Le_Preferred_Chain"
4103 Le_API
="$ACME_DIRECTORY"
4104 _savedomainconf
"Le_API" "$Le_API"
4106 _info
"Using CA: $ACME_DIRECTORY"
4107 if [ "$_alt_domains" = "$NO_VALUE" ]; then
4111 if [ "$_key_length" = "$NO_VALUE" ]; then
4115 if ! _on_before_issue
"$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
4116 _err
"_on_before_issue."
4120 _saved_account_key_hash
="$(_readcaconf "CA_KEY_HASH
")"
4121 _debug2 _saved_account_key_hash
"$_saved_account_key_hash"
4123 if [ -z "$ACCOUNT_URL" ] ||
[ -z "$_saved_account_key_hash" ] ||
[ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
4124 if ! _regAccount
"$_accountkeylength"; then
4125 _on_issue_err
"$_post_hook"
4129 _debug
"_saved_account_key_hash is not changed, skip register account."
4132 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
4133 _info
"Signing from existing CSR."
4135 _key
=$
(_readdomainconf Le_Keylength
)
4136 _debug
"Read key length:$_key"
4137 if [ ! -f "$CERT_KEY_PATH" ] ||
[ "$_key_length" != "$_key" ] ||
[ "$Le_ForceNewDomainKey" = "1" ]; then
4138 if ! createDomainKey
"$_main_domain" "$_key_length"; then
4139 _err
"Create domain key error."
4141 _on_issue_err
"$_post_hook"
4146 if ! _createcsr
"$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
4147 _err
"Create CSR error."
4149 _on_issue_err
"$_post_hook"
4154 _savedomainconf
"Le_Keylength" "$_key_length"
4157 _cleardomainconf
"Le_Vlist"
4158 _info
"Getting domain auth token for each domain"
4161 if [ -z "$vlist" ]; then
4162 if [ "$ACME_VERSION" = "2" ]; then
4163 #make new order request
4164 _identifiers
="{\"type\":\"dns\",\"value\":\"$(_idn "$_main_domain")\"}"
4167 d
="$(echo "$_alt_domains," | cut -d , -f "$_w_index")"
4168 _w_index
="$(_math "$_w_index" + 1)"
4170 if [ -z "$d" ]; then
4173 _identifiers
="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn "$d")\"}"
4175 _debug2 _identifiers
"$_identifiers"
4176 if ! _send_signed_request
"$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
4177 _err
"Create new order error."
4179 _on_issue_err
"$_post_hook"
4182 Le_LinkOrder
="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
4183 _debug Le_LinkOrder
"$Le_LinkOrder"
4184 Le_OrderFinalize
="$(echo "$response" | _egrep_o '"finalize
" *: *"[^
"]*"' | cut -d '"' -f 4)"
4185 _debug Le_OrderFinalize
"$Le_OrderFinalize"
4186 if [ -z "$Le_OrderFinalize" ]; then
4187 _err
"Create new order error. Le_OrderFinalize not found. $response"
4189 _on_issue_err
"$_post_hook"
4193 #for dns manual mode
4194 _savedomainconf
"Le_OrderFinalize" "$Le_OrderFinalize"
4196 _authorizations_seg
="$(echo "$response" | _json_decode | _egrep_o '"authorizations
" *: *\[[^\[]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
4197 _debug2 _authorizations_seg "$_authorizations_seg"
4198 if [ -z "$_authorizations_seg" ]; then
4199 _err "_authorizations_seg not found."
4201 _on_issue_err "$_post_hook"
4205 #domain and authz map
4206 _authorizations_map=""
4207 for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
4208 _debug2 "_authz_url" "$_authz_url"
4209 if ! _send_signed_request "$_authz_url"; then
4210 _err "get to authz error."
4211 _err "_authorizations_seg" "$_authorizations_seg"
4212 _err "_authz_url" "$_authz_url"
4214 _on_issue_err "$_post_hook"
4218 response="$(echo "$response" | _normalizeJson)"
4219 _debug2 response "$response"
4220 _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
4221 if _contains "$response" "\"wildcard\" *: *true"; then
4225 _authorizations_map="$_d,$response
4226 $_authorizations_map"
4228 _debug2 _authorizations_map "$_authorizations_map"
4235 d="$(echo "$_main_domain,$_alt_domains," | cut -d , -f "$_w_index")"
4236 _w_index="$(_math "$_w_index" + 1)"
4238 if [ -z "$d" ]; then
4241 _info "Getting webroot for domain" "$d"
4242 _index=$(_math $_index + 1)
4243 _w="$(echo $_web_roots | cut -d , -f $_index)"
4248 _debug "_currentRoot" "$_currentRoot"
4251 #todo, v2 wildcard force to use dns
4252 if _startswith "$_currentRoot" "$W_DNS"; then
4256 if [ "$_currentRoot" = "$W_ALPN" ]; then
4260 if [ "$ACME_VERSION" = "2" ]; then
4261 _idn_d="$(_idn "$d")"
4262 _candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
4263 _debug2 _candidates "$_candidates"
4264 if [ "$(echo "$_candidates" | wc -l)" -gt 1 ]; then
4265 for _can in $_candidates; do
4266 if _startswith "$(echo "$_can" | tr '.
' '|
')" "$(echo "$_idn_d" | tr '.
' '|
'),"; then
4272 response="$(echo "$_candidates" | sed "s/$_idn_d,//")"
4273 _debug2 "response" "$response"
4274 if [ -z "$response" ]; then
4275 _err "get to authz error."
4276 _err "_authorizations_map" "$_authorizations_map"
4278 _on_issue_err "$_post_hook"
4282 if ! __get_domain_new_authz "$d"; then
4284 _on_issue_err "$_post_hook"
4289 if [ -z "$thumbprint" ]; then
4290 thumbprint="$(__calc_account_thumbprint)"
4293 entry="$(echo "$response" | _egrep_o '[^\
{]*"type":"'$vtype'"[^\
}]*')"
4294 _debug entry "$entry"
4296 if [ -z "$entry" ]; then
4297 if ! _startswith "$d" '*.
'; then
4298 _debug "Not a wildcard domain, lets check whether the validation is already valid."
4299 if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4300 _debug "$d is already valid."
4301 keyauthorization="$STATE_VERIFIED"
4302 _debug keyauthorization "$keyauthorization"
4305 if [ -z "$keyauthorization" ]; then
4306 _err "Error, can not get domain token entry $d for $vtype"
4307 _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
4308 if [ "$_supported_vtypes" ]; then
4309 _err
"The supported validation types are: $_supported_vtypes, but you specified: $vtype"
4312 _on_issue_err
"$_post_hook"
4317 if [ -z "$keyauthorization" ]; then
4318 token
="$(echo "$entry" | _egrep_o '"token
":"[^
"]*' | cut -d : -f 2 | tr -d '"')"
4319 _debug token "$token"
4321 if [ -z "$token" ]; then
4322 _err "Error, can not get domain token $entry"
4324 _on_issue_err "$_post_hook"
4327 if [ "$ACME_VERSION" = "2" ]; then
4328 uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
4330 uri
="$(echo "$entry" | _egrep_o '"uri
":"[^
"]*' | cut -d '"' -f 4)"
4334 if [ -z "$uri" ]; then
4335 _err "Error, can not get domain uri. $entry"
4337 _on_issue_err "$_post_hook"
4340 keyauthorization="$token.$thumbprint"
4341 _debug keyauthorization "$keyauthorization"
4343 if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4344 _debug "$d is already verified."
4345 keyauthorization="$STATE_VERIFIED"
4346 _debug keyauthorization "$keyauthorization"
4350 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
4351 _debug dvlist "$dvlist"
4353 vlist="$vlist$dvlist$dvsep"
4356 _debug vlist "$vlist"
4360 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
4362 for ventry in $ventries; do
4363 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4364 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4365 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4366 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
4368 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
4369 _debug "$d is already verified, skip $vtype."
4370 _alias_index="$(_math "$_alias_index" + 1)"
4374 if [ "$vtype" = "$VTYPE_DNS" ]; then
4377 if _startswith "$_dns_root_d" "*."; then
4378 _dns_root_d="$(echo "$_dns_root_d" | sed 's
/*.
//')"
4380 _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
4381 _alias_index="$(_math "$_alias_index" + 1)"
4382 _debug "_d_alias" "$_d_alias"
4383 if [ "$_d_alias" ]; then
4384 if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
4385 txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
4387 txtdomain="_acme-challenge.$_d_alias"
4389 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot"
4391 txtdomain="_acme-challenge.$_dns_root_d"
4392 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot"
4395 _debug txtdomain "$txtdomain"
4396 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
4399 d_api="$(_findHook "$_dns_root_d" $_SUB_FOLDER_DNSAPI "$_currentRoot")"
4400 _debug d_api "$d_api"
4402 dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api"
4403 _debug2 dns_entry "$dns_entry"
4404 if [ "$d_api" ]; then
4405 _debug "Found domain api file: $d_api"
4407 if [ "$_currentRoot" != "$W_DNS" ]; then
4408 _err "Can not find dns api hook for: $_currentRoot"
4409 _info "You need to add the txt record manually."
4411 _info "$(__red "Add the following TXT record:")"
4412 _info "$(__red "Domain: '$
(__green
"$txtdomain")'")"
4413 _info "$(__red "TXT value: '$
(__green
"$txt")'")"
4414 _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
4415 _info "$(__red "so the resulting subdomain will be: $txtdomain")"
4420 if ! . "$d_api"; then
4421 _err "Load file $d_api error. Please check your api file and try again."
4425 addcommand="${_currentRoot}_add"
4426 if ! _exists "$addcommand"; then
4427 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
4430 _info "Adding txt value: $txt for domain: $txtdomain"
4431 if ! $addcommand "$txtdomain" "$txt"; then
4432 _err "Error add txt for domain:$txtdomain"
4435 _info "The txt record is added: Success."
4438 if [ "$?" != "0" ]; then
4439 _on_issue_err "$_post_hook" "$vlist"
4443 dns_entries="$dns_entries$dns_entry
4445 _debug2 "$dns_entries"
4450 if [ "$dnsadded" = '0' ]; then
4451 _savedomainconf "Le_Vlist" "$vlist"
4452 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
4453 _err "Please add the TXT records to the domains, and re-run with --renew."
4454 _on_issue_err "$_post_hook"
4461 if [ "$dns_entries" ]; then
4462 if [ -z "$Le_DNSSleep" ]; then
4463 _info "Let's check each DNS record now. Sleep
20 seconds first.
"
4465 if ! _check_dns_entries; then
4466 _err "check dns error.
"
4467 _on_issue_err "$_post_hook"
4472 _savedomainconf "Le_DNSSleep
" "$Le_DNSSleep"
4473 _info "Sleep $
(__green
$Le_DNSSleep) seconds
for the txt records to take effect
"
4474 _sleep "$Le_DNSSleep"
4478 NGINX_RESTORE_VLIST=""
4479 _debug "ok
, let's start to verify"
4482 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
4483 for ventry in $ventries; do
4484 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4485 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4486 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
4487 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4488 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
4490 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
4491 _info "$d is already verified, skip $vtype."
4495 _info "Verifying: $d"
4497 _debug "keyauthorization" "$keyauthorization"
4500 token="$(printf "%s" "$keyauthorization" | cut -d '.
' -f 1)"
4502 _debug "_currentRoot" "$_currentRoot"
4504 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4505 if [ "$_currentRoot" = "$NO_VALUE" ]; then
4506 _info "Standalone mode server"
4507 _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
4508 _ncIndex="$(_math $_ncIndex + 1)"
4509 _startserver "$keyauthorization" "$_ncaddr"
4510 if [ "$?" != "0" ]; then
4512 _on_issue_err "$_post_hook" "$vlist"
4516 _debug serverproc "$serverproc"
4517 elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
4518 _info "Stateless mode for domain:$d"
4520 elif _startswith "$_currentRoot" "$NGINX"; then
4521 _info "Nginx mode for domain:$d"
4522 #set up nginx server
4523 FOUND_REAL_NGINX_CONF=""
4524 BACKUP_NGINX_CONF=""
4525 if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
4527 _on_issue_err "$_post_hook" "$vlist"
4531 if [ "$FOUND_REAL_NGINX_CONF" ]; then
4532 _realConf="$FOUND_REAL_NGINX_CONF"
4533 _backup="$BACKUP_NGINX_CONF"
4534 _debug _realConf "$_realConf"
4535 NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
4539 if [ "$_currentRoot" = "apache" ]; then
4540 wellknown_path="$ACME_DIR"
4542 wellknown_path="$_currentRoot/.well-known/acme-challenge"
4543 if [ ! -d "$_currentRoot/.well-known" ]; then
4545 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ]; then
4552 _debug wellknown_path "$wellknown_path"
4554 _debug "writing token:$token to $wellknown_path/$token"
4556 mkdir -p "$wellknown_path"
4558 if ! printf "%s" "$keyauthorization" >"$wellknown_path/$token"; then
4559 _err "$d:Can not write token to file : $wellknown_path/$token"
4560 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4562 _on_issue_err "$_post_hook" "$vlist"
4566 if [ ! "$usingApache" ]; then
4567 if webroot_owner=$(_stat "$_currentRoot"); then
4568 _debug "Changing owner/group of .well-known to $webroot_owner"
4569 if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
4570 _debug "$(cat "$_EXEC_TEMP_ERR")"
4571 _exec_err >/dev/null 2>&1
4574 _debug "not changing owner/group of webroot"
4579 elif [ "$vtype" = "$VTYPE_ALPN" ]; then
4580 acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
4581 _debug acmevalidationv1 "$acmevalidationv1"
4582 if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then
4583 _err "Start tls server error."
4584 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4586 _on_issue_err "$_post_hook" "$vlist"
4591 if ! __trigger_validation "$uri" "$keyauthorization" "$vtype"; then
4592 _err "$d:Can not get challenge: $response"
4593 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4595 _on_issue_err "$_post_hook" "$vlist"
4599 if [ "$code" ] && [ "$code" != '202' ]; then
4600 if [ "$code" = '200' ]; then
4601 _debug "trigger validation code: $code"
4603 _err "$d:Challenge error: $response"
4604 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4606 _on_issue_err "$_post_hook" "$vlist"
4612 if [ -z "$MAX_RETRY_TIMES" ]; then
4617 waittimes=$(_math "$waittimes" + 1)
4618 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
4620 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4622 _on_issue_err "$_post_hook" "$vlist"
4626 _debug "sleep 2 secs to verify"
4629 if [ "$ACME_VERSION" = "2" ]; then
4630 _send_signed_request "$uri"
4632 response="$(_get "$uri")"
4634 if [ "$?" != "0" ]; then
4635 _err "$d:Verify error:$response"
4636 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4638 _on_issue_err "$_post_hook" "$vlist"
4641 _debug2 original "$response"
4643 response="$(echo "$response" | _normalizeJson)"
4644 _debug2 response "$response"
4646 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
4647 if [ "$status" = "valid
" ]; then
4648 _info "$
(__green Success
)"
4649 _stopserver "$serverproc"
4651 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4655 if [ "$status" = "invalid
" ]; then
4656 error="$
(echo "$response" | _egrep_o
'"error":\{[^\}]*')"
4657 _debug2 error "$error"
4658 errordetail="$
(echo "$error" | _egrep_o
'"detail": *"[^"]*' | cut
-d '"' -f 4)"
4659 _debug2 errordetail "$errordetail"
4660 if [ "$errordetail" ]; then
4661 _err "$d:Verify error
:$errordetail"
4663 _err "$d:Verify error
:$error"
4665 if [ "$DEBUG" ]; then
4666 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4667 _debug "Debug
: get token url.
"
4668 _get "http
://$d/.well-known
/acme-challenge
/$token" "" 1
4671 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4673 _on_issue_err "$_post_hook" "$vlist"
4677 if [ "$status" = "pending
" ]; then
4679 elif [ "$status" = "processing
" ]; then
4682 _err "$d:Verify error
:$response"
4683 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4685 _on_issue_err "$_post_hook" "$vlist"
4694 _info "Verify finished
, start to sign.
"
4695 der="$
(_getfile
"${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" |
tr -d "\r\n" | _url_replace
)"
4697 if [ "$ACME_VERSION" = "2" ]; then
4698 _info "Lets finalize the order.
"
4699 _info "Le_OrderFinalize
" "$Le_OrderFinalize"
4700 if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr
\": \"$der\"}"; then
4702 _on_issue_err "$_post_hook"
4705 if [ "$code" != "200" ]; then
4706 _err "Sign failed
, finalize code is not
200.
"
4708 _on_issue_err "$_post_hook"
4711 if [ -z "$Le_LinkOrder" ]; then
4712 Le_LinkOrder="$
(echo "$responseHeaders" |
grep -i '^Location.*$' | _tail_n
1 |
tr -d "\r\n" | cut
-d ":" -f 2-)"
4715 _savedomainconf "Le_LinkOrder
" "$Le_LinkOrder"
4719 while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do
4720 if _contains "$response" "\"status
\":\"valid
\""; then
4721 _debug "Order status is valid.
"
4722 Le_LinkCert="$
(echo "$response" | _egrep_o
'"certificate" *: *"[^"]*"' | cut
-d '"' -f 4)"
4723 _debug Le_LinkCert "$Le_LinkCert"
4724 if [ -z "$Le_LinkCert" ]; then
4725 _err "Sign error
, can not
find Le_LinkCert
"
4727 _on_issue_err "$_post_hook"
4731 elif _contains "$response" "\"processing
\""; then
4732 _info "Order status is processing
, lets
sleep and retry.
"
4733 _retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After
*:" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
4734 _debug "_retryafter
" "$_retryafter"
4735 if [ "$_retryafter" ]; then
4736 _info "Retry after
: $_retryafter"
4742 _err "Sign error
, wrong status
"
4744 _on_issue_err "$_post_hook"
4747 #the order is processing, so we are going to poll order status
4748 if [ -z "$Le_LinkOrder" ]; then
4749 _err "Sign error
, can not get order link location header
"
4750 _err "responseHeaders
" "$responseHeaders"
4751 _on_issue_err "$_post_hook"
4754 _info "Polling order status
: $Le_LinkOrder"
4755 if ! _send_signed_request "$Le_LinkOrder"; then
4756 _err "Sign failed
, can not post to Le_LinkOrder cert
:$Le_LinkOrder.
"
4758 _on_issue_err "$_post_hook"
4761 _link_cert_retry="$
(_math
$_link_cert_retry + 1)"
4764 if [ -z "$Le_LinkCert" ]; then
4765 _err "Sign failed
, can not get Le_LinkCert
, retry
time limit.
"
4767 _on_issue_err "$_post_hook"
4770 _info "Downloading cert.
"
4771 _info "Le_LinkCert
" "$Le_LinkCert"
4772 if ! _send_signed_request "$Le_LinkCert"; then
4773 _err "Sign failed
, can not download cert
:$Le_LinkCert.
"
4775 _on_issue_err "$_post_hook"
4779 echo "$response" >"$CERT_PATH"
4780 _split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH"
4782 if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then
4783 if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then
4784 rels="$
(echo "$responseHeaders" |
tr -d ' <>' |
grep -i "^link:" |
grep -i 'rel="alternate"' | cut
-d : -f 2- | cut
-d ';' -f 1)"
4785 _debug2 "rels
" "$rels"
4786 for rel in $rels; do
4787 _info "Try rel
: $rel"
4788 if ! _send_signed_request "$rel"; then
4789 _err "Sign failed
, can not download cert
:$rel"
4793 _relcert="$CERT_PATH.alt
"
4794 _relfullchain="$CERT_FULLCHAIN_PATH.alt
"
4795 _relca="$CA_CERT_PATH.alt
"
4796 echo "$response" >"$_relcert"
4797 _split_cert_chain "$_relcert" "$_relfullchain" "$_relca"
4798 if _match_issuer "$_relfullchain" "$_preferred_chain"; then
4799 _info "Matched issuer
in: $rel"
4800 cat $_relcert >"$CERT_PATH"
4801 cat $_relfullchain >"$CERT_FULLCHAIN_PATH"
4802 cat $_relca >"$CA_CERT_PATH"
4809 if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource
\": \"$ACME_NEW_ORDER_RES\", \"csr
\": \"$der\"}" "needbase64
"; then
4810 _err "Sign failed.
$response"
4811 _on_issue_err "$_post_hook"
4815 Le_LinkCert="$
(grep -i '^Location.*$' "$HTTP_HEADER" | _tail_n
1 |
tr -d "\r\n" | cut
-d " " -f 2)"
4816 echo "$BEGIN_CERT" >"$CERT_PATH"
4818 #if ! _get "$Le_LinkCert" | _base64 "multiline
" >> "$CERT_PATH" ; then
4819 # _debug "Get cert failed. Let
's try last response."
4820 # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
4823 if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
4824 _debug "Try cert link."
4825 _get "$Le_LinkCert" | _base64 "multiline" >>"$CERT_PATH"
4828 echo "$END_CERT" >>"$CERT_PATH"
4831 _debug "Le_LinkCert" "$Le_LinkCert"
4832 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
4834 if [ -z "$Le_LinkCert" ] || ! _checkcert "$CERT_PATH"; then
4835 response="$(echo "$response" | _dbase64 "multiline" | tr -d '\
0' | _normalizeJson)"
4836 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
4837 _on_issue_err
"$_post_hook"
4841 if [ "$Le_LinkCert" ]; then
4842 _info
"$(__green "Cert success.
")"
4845 _info
"Your cert is in $(__green " $CERT_PATH ")"
4847 if [ -f "$CERT_KEY_PATH" ]; then
4848 _info
"Your cert key is in $(__green " $CERT_KEY_PATH ")"
4851 if [ ! "$USER_PATH" ] ||
[ ! "$_ACME_IN_CRON" ]; then
4853 _saveaccountconf
"USER_PATH" "$USER_PATH"
4857 if [ "$ACME_VERSION" = "2" ]; then
4860 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
4861 Le_LinkIssuer
=$
(grep -i '^Link' "$HTTP_HEADER" | _head_n
1 | cut
-d " " -f 2 | cut
-d ';' -f 1 |
tr -d '<>')
4863 if [ "$Le_LinkIssuer" ]; then
4864 if ! _contains
"$Le_LinkIssuer" ":"; then
4865 _info
"$(__red "Relative issuer link found.
")"
4866 Le_LinkIssuer
="$_ACME_SERVER_HOST$Le_LinkIssuer"
4868 _debug Le_LinkIssuer
"$Le_LinkIssuer"
4869 _savedomainconf
"Le_LinkIssuer" "$Le_LinkIssuer"
4871 _link_issuer_retry
=0
4873 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
4874 _debug _link_issuer_retry
"$_link_issuer_retry"
4875 if [ "$ACME_VERSION" = "2" ]; then
4876 if _send_signed_request
"$Le_LinkIssuer"; then
4877 echo "$response" >"$CA_CERT_PATH"
4881 if _get
"$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
4882 echo "$BEGIN_CERT" >"$CA_CERT_PATH"
4883 _base64
"multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
4884 echo "$END_CERT" >>"$CA_CERT_PATH"
4885 if ! _checkcert
"$CA_CERT_PATH"; then
4886 _err
"Can not get the ca cert."
4889 cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
4890 rm -f "$CA_CERT_PATH.der"
4894 _link_issuer_retry
=$
(_math
$_link_issuer_retry + 1)
4895 _sleep
"$_link_issuer_retry"
4897 if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
4898 _err
"Max retry for issuer ca cert is reached."
4901 _debug
"No Le_LinkIssuer header found."
4904 [ -f "$CA_CERT_PATH" ] && _info
"The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
4905 [ -f "$CERT_FULLCHAIN_PATH" ] && _info
"And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
4907 Le_CertCreateTime
=$
(_time
)
4908 _savedomainconf
"Le_CertCreateTime" "$Le_CertCreateTime"
4910 Le_CertCreateTimeStr
=$
(date -u)
4911 _savedomainconf
"Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
4913 if [ -z "$Le_RenewalDays" ] ||
[ "$Le_RenewalDays" -lt "0" ]; then
4914 Le_RenewalDays
="$DEFAULT_RENEW"
4916 _savedomainconf
"Le_RenewalDays" "$Le_RenewalDays"
4919 if [ "$CA_BUNDLE" ]; then
4920 _saveaccountconf CA_BUNDLE
"$CA_BUNDLE"
4922 _clearaccountconf
"CA_BUNDLE"
4925 if [ "$CA_PATH" ]; then
4926 _saveaccountconf CA_PATH
"$CA_PATH"
4928 _clearaccountconf
"CA_PATH"
4931 if [ "$HTTPS_INSECURE" ]; then
4932 _saveaccountconf HTTPS_INSECURE
"$HTTPS_INSECURE"
4934 _clearaccountconf
"HTTPS_INSECURE"
4937 if [ "$Le_Listen_V4" ]; then
4938 _savedomainconf
"Le_Listen_V4" "$Le_Listen_V4"
4939 _cleardomainconf Le_Listen_V6
4940 elif [ "$Le_Listen_V6" ]; then
4941 _savedomainconf
"Le_Listen_V6" "$Le_Listen_V6"
4942 _cleardomainconf Le_Listen_V4
4945 if [ "$Le_ForceNewDomainKey" = "1" ]; then
4946 _savedomainconf
"Le_ForceNewDomainKey" "$Le_ForceNewDomainKey"
4948 _cleardomainconf Le_ForceNewDomainKey
4951 Le_NextRenewTime
=$
(_math
"$Le_CertCreateTime" + "$Le_RenewalDays" \
* 24 \
* 60 \
* 60)
4953 Le_NextRenewTimeStr
=$
(_time2str
"$Le_NextRenewTime")
4954 _savedomainconf
"Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
4956 Le_NextRenewTime
=$
(_math
"$Le_NextRenewTime" - 86400)
4957 _savedomainconf
"Le_NextRenewTime" "$Le_NextRenewTime"
4959 if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
4960 _savedomainconf
"Le_RealCertPath" "$_real_cert"
4961 _savedomainconf
"Le_RealCACertPath" "$_real_ca"
4962 _savedomainconf
"Le_RealKeyPath" "$_real_key"
4963 _savedomainconf
"Le_ReloadCmd" "$_reload_cmd" "base64"
4964 _savedomainconf
"Le_RealFullChainPath" "$_real_fullchain"
4965 if ! _installcert
"$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"; then
4970 if ! _on_issue_success
"$_post_hook" "$_renew_hook"; then
4971 _err
"Call hook error."
4976 #in_out_cert out_fullchain out out_ca
4977 _split_cert_chain
() {
4981 if [ "$(grep -- "$BEGIN_CERT" "$_certf" | wc -l)" -gt "1" ]; then
4982 _debug
"Found cert chain"
4983 cat "$_certf" >"$_fullchainf"
4984 _end_n
="$(grep -n -- "$END_CERT" "$_fullchainf" | _head_n 1 | cut -d : -f 1)"
4985 _debug _end_n
"$_end_n"
4986 sed -n "1,${_end_n}p" "$_fullchainf" >"$_certf"
4987 _end_n
="$(_math $_end_n + 1)"
4988 sed -n "${_end_n},9999p" "$_fullchainf" >"$_caf"
4995 if [ -z "$Le_Domain" ]; then
4996 _usage
"Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
5002 _initpath
"$Le_Domain" "$_isEcc"
5004 _info
"$(__green "Renew
: '$Le_Domain'")"
5005 if [ ! -f "$DOMAIN_CONF" ]; then
5006 _info
"'$Le_Domain' is not a issued domain, skip."
5010 if [ "$Le_RenewalDays" ]; then
5011 _savedomainconf Le_RenewalDays
"$Le_RenewalDays"
5015 _debug Le_API
"$Le_API"
5017 if [ "$Le_API" = "$LETSENCRYPT_CA_V1" ]; then
5018 _cleardomainconf Le_API
5019 Le_API
="$DEFAULT_CA"
5021 if [ "$Le_API" = "$LETSENCRYPT_STAGING_CA_V1" ]; then
5022 _cleardomainconf Le_API
5023 Le_API
="$DEFAULT_STAGING_CA"
5026 if [ "$Le_API" ]; then
5027 export ACME_DIRECTORY
="$Le_API"
5030 ACCOUNT_JSON_PATH
=""
5032 _debug3
"initpath again."
5033 _initpath
"$Le_Domain" "$_isEcc"
5036 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
5037 _info
"Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
5038 _info
"Add '$(__red '--force')' to force to renew."
5039 return "$RENEW_SKIP"
5042 if [ "$_ACME_IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
5043 _info
"Skip invalid cert for: $Le_Domain"
5048 Le_ReloadCmd
="$(_readdomainconf Le_ReloadCmd)"
5049 Le_PreHook
="$(_readdomainconf Le_PreHook)"
5050 Le_PostHook
="$(_readdomainconf Le_PostHook)"
5051 Le_RenewHook
="$(_readdomainconf Le_RenewHook)"
5052 Le_Preferred_Chain
="$(_readdomainconf Le_Preferred_Chain)"
5053 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"
5055 if [ "$res" != "0" ]; then
5059 if [ "$Le_DeployHook" ]; then
5060 _deploy
"$Le_Domain" "$Le_DeployHook"
5069 #renewAll [stopRenewOnError]
5072 _stopRenewOnError
="$1"
5073 _debug
"_stopRenewOnError" "$_stopRenewOnError"
5078 _error_level
=$NOTIFY_LEVEL_SKIP
5079 _notify_code
=$RENEW_SKIP
5080 _set_level
=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
5081 _debug
"_set_level" "$_set_level"
5082 for di
in "${CERT_HOME}"/*.
*/; do
5084 if ! [ -d "$di" ]; then
5085 _debug
"Not directory, skip: $di"
5091 if _endswith
"$d" "$ECC_SUFFIX"; then
5092 _isEcc
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 2)
5093 d
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 1)
5095 renew
"$d" "$_isEcc"
5098 _debug
"Return code: $rc"
5099 if [ "$rc" = "0" ]; then
5100 if [ $_error_level -gt $NOTIFY_LEVEL_RENEW ]; then
5101 _error_level
="$NOTIFY_LEVEL_RENEW"
5104 if [ "$_ACME_IN_CRON" ]; then
5105 if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then
5106 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5107 _send_notify
"Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0
5111 _success_msg
="${_success_msg} $d
5113 elif [ "$rc" = "$RENEW_SKIP" ]; then
5114 if [ $_error_level -gt $NOTIFY_LEVEL_SKIP ]; then
5115 _error_level
="$NOTIFY_LEVEL_SKIP"
5116 _notify_code
=$RENEW_SKIP
5118 if [ "$_ACME_IN_CRON" ]; then
5119 if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then
5120 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5121 _send_notify
"Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP"
5126 _skipped_msg
="${_skipped_msg} $d
5129 if [ $_error_level -gt $NOTIFY_LEVEL_ERROR ]; then
5130 _error_level
="$NOTIFY_LEVEL_ERROR"
5133 if [ "$_ACME_IN_CRON" ]; then
5134 if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then
5135 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5136 _send_notify
"Renew $d error" "There is an error." "$NOTIFY_HOOK" 1
5140 _error_msg
="${_error_msg} $d
5142 if [ "$_stopRenewOnError" ]; then
5143 _err
"Error renew $d, stop now."
5148 _err
"Error renew $d."
5152 _debug _error_level
"$_error_level"
5153 _debug _set_level
"$_set_level"
5154 if [ "$_ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then
5155 if [ -z "$NOTIFY_MODE" ] ||
[ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then
5156 _msg_subject
="Renew"
5157 if [ "$_error_msg" ]; then
5158 _msg_subject
="${_msg_subject} Error"
5159 _msg_data
="Error certs:
5163 if [ "$_success_msg" ]; then
5164 _msg_subject
="${_msg_subject} Success"
5165 _msg_data
="${_msg_data}Success certs:
5169 if [ "$_skipped_msg" ]; then
5170 _msg_subject
="${_msg_subject} Skipped"
5171 _msg_data
="${_msg_data}Skipped certs:
5176 _send_notify
"$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" "$_notify_code"
5187 if [ -z "$_csrfile" ] ||
[ -z "$_csrW" ]; then
5188 _usage
"Usage: $PROJECT_ENTRY --signcsr --csr mycsr.csr -w /path/to/webroot/a.com/ "
5196 _real_fullchain
="$7"
5201 _challenge_alias
="${12}"
5203 _csrsubj
=$
(_readSubjectFromCSR
"$_csrfile")
5204 if [ "$?" != "0" ]; then
5205 _err
"Can not read subject from csr: $_csrfile"
5208 _debug _csrsubj
"$_csrsubj"
5209 if _contains
"$_csrsubj" ' ' ||
! _contains
"$_csrsubj" '.'; then
5210 _info
"It seems that the subject: $_csrsubj is not a valid domain name. Drop it."
5214 _csrdomainlist
=$
(_readSubjectAltNamesFromCSR
"$_csrfile")
5215 if [ "$?" != "0" ]; then
5216 _err
"Can not read domain list from csr: $_csrfile"
5219 _debug
"_csrdomainlist" "$_csrdomainlist"
5221 if [ -z "$_csrsubj" ]; then
5222 _csrsubj
="$(_getfield "$_csrdomainlist" 1)"
5223 _debug _csrsubj
"$_csrsubj"
5224 _csrdomainlist
="$(echo "$_csrdomainlist" | cut -d , -f 2-)"
5225 _debug
"_csrdomainlist" "$_csrdomainlist"
5228 if [ -z "$_csrsubj" ]; then
5229 _err
"Can not read subject from csr: $_csrfile"
5233 _csrkeylength
=$
(_readKeyLengthFromCSR
"$_csrfile")
5234 if [ "$?" != "0" ] ||
[ -z "$_csrkeylength" ]; then
5235 _err
"Can not read key length from csr: $_csrfile"
5239 if [ -z "$ACME_VERSION" ] && _contains
"$_csrsubj,$_csrdomainlist" "*."; then
5240 export ACME_VERSION
=2
5242 _initpath
"$_csrsubj" "$_csrkeylength"
5243 mkdir
-p "$DOMAIN_PATH"
5245 _info
"Copy csr to: $CSR_PATH"
5246 cp "$_csrfile" "$CSR_PATH"
5248 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"
5255 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
5256 _usage
"Usage: $PROJECT_ENTRY --showcsr --csr mycsr.csr"
5262 _csrsubj
=$
(_readSubjectFromCSR
"$_csrfile")
5263 if [ "$?" != "0" ] ||
[ -z "$_csrsubj" ]; then
5264 _err
"Can not read subject from csr: $_csrfile"
5268 _info
"Subject=$_csrsubj"
5270 _csrdomainlist
=$
(_readSubjectAltNamesFromCSR
"$_csrfile")
5271 if [ "$?" != "0" ]; then
5272 _err
"Can not read domain list from csr: $_csrfile"
5275 _debug
"_csrdomainlist" "$_csrdomainlist"
5277 _info
"SubjectAltNames=$_csrdomainlist"
5279 _csrkeylength
=$
(_readKeyLengthFromCSR
"$_csrfile")
5280 if [ "$?" != "0" ] ||
[ -z "$_csrkeylength" ]; then
5281 _err
"Can not read key length from csr: $_csrfile"
5284 _info
"KeyLength=$_csrkeylength"
5294 if [ "$_raw" ]; then
5295 if [ -z "$_domain" ]; then
5296 printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}CA${_sep}Created${_sep}Renew"
5298 for di
in "${CERT_HOME}"/*.
*/; do
5302 if _endswith
"$d" "$ECC_SUFFIX"; then
5304 d
=$
(echo "$d" | cut
-d "$ECC_SEP" -f 1)
5306 DOMAIN_CONF
="$di/$d.conf"
5307 if [ -f "$DOMAIN_CONF" ]; then
5309 _ca
="$(_getCAShortName "$Le_API")"
5310 if [ -z "$_domain" ]; then
5311 printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
5313 if [ "$_domain" = "$d" ]; then
5321 if _exists
column; then
5322 list
"raw" "$_domain" |
column -t -s "$_sep"
5324 list
"raw" "$_domain" |
tr "$_sep" '\t'
5334 for _d_api
in $
(echo "$_hooks" |
tr ',' " "); do
5335 _deployApi
="$(_findHook "$_d" $_SUB_FOLDER_DEPLOY "$_d_api")"
5336 if [ -z "$_deployApi" ]; then
5337 _err
"The deploy hook $_d_api is not found."
5340 _debug _deployApi
"$_deployApi"
5343 if ! .
"$_deployApi"; then
5344 _err
"Load file $_deployApi error. Please check your api file and try again."
5348 d_command
="${_d_api}_deploy"
5349 if ! _exists
"$d_command"; then
5350 _err
"It seems that your api file is not correct, it must have a function named: $d_command"
5354 if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
5355 _err
"Error deploy for domain:$_d"
5359 _err
"Deploy error."
5362 _info
"$(__green Success)"
5372 if [ -z "$_hooks" ]; then
5373 _usage
"Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
5377 _initpath
"$_d" "$_isEcc"
5378 if [ ! -d "$DOMAIN_PATH" ]; then
5379 _err
"The domain '$_d' is not a cert name. You must use the cert name to specify the cert to install."
5380 _err
"Can not find path:'$DOMAIN_PATH'"
5386 _savedomainconf Le_DeployHook
"$_hooks"
5388 _deploy
"$_d" "$_hooks"
5393 if [ -z "$_main_domain" ]; then
5394 _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]"
5402 _real_fullchain
="$6"
5405 _initpath
"$_main_domain" "$_isEcc"
5406 if [ ! -d "$DOMAIN_PATH" ]; then
5407 _err
"The domain '$_main_domain' is not a cert name. You must use the cert name to specify the cert to install."
5408 _err
"Can not find path:'$DOMAIN_PATH'"
5412 _savedomainconf
"Le_RealCertPath" "$_real_cert"
5413 _savedomainconf
"Le_RealCACertPath" "$_real_ca"
5414 _savedomainconf
"Le_RealKeyPath" "$_real_key"
5415 _savedomainconf
"Le_ReloadCmd" "$_reload_cmd" "base64"
5416 _savedomainconf
"Le_RealFullChainPath" "$_real_fullchain"
5418 _installcert
"$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
5421 #domain cert key ca fullchain reloadcmd backup-prefix
5427 _real_fullchain
="$5"
5431 if [ "$_real_cert" = "$NO_VALUE" ]; then
5434 if [ "$_real_key" = "$NO_VALUE" ]; then
5437 if [ "$_real_ca" = "$NO_VALUE" ]; then
5440 if [ "$_reload_cmd" = "$NO_VALUE" ]; then
5443 if [ "$_real_fullchain" = "$NO_VALUE" ]; then
5447 _backup_path
="$DOMAIN_BACKUP_PATH/$_backup_prefix"
5448 mkdir
-p "$_backup_path"
5450 if [ "$_real_cert" ]; then
5451 _info
"Installing cert to:$_real_cert"
5452 if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then
5453 cp "$_real_cert" "$_backup_path/cert.bak"
5455 cat "$CERT_PATH" >"$_real_cert" ||
return 1
5458 if [ "$_real_ca" ]; then
5459 _info
"Installing CA to:$_real_ca"
5460 if [ "$_real_ca" = "$_real_cert" ]; then
5461 echo "" >>"$_real_ca"
5462 cat "$CA_CERT_PATH" >>"$_real_ca" ||
return 1
5464 if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then
5465 cp "$_real_ca" "$_backup_path/ca.bak"
5467 cat "$CA_CERT_PATH" >"$_real_ca" ||
return 1
5471 if [ "$_real_key" ]; then
5472 _info
"Installing key to:$_real_key"
5473 if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then
5474 cp "$_real_key" "$_backup_path/key.bak"
5476 if [ -f "$_real_key" ]; then
5477 cat "$CERT_KEY_PATH" >"$_real_key" ||
return 1
5479 cat "$CERT_KEY_PATH" >"$_real_key" ||
return 1
5480 chmod 600 "$_real_key"
5484 if [ "$_real_fullchain" ]; then
5485 _info
"Installing full chain to:$_real_fullchain"
5486 if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then
5487 cp "$_real_fullchain" "$_backup_path/fullchain.bak"
5489 cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" ||
return 1
5492 if [ "$_reload_cmd" ]; then
5493 _info
"Run reload cmd: $_reload_cmd"
5496 export CERT_KEY_PATH
5498 export CERT_FULLCHAIN_PATH
5499 export Le_Domain
="$_main_domain"
5500 cd "$DOMAIN_PATH" && eval "$_reload_cmd"
5502 _info
"$(__green "Reload success
")"
5504 _err
"Reload error for :$Le_Domain"
5512 prompt
="Enter Password:"
5513 while IFS
= read -p "$prompt" -r -s -n 1 char
; do
5514 if [ "$char" = $
'\0' ]; then
5523 _install_win_taskscheduler
() {
5527 if ! _exists cygpath
; then
5528 _err
"cygpath not found"
5531 if ! _exists schtasks
; then
5532 _err
"schtasks.exe is not found, are you on Windows?"
5535 _winbash
="$(cygpath -w $(which bash))"
5536 _debug _winbash
"$_winbash"
5537 if [ -z "$_winbash" ]; then
5538 _err
"can not find bash path"
5542 _debug
"_myname" "$_myname"
5543 if [ -z "$_myname" ]; then
5544 _err
"can not find my user name"
5547 _debug
"_lesh" "$_lesh"
5549 _info
"To install scheduler task in your Windows account, you must input your windows password."
5550 _info
"$PROJECT_NAME doesn't save your password."
5551 _info
"Please input your Windows password for: $(__green "$_myname")"
5552 _password
="$(__read_password)"
5553 #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
5554 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
5559 _uninstall_win_taskscheduler
() {
5560 if ! _exists schtasks
; then
5561 _err
"schtasks.exe is not found, are you on Windows?"
5564 if ! echo SCHTASKS
/query
/tn
"$_WINDOWS_SCHEDULER_NAME" | cmd.exe
>/dev
/null
; then
5565 _debug
"scheduler $_WINDOWS_SCHEDULER_NAME is not found."
5567 _info
"Removing $_WINDOWS_SCHEDULER_NAME"
5568 echo SCHTASKS
/delete
/f
/tn
"$_WINDOWS_SCHEDULER_NAME" | cmd.exe
>/dev
/null
5577 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
5578 lesh
="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
5580 _err
"Can not install cronjob, $PROJECT_ENTRY not found."
5583 if [ "$_c_home" ]; then
5584 _c_entry
="--config-home \"$_c_home\" "
5587 random_minute
=$
(_math
$_t % 60)
5589 if ! _exists
"$_CRONTAB" && _exists
"fcrontab"; then
5593 if ! _exists
"$_CRONTAB"; then
5594 if _exists cygpath
&& _exists schtasks.exe
; then
5595 _info
"It seems you are on Windows, let's install Windows scheduler task."
5596 if _install_win_taskscheduler
"$lesh" "$_c_entry" "$random_minute"; then
5597 _info
"Install Windows scheduler task success."
5600 _err
"Install Windows scheduler task failed."
5604 _err
"crontab/fcrontab doesn't exist, so, we can not install cron jobs."
5605 _err
"All your certs will not be renewed automatically."
5606 _err
"You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
5609 _info
"Installing cron job"
5610 if ! $_CRONTAB -l |
grep "$PROJECT_ENTRY --cron"; then
5611 if _exists uname
&& uname
-a |
grep SunOS
>/dev
/null
; then
5614 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
5619 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
5623 if [ "$?" != "0" ]; then
5624 _err
"Install cron job failed. You need to manually renew your certs."
5625 _err
"Or you can add cronjob by yourself:"
5626 _err
"$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
5631 uninstallcronjob
() {
5633 if ! _exists
"$_CRONTAB" && _exists
"fcrontab"; then
5637 if ! _exists
"$_CRONTAB"; then
5638 if _exists cygpath
&& _exists schtasks.exe
; then
5639 _info
"It seems you are on Windows, let's uninstall Windows scheduler task."
5640 if _uninstall_win_taskscheduler
; then
5641 _info
"Uninstall Windows scheduler task success."
5644 _err
"Uninstall Windows scheduler task failed."
5650 _info
"Removing cron job"
5651 cr
="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
5653 if _exists uname
&& uname
-a |
grep solaris
>/dev
/null
; then
5654 $_CRONTAB -l |
sed "/$PROJECT_ENTRY --cron/d" |
$_CRONTAB --
5656 $_CRONTAB -l |
sed "/$PROJECT_ENTRY --cron/d" |
$_CRONTAB -
5658 LE_WORKING_DIR
="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
5659 _info LE_WORKING_DIR "$LE_WORKING_DIR"
5660 if _contains "$cr" "--config-home"; then
5661 LE_CONFIG_HOME="$(echo "$cr" | cut -d ' ' -f 11 | tr -d '"')"
5662 _debug LE_CONFIG_HOME
"$LE_CONFIG_HOME"
5669 #domain isECC revokeReason
5672 if [ -z "$Le_Domain" ]; then
5673 _usage
"Usage: $PROJECT_ENTRY --revoke -d domain.com [--ecc]"
5679 if [ -z "$_reason" ]; then
5682 _initpath
"$Le_Domain" "$_isEcc"
5683 if [ ! -f "$DOMAIN_CONF" ]; then
5684 _err
"$Le_Domain is not a issued domain, skip."
5688 if [ ! -f "$CERT_PATH" ]; then
5689 _err
"Cert for $Le_Domain $CERT_PATH is not found, skip."
5693 cert
="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
5695 if [ -z "$cert" ]; then
5696 _err
"Cert for $Le_Domain is empty found, skip."
5702 if [ "$ACME_VERSION" = "2" ]; then
5703 data
="{\"certificate\": \"$cert\",\"reason\":$_reason}"
5705 data
="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
5707 uri
="${ACME_REVOKE_CERT}"
5709 if [ -f "$CERT_KEY_PATH" ]; then
5710 _info
"Try domain key first."
5711 if _send_signed_request
"$uri" "$data" "" "$CERT_KEY_PATH"; then
5712 if [ -z "$response" ]; then
5713 _info
"Revoke success."
5717 _err
"Revoke error by domain key."
5722 _info
"Domain key file doesn't exist."
5725 _info
"Try account key."
5727 if _send_signed_request
"$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then
5728 if [ -z "$response" ]; then
5729 _info
"Revoke success."
5733 _err
"Revoke error."
5743 if [ -z "$Le_Domain" ]; then
5744 _usage
"Usage: $PROJECT_ENTRY --remove -d domain.com [--ecc]"
5750 _initpath
"$Le_Domain" "$_isEcc"
5751 _removed_conf
="$DOMAIN_CONF.removed"
5752 if [ ! -f "$DOMAIN_CONF" ]; then
5753 if [ -f "$_removed_conf" ]; then
5754 _err
"$Le_Domain is already removed, You can remove the folder by yourself: $DOMAIN_PATH"
5756 _err
"$Le_Domain is not a issued domain, skip."
5761 if mv "$DOMAIN_CONF" "$_removed_conf"; then
5762 _info
"$Le_Domain is removed, the key and cert files are in $(__green $DOMAIN_PATH)"
5763 _info
"You can remove them by yourself."
5766 _err
"Remove $Le_Domain failed."
5777 if [ "$ACME_VERSION" = "2" ]; then
5778 _identifiers
="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
5779 if ! _send_signed_request
"$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
5780 _err
"Can not get domain new order."
5783 _authorizations_seg
="$(echo "$response" | _egrep_o '"authorizations
" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
5784 _debug2 _authorizations_seg "$_authorizations_seg"
5785 if [ -z "$_authorizations_seg" ]; then
5786 _err "_authorizations_seg not found."
5788 _on_issue_err "$_post_hook"
5792 authzUri="$_authorizations_seg"
5793 _debug2 "authzUri" "$authzUri"
5794 if ! _send_signed_request "$authzUri"; then
5795 _err "get to authz error."
5796 _err "_authorizations_seg" "$_authorizations_seg"
5797 _err "authzUri" "$authzUri"
5799 _on_issue_err "$_post_hook"
5803 response="$(echo "$response" | _normalizeJson)"
5804 _debug2 response "$response"
5807 if ! __get_domain_new_authz "$_d_domain"; then
5808 _err "Can not get domain new authz token."
5812 authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
5813 _debug "authzUri" "$authzUri"
5814 if [ "$code" ] && [ ! "$code" = '201' ]; then
5815 _err "new-authz error: $response"
5821 entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
5822 if [ -z "$entries" ]; then
5823 _info "No valid entries found."
5824 if [ -z "$thumbprint" ]; then
5825 thumbprint="$(__calc_account_thumbprint)"
5827 _debug "Trigger validation."
5829 entry="$(echo "$response" | _egrep_o '[^\
{]*"type":"'$vtype'"[^\
}]*')"
5830 _debug entry "$entry"
5831 if [ -z "$entry" ]; then
5832 _err "Error, can not get domain token $d"
5835 token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
5836 _debug token
"$token"
5838 uri
="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^
\"]*" | cut -d : -f 2,3 | tr -d '"')"
5841 keyauthorization="$token.$thumbprint"
5842 _debug keyauthorization "$keyauthorization"
5843 __trigger_validation "$uri" "$keyauthorization"
5848 _d_max_retry=$(echo "$entries" | wc -l)
5849 while [ "$_d_i" -lt "$_d_max_retry" ]; do
5850 _info "Deactivate: $_d_domain"
5851 _d_i="$(_math $_d_i + 1)"
5852 entry="$(echo "$entries" | sed -n "${_d_i}p")"
5853 _debug entry "$entry"
5855 if [ -z "$entry" ]; then
5856 _info "No more valid entry found."
5860 _vtype="$(echo "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')"
5861 _debug _vtype "$_vtype"
5862 _info "Found $_vtype"
5864 uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
5867 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
5868 _info
"Skip $_vtype"
5872 _info
"Deactivate: $_vtype"
5874 if [ "$ACME_VERSION" = "2" ]; then
5875 _djson
="{\"status\":\"deactivated\"}"
5877 _djson
="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
5880 if _send_signed_request
"$authzUri" "$_djson" && _contains
"$response" '"deactivated"'; then
5881 _info
"Deactivate: $_vtype success."
5883 _err
"Can not deactivate $_vtype."
5889 if [ "$_d_i" -eq "$_d_max_retry" ]; then
5890 _info
"Deactivated success!"
5892 _err
"Deactivate failed."
5902 _debug _d_domain_list
"$_d_domain_list"
5903 if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then
5904 _usage
"Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
5907 for _d_dm
in $
(echo "$_d_domain_list" |
tr ',' ' '); do
5908 if [ -z "$_d_dm" ] ||
[ "$_d_dm" = "$NO_VALUE" ]; then
5911 if ! _deactivate
"$_d_dm" "$_d_type"; then
5917 # Detect profile file if not specified as environment variable
5919 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
5925 SHELLTYPE
="$(basename "/$SHELL")"
5927 if [ "$SHELLTYPE" = "bash" ]; then
5928 if [ -f "$HOME/.bashrc" ]; then
5929 DETECTED_PROFILE
="$HOME/.bashrc"
5930 elif [ -f "$HOME/.bash_profile" ]; then
5931 DETECTED_PROFILE
="$HOME/.bash_profile"
5933 elif [ "$SHELLTYPE" = "zsh" ]; then
5934 DETECTED_PROFILE
="$HOME/.zshrc"
5937 if [ -z "$DETECTED_PROFILE" ]; then
5938 if [ -f "$HOME/.profile" ]; then
5939 DETECTED_PROFILE
="$HOME/.profile"
5940 elif [ -f "$HOME/.bashrc" ]; then
5941 DETECTED_PROFILE
="$HOME/.bashrc"
5942 elif [ -f "$HOME/.bash_profile" ]; then
5943 DETECTED_PROFILE
="$HOME/.bash_profile"
5944 elif [ -f "$HOME/.zshrc" ]; then
5945 DETECTED_PROFILE
="$HOME/.zshrc"
5949 echo "$DETECTED_PROFILE"
5954 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
5957 #LOG_FILE=\"$DEFAULT_LOG_FILE\"
5964 " >"$ACCOUNT_CONF_PATH"
5972 if ! _exists
"curl" && ! _exists
"wget"; then
5973 _err
"Please install curl or wget first, we need to access http resources."
5977 if [ -z "$_nocron" ]; then
5978 if ! _exists
"crontab" && ! _exists
"fcrontab"; then
5979 if _exists cygpath
&& _exists schtasks.exe
; then
5980 _info
"It seems you are on Windows, we will install Windows scheduler task."
5982 _err
"It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
5983 _err
"We need to set cron job to renew the certs automatically."
5984 _err
"Otherwise, your certs will not be able to be renewed automatically."
5985 if [ -z "$FORCE" ]; then
5986 _err
"Please add '--force' and try install again to go without crontab."
5987 _err
"./$PROJECT_ENTRY --install --force"
5994 if ! _exists
"${ACME_OPENSSL_BIN:-openssl}"; then
5995 _err
"Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
5996 _err
"We need openssl to generate keys."
6000 if ! _exists
"socat"; then
6001 _err
"It is recommended to install socat first."
6002 _err
"We use socat for standalone server if you use standalone mode."
6003 _err
"If you don't use standalone mode, just ignore this warning."
6012 if [ -z "$_shebang" ]; then
6013 _usage
"Usage: file shebang"
6016 cp "$_file" "$_file.tmp"
6017 echo "$_shebang" >"$_file"
6018 sed -n 2,99999p
"$_file.tmp" >>"$_file"
6027 _envfile
="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
6028 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ]; then
6029 echo "$(cat "$_envfile")" |
sed "s|^LE_WORKING_DIR.*$||" >"$_envfile"
6030 echo "$(cat "$_envfile")" |
sed "s|^alias le.*$||" >"$_envfile"
6031 echo "$(cat "$_envfile")" |
sed "s|^alias le.sh.*$||" >"$_envfile"
6034 if [ "$_c_home" ]; then
6035 _c_entry
=" --config-home '$_c_home'"
6038 _setopt
"$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
6039 if [ "$_c_home" ]; then
6040 _setopt
"$_envfile" "export LE_CONFIG_HOME" "=" "\"$LE_CONFIG_HOME\""
6042 _sed_i
"/^export LE_CONFIG_HOME/d" "$_envfile"
6044 _setopt
"$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6046 _profile
="$(_detect_profile)"
6047 if [ "$_profile" ]; then
6048 _debug
"Found profile: $_profile"
6049 _info
"Installing alias to '$_profile'"
6050 _setopt
"$_profile" ". \"$_envfile\""
6051 _info
"OK, Close and reopen your terminal to start using $PROJECT_NAME"
6053 _info
"No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
6057 _cshfile
="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
6058 _csh_profile
="$HOME/.cshrc"
6059 if [ -f "$_csh_profile" ]; then
6060 _info
"Installing alias to '$_csh_profile'"
6061 _setopt
"$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
6062 if [ "$_c_home" ]; then
6063 _setopt
"$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
6065 _sed_i
"/^setenv LE_CONFIG_HOME/d" "$_cshfile"
6067 _setopt
"$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6068 _setopt
"$_csh_profile" "source \"$_cshfile\""
6072 _tcsh_profile
="$HOME/.tcshrc"
6073 if [ -f "$_tcsh_profile" ]; then
6074 _info
"Installing alias to '$_tcsh_profile'"
6075 _setopt
"$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
6076 if [ "$_c_home" ]; then
6077 _setopt
"$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
6079 _setopt
"$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
6080 _setopt
"$_tcsh_profile" "source \"$_cshfile\""
6085 # nocron confighome noprofile
6088 if [ -z "$LE_WORKING_DIR" ]; then
6089 LE_WORKING_DIR
="$DEFAULT_INSTALL_HOME"
6095 if ! _initpath
; then
6096 _err
"Install failed."
6099 if [ "$_nocron" ]; then
6100 _debug
"Skip install cron job"
6103 if [ "$_ACME_IN_CRON" != "1" ]; then
6104 if ! _precheck
"$_nocron"; then
6105 _err
"Pre-check failed, can not install."
6110 if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
6111 _info
"Using config home: $LE_CONFIG_HOME"
6112 _c_home
="$LE_CONFIG_HOME"
6116 if [ -d "$HOME/.le" ]; then
6117 for envfile
in "le.env" "le.sh.env"; do
6118 if [ -f "$HOME/.le/$envfile" ]; then
6119 if grep "le.sh" "$HOME/.le/$envfile" >/dev
/null
; then
6121 _info
"You are upgrading from le.sh"
6122 _info
"Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
6123 mv "$HOME/.le" "$LE_WORKING_DIR"
6124 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
6131 _info
"Installing to $LE_WORKING_DIR"
6133 if [ ! -d "$LE_WORKING_DIR" ]; then
6134 if ! mkdir
-p "$LE_WORKING_DIR"; then
6135 _err
"Can not create working dir: $LE_WORKING_DIR"
6139 chmod 700 "$LE_WORKING_DIR"
6142 if [ ! -d "$LE_CONFIG_HOME" ]; then
6143 if ! mkdir
-p "$LE_CONFIG_HOME"; then
6144 _err
"Can not create config dir: $LE_CONFIG_HOME"
6148 chmod 700 "$LE_CONFIG_HOME"
6151 cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x
"$LE_WORKING_DIR/$PROJECT_ENTRY"
6153 if [ "$?" != "0" ]; then
6154 _err
"Install failed, can not copy $PROJECT_ENTRY"
6158 _info
"Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
6160 if [ "$_ACME_IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
6161 _installalias
"$_c_home"
6164 for subf
in $_SUB_FOLDERS; do
6165 if [ -d "$subf" ]; then
6166 mkdir
-p "$LE_WORKING_DIR/$subf"
6167 cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
6171 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
6175 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ]; then
6176 _setopt
"$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
6179 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]; then
6180 _saveaccountconf
"CERT_HOME" "$CERT_HOME"
6183 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]; then
6184 _saveaccountconf
"ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
6187 if [ -z "$_nocron" ]; then
6188 installcronjob
"$_c_home"
6191 if [ -z "$NO_DETECT_SH" ]; then
6193 if _exists bash
; then
6194 _bash_path
="$(bash -c "command -v bash
2>/dev
/null
")"
6195 if [ -z "$_bash_path" ]; then
6196 _bash_path
="$(bash -c 'echo $SHELL')"
6199 if [ "$_bash_path" ]; then
6200 _info
"Good, bash is found, so change the shebang to use bash as preferred."
6201 _shebang
='#!'"$_bash_path"
6202 _setShebang
"$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
6203 for subf
in $_SUB_FOLDERS; do
6204 if [ -d "$LE_WORKING_DIR/$subf" ]; then
6205 for _apifile
in "$LE_WORKING_DIR/$subf/"*.sh
; do
6206 _setShebang
"$_apifile" "$_shebang"
6219 if [ -z "$_nocron" ]; then
6226 rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
6227 _info
"The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\", you can remove them by yourself."
6234 _profile
="$(_detect_profile)"
6235 if [ "$_profile" ]; then
6236 _info
"Uninstalling alias from: '$_profile'"
6237 text
="$(cat "$_profile")"
6238 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" >"$_profile"
6241 _csh_profile
="$HOME/.cshrc"
6242 if [ -f "$_csh_profile" ]; then
6243 _info
"Uninstalling alias from: '$_csh_profile'"
6244 text
="$(cat "$_csh_profile")"
6245 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_csh_profile"
6248 _tcsh_profile
="$HOME/.tcshrc"
6249 if [ -f "$_tcsh_profile" ]; then
6250 _info
"Uninstalling alias from: '$_csh_profile'"
6251 text
="$(cat "$_tcsh_profile")"
6252 echo "$text" |
sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_tcsh_profile"
6258 export _ACME_IN_CRON
=1
6260 _info
"$(__green "===Starting cron
===")"
6261 if [ "$AUTO_UPGRADE" = "1" ]; then
6262 export LE_WORKING_DIR
6265 _err
"Cron:Upgrade failed!"
6269 .
"$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev
/null
6275 _info
"Auto upgraded to: $VER"
6280 _info
"$(__green "===End cron
===")"
6289 # subject content hooks code
6296 if [ "$NOTIFY_LEVEL" = "$NOTIFY_LEVEL_DISABLE" ]; then
6297 _debug
"The NOTIFY_LEVEL is $NOTIFY_LEVEL, disabled, just return."
6301 if [ -z "$_nhooks" ]; then
6302 _debug
"The NOTIFY_HOOK is empty, just return."
6307 for _n_hook
in $
(echo "$_nhooks" |
tr ',' " "); do
6308 _n_hook_file
="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")"
6309 _info
"Sending via: $_n_hook"
6310 _debug
"Found $_n_hook_file for $_n_hook"
6311 if [ -z "$_n_hook_file" ]; then
6312 _err
"Can not find the hook file for $_n_hook"
6316 if ! .
"$_n_hook_file"; then
6317 _err
"Load file $_n_hook_file error. Please check your api file and try again."
6321 d_command
="${_n_hook}_send"
6322 if ! _exists
"$d_command"; then
6323 _err
"It seems that your api file is not correct, it must have a function named: $d_command"
6327 if ! $d_command "$_nsubject" "$_ncontent" "$_nerror"; then
6328 _err
"Error send message by $d_command"
6334 _err
"Set $_n_hook_file error."
6337 _info
"$_n_hook $(__green Success)"
6345 _set_notify_hook
() {
6348 _test_subject
="Hello, this is a notification from $PROJECT_NAME"
6349 _test_content
="If you receive this message, your notification works."
6351 _send_notify
"$_test_subject" "$_test_content" "$_nhooks" 0
6355 #[hook] [level] [mode]
6363 if [ -z "$_nhook$_nlevel$_nmode" ]; then
6364 _usage
"Usage: $PROJECT_ENTRY --set-notify [--notify-hook mailgun] [--notify-level $NOTIFY_LEVEL_DEFAULT] [--notify-mode $NOTIFY_MODE_DEFAULT]"
6365 _usage
"$_NOTIFY_WIKI"
6369 if [ "$_nlevel" ]; then
6370 _info
"Set notify level to: $_nlevel"
6371 export "NOTIFY_LEVEL=$_nlevel"
6372 _saveaccountconf
"NOTIFY_LEVEL" "$NOTIFY_LEVEL"
6375 if [ "$_nmode" ]; then
6376 _info
"Set notify mode to: $_nmode"
6377 export "NOTIFY_MODE=$_nmode"
6378 _saveaccountconf
"NOTIFY_MODE" "$NOTIFY_MODE"
6381 if [ "$_nhook" ]; then
6382 _info
"Set notify hook to: $_nhook"
6383 if [ "$_nhook" = "$NO_VALUE" ]; then
6384 _info
"Clear notify hook"
6385 _clearaccountconf
"NOTIFY_HOOK"
6387 if _set_notify_hook
"$_nhook"; then
6388 export NOTIFY_HOOK
="$_nhook"
6389 _saveaccountconf
"NOTIFY_HOOK" "$NOTIFY_HOOK"
6392 _err
"Can not set notify hook to: $_nhook"
6403 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
6405 --help, -h Show this help message.
6406 --version, -v Show version info.
6407 --install Install $PROJECT_NAME to your system.
6408 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
6409 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
6410 --issue Issue a cert.
6411 --signcsr Issue a cert from an existing csr.
6412 --deploy Deploy the cert to your server.
6413 --install-cert Install the issued cert to apache/nginx or any other server.
6414 --renew, -r Renew a cert.
6415 --renew-all Renew all the certs.
6416 --revoke Revoke a cert.
6417 --remove Remove the cert from list of certs known to $PROJECT_NAME.
6418 --list List all the certs.
6419 --showcsr Show the content of a csr.
6420 --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.
6421 --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
6422 --cron Run cron job to renew all the certs.
6423 --toPkcs Export the certificate and key to a pfx file.
6424 --toPkcs8 Convert to pkcs8 format.
6425 --update-account Update account info.
6426 --register-account Register account key.
6427 --deactivate-account Deactivate the account.
6428 --create-account-key Create an account private key, professional use.
6429 --create-domain-key Create an domain private key, professional use.
6430 --createCSR, -ccsr Create CSR , professional use.
6431 --deactivate Deactivate the domain authz, professional use.
6432 --set-notify Set the cron notification hook, level or mode.
6433 --set-default-ca Used with '--server' , to set the default CA to use to use.
6437 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
6438 --challenge-alias domain.tld The challenge domain alias for DNS alias mode.
6439 See: $_DNS_ALIAS_WIKI
6441 --domain-alias domain.tld The domain alias for DNS alias mode.
6442 See: $_DNS_ALIAS_WIKI
6444 --preferred-chain CHAIN If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
6445 If no match, the default offered chain will be used. (default: empty)
6446 See: $_PREFERRED_CHAIN_WIKI
6448 --force, -f Used to force to install or force to renew a cert immediately.
6449 --staging, --test Use staging server, just for test.
6450 --debug Output debug info.
6451 --output-insecure Output all the sensitive messages.
6452 By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
6454 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
6455 --standalone Use standalone mode.
6456 --alpn Use standalone alpn mode.
6457 --stateless Use stateless mode.
6458 See: $_STATELESS_WIKI
6460 --apache Use apache mode.
6461 --dns [dns_hook] Use dns mode or dns api.
6464 --dnssleep 300 The time in seconds to wait for all the txt records to propagate in dns api mode.
6465 It's not necessary to use this by default, $PROJECT_NAME polls dns status by DOH automatically.
6467 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
6468 --accountkeylength, -ak [2048] Specifies the account key length: 2048, 3072, 4096
6469 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
6470 --log-level 1|2 Specifies the log level, default is 1.
6471 --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
6473 --eab-kid EAB_KID Key Identifier for External Account Binding.
6474 --eab-hmac-key EAB_HMAC_KEY HMAC key for External Account Binding.
6477 These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
6479 --cert-file After issue/renew, the cert will be copied to this path.
6480 --key-file After issue/renew, the key will be copied to this path.
6481 --ca-file After issue/renew, the intermediate cert will be copied to this path.
6482 --fullchain-file After issue/renew, the fullchain cert will be copied to this path.
6484 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
6486 --server SERVER ACME Directory Resource URI. (default: $DEFAULT_CA)
6489 --accountconf Specifies a customized account config file.
6490 --home Specifies the home dir for $PROJECT_NAME.
6491 --cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
6492 --config-home Specifies the home dir to save all the configurations.
6493 --useragent Specifies the user agent string. it will be saved for future use too.
6494 --accountemail, -m Specifies the account email, only valid for the '--install' and '--update-account' command.
6495 --accountkey Specifies the account key path, only valid for the '--install' command.
6496 --days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
6497 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
6498 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
6499 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
6500 --listraw Only used for '--list' command, list the certs in raw format.
6501 --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal.
6502 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
6503 --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate.
6504 --ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl.
6505 --nocron Only valid for '--install' command, which means: do not install the default cron job.
6506 In this case, the certs will not be renewed automatically.
6508 --noprofile Only valid for '--install' command, which means: do not install aliases to user profile.
6509 --no-color Do not output color text.
6510 --force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
6511 --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
6512 --csr Specifies the input csr.
6513 --pre-hook Command to be run before obtaining any certificates.
6514 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed.
6515 --renew-hook Command to be run once for each successfully renewed certificate.
6516 --deploy-hook The hook file to deploy cert
6517 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
6518 --always-force-new-domain-key Generate new domain key when renewal. Otherwise, the domain key is not changed by default.
6519 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
6520 --listen-v4 Force standalone/tls server to listen at ipv4.
6521 --listen-v6 Force standalone/tls server to listen at ipv6.
6522 --openssl-bin Specifies a custom openssl bin location.
6523 --use-wget Force to use wget, if you have both curl and wget installed.
6524 --yes-I-know-dns-manual-mode-enough-go-ahead-please Force to use dns manual mode.
6525 See: $_DNS_MANUAL_WIKI
6527 --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to.
6529 --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT.
6530 0: disabled, no notification will be sent.
6531 1: send notifications only when there is an error.
6532 2: send notifications when a cert is successfully renewed, or there is an error.
6533 3: send notifications when a cert is skipped, renewed, or error.
6535 --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT.
6536 0: Bulk mode. Send all the domain's notifications in one message(mail).
6537 1: Cert mode. Send a message for every single cert.
6539 --notify-hook [hookname] Set the notify hook
6540 --revoke-reason [0-10] The reason for '--revoke' command.
6549 _info
"Installing from online archive."
6552 if [ ! "$BRANCH" ]; then
6556 target
="$PROJECT/archive/$BRANCH.tar.gz"
6557 _info
"Downloading $target"
6558 localname
="$BRANCH.tar.gz"
6559 if ! _get
"$target" >$localname; then
6560 _err
"Download error."
6564 _info
"Extracting $localname"
6565 if ! (tar xzf
$localname || gtar xzf
$localname); then
6566 _err
"Extraction error."
6570 cd "$PROJECT_NAME-$BRANCH"
6571 chmod +x
$PROJECT_ENTRY
6572 if .
/$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
6573 _info
"Install success!"
6575 _saveaccountconf
"UPGRADE_HASH" "$(_getUpgradeHash)"
6580 rm -rf "$PROJECT_NAME-$BRANCH"
6588 _hash_url
="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path"
6589 _get
$_hash_url |
tr -d "\r\n" |
tr '{},' '\n' |
grep '"sha":' | cut
-d '"' -f 4
6594 if [ -z "$_b" ]; then
6597 _hash
=$
(_getRepoHash
"heads/$_b")
6598 if [ -z "$_hash" ]; then _hash
=$
(_getRepoHash
"tags/$_b"); fi
6605 [ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH
")" ] && _info
"Already uptodate!" && exit 0
6606 export LE_WORKING_DIR
6607 cd "$LE_WORKING_DIR"
6608 _installOnline
"nocron" "noprofile"
6610 _info
"Upgrade success!"
6613 _err
"Upgrade failed!"
6618 _processAccountConf
() {
6619 if [ "$_useragent" ]; then
6620 _saveaccountconf
"USER_AGENT" "$_useragent"
6621 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ]; then
6622 _saveaccountconf
"USER_AGENT" "$USER_AGENT"
6625 if [ "$_openssl_bin" ]; then
6626 _saveaccountconf
"ACME_OPENSSL_BIN" "$_openssl_bin"
6627 elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
6628 _saveaccountconf
"ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
6631 if [ "$_auto_upgrade" ]; then
6632 _saveaccountconf
"AUTO_UPGRADE" "$_auto_upgrade"
6633 elif [ "$AUTO_UPGRADE" ]; then
6634 _saveaccountconf
"AUTO_UPGRADE" "$AUTO_UPGRADE"
6637 if [ "$_use_wget" ]; then
6638 _saveaccountconf
"ACME_USE_WGET" "$_use_wget"
6639 elif [ "$ACME_USE_WGET" ]; then
6640 _saveaccountconf
"ACME_USE_WGET" "$ACME_USE_WGET"
6646 if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
6647 if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
6648 #it's root using sudo, no matter it's using sudo or not, just fine
6651 if [ -n "$SUDO_COMMAND" ]; then
6652 #it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
6653 _endswith
"$SUDO_COMMAND" /bin
/su ||
grep "^$SUDO_COMMAND\$" /etc
/shells
>/dev
/null
2>&1
6665 _server_lower
="$(echo "$_server" | _lower_case)"
6667 for snames
in $CA_NAMES; do
6668 snames
="$(echo "$snames" | _lower_case)"
6669 _sindex
="$(_math $_sindex + 1)"
6670 _debug2
"_selectServer try snames" "$snames"
6671 for sname
in $
(echo "$snames" |
tr ',' ' '); do
6672 if [ "$_server_lower" = "$sname" ]; then
6673 _debug2
"_selectServer match $sname"
6674 _serverdir
="$(_getfield "$CA_SERVERS" $_sindex)"
6675 _debug
"Selected server: $_serverdir"
6676 ACME_DIRECTORY
="$_serverdir"
6677 export ACME_DIRECTORY
6682 ACME_DIRECTORY
="$_server"
6683 export ACME_DIRECTORY
6689 if [ -z "$caurl" ]; then
6692 caurl_lower
="$(echo $caurl | _lower_case)"
6694 for surl
in $
(echo "$CA_SERVERS" | _lower_case |
tr , ' '); do
6695 _sindex
="$(_math $_sindex + 1)"
6696 if [ "$caurl_lower" = "$surl" ]; then
6698 for snames
in $CA_NAMES; do
6699 _nindex
="$(_math $_nindex + 1)"
6700 if [ $_nindex -ge $_sindex ]; then
6701 _getfield
"$snames" 1
6710 #set default ca to $ACME_DIRECTORY
6712 if [ -z "$ACME_DIRECTORY" ]; then
6713 _err
"Please give a --server parameter."
6716 _saveaccountconf
"DEFAULT_ACME_SERVER" "$ACME_DIRECTORY"
6717 _info
"Changed default CA to: $(__green "$ACME_DIRECTORY")"
6723 _altdomains
="$NO_VALUE"
6727 _accountkeylength
=""
6744 _stopRenewOnError
=""
6774 while [ ${#} -gt 0 ]; do
6806 --installcert |
-i |
--install-cert)
6812 --renewAll |
--renewall |
--renew-all)
6824 --installcronjob |
--install-cronjob)
6825 _CMD
="installcronjob"
6827 --uninstallcronjob |
--uninstall-cronjob)
6828 _CMD
="uninstallcronjob"
6839 --createAccountKey |
--createaccountkey |
-cak |
--create-account-key)
6840 _CMD
="createAccountKey"
6842 --createDomainKey |
--createdomainkey |
-cdk |
--create-domain-key)
6843 _CMD
="createDomainKey"
6845 --createCSR |
--createcsr |
-ccr)
6851 --updateaccount |
--update-account)
6852 _CMD
="updateaccount"
6854 --registeraccount |
--register-account)
6855 _CMD
="registeraccount"
6857 --deactivate-account)
6858 _CMD
="deactivateaccount"
6869 if [ "$_dvalue" ]; then
6870 if _startswith
"$_dvalue" "-"; then
6871 _err
"'$_dvalue' is not a valid domain for parameter '$1'"
6874 if _is_idn
"$_dvalue" && ! _exists idn
; then
6875 _err
"It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first."
6879 if _startswith
"$_dvalue" "*."; then
6880 _debug
"Wildcard domain"
6881 export ACME_VERSION
=2
6883 if [ -z "$_domain" ]; then
6886 if [ "$_altdomains" = "$NO_VALUE" ]; then
6887 _altdomains
="$_dvalue"
6889 _altdomains
="$_altdomains,$_dvalue"
6905 _selectServer
"$_server"
6909 if [ -z "$2" ] || _startswith
"$2" "-"; then
6910 DEBUG
="$DEBUG_LEVEL_DEFAULT"
6917 export OUTPUT_INSECURE
=1
6921 if [ -z "$_webroot" ]; then
6924 _webroot
="$_webroot,$wvalue"
6930 _challenge_alias
="$_challenge_alias$cvalue,"
6934 cvalue
="$DNS_ALIAS_PREFIX$2"
6935 _challenge_alias
="$_challenge_alias$cvalue,"
6940 if [ -z "$_webroot" ]; then
6943 _webroot
="$_webroot,$wvalue"
6948 if [ -z "$_webroot" ]; then
6951 _webroot
="$_webroot,$wvalue"
6955 wvalue
="$MODE_STATELESS"
6956 if [ -z "$_webroot" ]; then
6959 _webroot
="$_webroot,$wvalue"
6964 _local_address
="$_local_address$lvalue,"
6969 if [ -z "$_webroot" ]; then
6972 _webroot
="$_webroot,$wvalue"
6977 if [ "$2" ] && ! _startswith
"$2" "-"; then
6981 if [ -z "$_webroot" ]; then
6984 _webroot
="$_webroot,$wvalue"
6989 if [ "$2" ] && ! _startswith
"$2" "-"; then
6993 if [ -z "$_webroot" ]; then
6996 _webroot
="$_webroot,$wvalue"
7001 Le_DNSSleep
="$_dnssleep"
7009 --accountkeylength |
-ak)
7010 _accountkeylength
="$2"
7014 --cert-file |
--certpath)
7018 --key-file |
--keypath)
7022 --ca-file |
--capath)
7026 --fullchain-file |
--fullchainpath)
7027 _fullchain_file
="$2"
7030 --reloadcmd |
--reloadCmd)
7040 ACCOUNT_CONF_PATH
="$_accountconf"
7047 --certhome |
--cert-home)
7049 CERT_HOME
="$_certhome"
7054 LE_CONFIG_HOME
="$_confighome"
7059 USER_AGENT
="$_useragent"
7062 --accountemail |
-m)
7064 ACCOUNT_EMAIL
="$_accountemail"
7069 ACCOUNT_KEY_PATH
="$_accountkey"
7074 Le_RenewalDays
="$_days"
7079 Le_HTTPPort
="$_httpport"
7084 Le_TLSPort
="$_tlsport"
7090 --stopRenewOnError |
--stoprenewonerror |
-se)
7091 _stopRenewOnError
="1"
7098 _ca_bundle
="$(_readlink "$2")"
7099 CA_BUNDLE
="$_ca_bundle"
7114 export ACME_NO_COLOR
=1
7117 export ACME_FORCE_COLOR
=1
7139 if [ -z "$2" ] || _startswith
"$2" "-"; then
7140 _usage
"Please specify a value for '--deploy-hook'"
7143 _deploy_hook
="$_deploy_hook$2,"
7146 --ocsp-must-staple |
--ocsp)
7149 --always-force-new-domain-key)
7150 if [ -z "$2" ] || _startswith
"$2" "-"; then
7151 Le_ForceNewDomainKey
=1
7153 Le_ForceNewDomainKey
="$2"
7157 --yes-I-know-dns-manual-mode-enough-go-ahead-please)
7158 export FORCE_DNS_MANUAL
=1
7163 if _startswith
"$_logfile" '-'; then
7168 LOG_FILE
="$_logfile"
7169 if [ -z "$LOG_LEVEL" ]; then
7170 LOG_LEVEL
="$DEFAULT_LOG_LEVEL"
7175 LOG_LEVEL
="$_log_level"
7179 if ! _startswith
"$2" '-'; then
7183 if [ -z "$_syslog" ]; then
7184 _syslog
="$SYSLOG_LEVEL_DEFAULT"
7189 if [ -z "$_auto_upgrade" ] || _startswith
"$_auto_upgrade" '-'; then
7194 AUTO_UPGRADE
="$_auto_upgrade"
7198 Le_Listen_V4
="$_listen_v4"
7202 Le_Listen_V6
="$_listen_v6"
7206 ACME_OPENSSL_BIN
="$_openssl_bin"
7219 if _startswith
"$_nhook" "-"; then
7220 _err
"'$_nhook' is not a hook name for '$1'"
7223 if [ "$_notify_hook" ]; then
7224 _notify_hook
="$_notify_hook,$_nhook"
7226 _notify_hook
="$_nhook"
7232 if _startswith
"$_nlevel" "-"; then
7233 _err
"'$_nlevel' is not a integer for '$1'"
7236 _notify_level
="$_nlevel"
7241 if _startswith
"$_nmode" "-"; then
7242 _err
"'$_nmode' is not a integer for '$1'"
7245 _notify_mode
="$_nmode"
7250 if _startswith
"$_revoke_reason" "-"; then
7251 _err
"'$_revoke_reason' is not a integer for '$1'"
7265 _preferred_chain
="$2"
7269 _err
"Unknown parameter : $1"
7277 if [ "${_CMD}" != "install" ]; then
7278 if [ "$__INTERACTIVE" ] && ! _checkSudo
; then
7279 if [ -z "$FORCE" ]; then
7280 #Use "echo" here, instead of _info. it's too early
7281 echo "It seems that you are using sudo, please read this link first:"
7287 if [ "$_log" ]; then
7288 if [ -z "$_logfile" ]; then
7289 _logfile
="$DEFAULT_LOG_FILE"
7292 if [ "$_logfile" ]; then
7293 _saveaccountconf
"LOG_FILE" "$_logfile"
7294 LOG_FILE
="$_logfile"
7297 if [ "$_log_level" ]; then
7298 _saveaccountconf
"LOG_LEVEL" "$_log_level"
7299 LOG_LEVEL
="$_log_level"
7302 if [ "$_syslog" ]; then
7303 if _exists logger
; then
7304 if [ "$_syslog" = "0" ]; then
7305 _clearaccountconf
"SYS_LOG"
7307 _saveaccountconf
"SYS_LOG" "$_syslog"
7311 _err
"The 'logger' command is not found, can not enable syslog."
7312 _clearaccountconf
"SYS_LOG"
7320 _debug2 LE_WORKING_DIR
"$LE_WORKING_DIR"
7322 if [ "$DEBUG" ]; then
7324 if [ "$_server" ]; then
7325 _debug
"Using server: $_server"
7328 _debug
"Running cmd: ${_CMD}"
7330 install) install "$_nocron" "$_confighome" "$_noprofile" ;;
7331 uninstall
) uninstall
"$_nocron" ;;
7334 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"
7337 deploy
"$_domain" "$_deploy_hook" "$_ecc"
7340 signcsr
"$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
7343 showcsr
"$_csr" "$_domain"
7346 installcert
"$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
7349 renew
"$_domain" "$_ecc"
7352 renewAll
"$_stopRenewOnError"
7355 revoke
"$_domain" "$_ecc" "$_revoke_reason"
7358 remove
"$_domain" "$_ecc"
7361 deactivate
"$_domain,$_altdomains"
7364 registeraccount
"$_accountkeylength" "$_eab_kid" "$_eab_hmac_key"
7373 list
"$_listraw" "$_domain"
7375 installcronjob
) installcronjob
"$_confighome" ;;
7376 uninstallcronjob
) uninstallcronjob
;;
7379 toPkcs
"$_domain" "$_password" "$_ecc"
7382 toPkcs8
"$_domain" "$_ecc"
7385 createAccountKey
"$_accountkeylength"
7388 createDomainKey
"$_domain" "$_keylength"
7391 createCSR
"$_domain" "$_altdomains" "$_ecc"
7394 setnotify
"$_notify_hook" "$_notify_level" "$_notify_mode"
7400 if [ "$_CMD" ]; then
7401 _err
"Invalid command: $_CMD"
7408 if [ "$_ret" != "0" ]; then
7412 if [ "${_CMD}" = "install" ]; then
7413 if [ "$_log" ]; then
7414 if [ -z "$LOG_FILE" ]; then
7415 LOG_FILE
="$DEFAULT_LOG_FILE"
7417 _saveaccountconf
"LOG_FILE" "$LOG_FILE"
7420 if [ "$_log_level" ]; then
7421 _saveaccountconf
"LOG_LEVEL" "$_log_level"
7424 if [ "$_syslog" ]; then
7425 if _exists logger
; then
7426 if [ "$_syslog" = "0" ]; then
7427 _clearaccountconf
"SYS_LOG"
7429 _saveaccountconf
"SYS_LOG" "$_syslog"
7432 _err
"The 'logger' command is not found, can not enable syslog."
7433 _clearaccountconf
"SYS_LOG"
7443 if [ "$INSTALLONLINE" ]; then
7450 [ -z "$1" ] && showhelp
&& return
7451 if _startswith
"$1" '-'; then _process
"$@"; else "$@"; fi