#!/usr/bin/env sh
-VER=3.0.2
+VER=3.0.5
PROJECT_NAME="acme.sh"
_SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
-CA_LETSENCRYPT_V1="https://acme-v01.api.letsencrypt.org/directory"
-
CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory"
CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory"
CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
-_ZERO_EAB_ENDPOINT="http://api.zerossl.com/acme/eab-credentials-email"
+_ZERO_EAB_ENDPOINT="https://api.zerossl.com/acme/eab-credentials-email"
CA_SSLCOM_RSA="https://acme.ssl.com/sslcom-dv-rsa"
CA_SSLCOM_ECC="https://acme.ssl.com/sslcom-dv-ecc"
+CA_GOOGLE="https://dv.acme-v02.api.pki.goog/directory"
+CA_GOOGLE_TEST="https://dv.acme-v02.test-api.pki.goog/directory"
+
DEFAULT_CA=$CA_ZEROSSL
DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST
BuyPass.com,buypass
BuyPass.com_test,buypass_test,buypasstest
SSL.com,sslcom
+Google.com,google
+Google.com_test,googletest,google_test
"
-CA_SERVERS="$CA_ZEROSSL,$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_SSLCOM_RSA"
+CA_SERVERS="$CA_ZEROSSL,$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_SSLCOM_RSA,$CA_GOOGLE,$CA_GOOGLE_TEST"
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
CONTENT_TYPE_JSON="application/jose+json"
RENEW_SKIP=2
+CODE_DNS_MANUAL=3
B64CONF_START="__ACME_BASE64__START_"
B64CONF_END="__ACME_BASE64__END_"
_PREFERRED_CHAIN_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain"
+_VALIDITY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Validity"
+
_DNSCHECK_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dnscheck"
_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."
}
_upper_case() {
- if _is_solaris; then
- tr '[:lower:]' '[:upper:]'
- else
- # shellcheck disable=SC2018,SC2019
- tr 'a-z' 'A-Z'
- fi
+ # shellcheck disable=SC2018,SC2019
+ tr '[a-z]' '[A-Z]'
}
_lower_case() {
- if _is_solaris; then
- tr '[:upper:]' '[:lower:]'
- else
- # shellcheck disable=SC2018,SC2019
- tr 'A-Z' 'a-z'
- fi
+ # shellcheck disable=SC2018,SC2019
+ tr '[A-Z]' '[a-z]'
}
_startswith() {
#Usage: multiline
_dbase64() {
if [ "$1" ]; then
- ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
- else
${ACME_OPENSSL_BIN:-openssl} base64 -d
+ else
+ ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
fi
}
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
- if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
+ if _isRSA "$keyfile" >/dev/null 2>&1; then
$_sign_openssl -$alg | _base64
- elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
+ elif _isEcc "$keyfile" >/dev/null 2>&1; then
if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
_err "Sign failed: $_sign_openssl"
_err "Key file: $keyfile"
_debug "Use length $length"
- if ! touch "$f" >/dev/null 2>&1; then
- _f_path="$(dirname "$f")"
- _debug _f_path "$_f_path"
- if ! mkdir -p "$_f_path"; then
- _err "Can not create path: $_f_path"
+ if ! [ -e "$f" ]; then
+ if ! touch "$f" >/dev/null 2>&1; then
+ _f_path="$(dirname "$f")"
+ _debug _f_path "$_f_path"
+ if ! mkdir -p "$_f_path"; then
+ _err "Can not create path: $_f_path"
+ return 1
+ fi
+ fi
+ if ! touch "$f" >/dev/null 2>&1; then
return 1
fi
+ chmod 600 "$f"
fi
if _isEccKey "$length"; then
_is_idn() {
_is_idn_d="$1"
_debug2 _is_idn_d "$_is_idn_d"
- _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-_')
+ _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '[0-9]' | tr -d '[a-z]' | tr -d '[A-Z]' | tr -d '*.,-_')
_debug2 _idn_temp "$_idn_temp"
[ "$_idn_temp" ]
}
domainlist="$(_idn "$domainlist")"
_debug2 domainlist "$domainlist"
alt="$(_getIdType "$domain" | _upper_case):$(_idn "$domain")"
- for dl in $(echo "$domainlist" | tr "," ' '); do
+ for dl in $(echo "'$domainlist'" | sed "s/,/' '/g"); do
+ dl=$(echo "$dl" | tr -d "'")
alt="$alt,$(_getIdType "$dl" | _upper_case):$dl"
done
#multi
_csr_cn="$(_idn "$domain")"
_debug2 _csr_cn "$_csr_cn"
if _contains "$(uname -a)" "MINGW"; then
- ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ if _isIP "$_csr_cn"; then
+ ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//O=$PROJECT_NAME" -config "$csrconf" -out "$csr"
+ else
+ ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ fi
else
- ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ if _isIP "$_csr_cn"; then
+ ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/O=$PROJECT_NAME" -config "$csrconf" -out "$csr"
+ else
+ ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ fi
fi
}
else
#generate account key
if _createkey "$length" "$ACCOUNT_KEY_PATH"; then
- chmod 600 "$ACCOUNT_KEY_PATH"
_info "Create account key ok."
return 0
else
_time2str() {
#BSD
- if date -u -r "$1" 2>/dev/null; then
+ if date -u -r "$1" -j "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; then
return
fi
#Linux
- if date -u -d@"$1" 2>/dev/null; then
+ if date -u --date=@"$1" "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; then
return
fi
#Solaris
- if _exists adb; then
- _t_s_a=$(echo "0t${1}=Y" | adb)
- echo "$_t_s_a"
+ if printf "%(%Y-%m-%dT%H:%M:%SZ)T\n" $1 2>/dev/null; then
+ return
fi
#Busybox
- if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
+ if echo "$1" | awk '{ print strftime("%Y-%m-%dT%H:%M:%SZ", $0); }' 2>/dev/null; then
return
fi
}
return 1 #error, 'stat' not found
}
+#keyfile
+_isRSA() {
+ keyfile=$1
+ if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || ${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" >/dev/null 2>&1; then
+ return 0
+ fi
+ return 1
+}
+
+#keyfile
+_isEcc() {
+ keyfile=$1
+ if grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || ${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" >/dev/null 2>&1; then
+ return 0
+ fi
+ return 1
+}
+
#keyfile
_calcjwk() {
keyfile="$1"
return 0
fi
- if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
+ if _isRSA "$keyfile"; then
_debug "RSA key"
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)
if [ "${#pub_exp}" = "5" ]; then
JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "'
JWK_HEADERPLACE_PART2='", "alg": "RS256"'
- elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
+ elif _isEcc "$keyfile"; then
_debug "EC key"
crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
_debug3 crv "$crv"
date -u "+%s"
}
+#support 2 formats:
+# 2022-04-01 08:10:33 to 1648800633
+#or 2022-04-01T08:10:33Z to 1648800633
+_date2time() {
+ #Linux
+ if date -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
+ return
+ fi
+
+ #Solaris
+ if gdate -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
+ return
+ fi
+ #Mac/BSD
+ if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
+ return
+ fi
+ _err "Can not parse _date2time $1"
+ return 1
+}
+
_utc_date() {
date -u "+%Y-%m-%d %H:%M:%S"
}
_ACME_WGET="$_ACME_WGET --max-redirect 0 "
fi
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
- _ACME_WGET="$_ACME_WGET -d "
+ if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--debug"; then
+ _ACME_WGET="$_ACME_WGET -d "
+ fi
fi
if [ "$CA_PATH" ]; then
_ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
return 1
}
+_clearCA() {
+ export CA_CONF=
+ export ACCOUNT_KEY_PATH=
+ export ACCOUNT_JSON_PATH=
+}
+
#[domain] [keylength or isEcc flag]
_initpath() {
domain="$1"
_email="$(_getAccountEmail)"
- if [ "$ACCOUNT_EMAIL" ]; then
+ if [ "$_email" ]; then
updjson='{"contact": ["mailto:'$_email'"]}'
else
updjson='{"contact": []}'
if [ "$code" = '200' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH"
- _info "account update success for $_accUri."
+ _info "Account update success for $_accUri."
else
_info "Error. The account was not updated."
return 1
_isIPv4() {
for seg in $(echo "$1" | tr '.' ' '); do
_debug2 seg "$seg"
- if [ "$(echo "$seg" | tr -d [0-9])" ]; then
+ if [ "$(echo "$seg" | tr -d '[0-9]')" ]; then
#not all number
return 1
fi
fi
}
+# beginTime dateTo
+# beginTime is full string format("2022-04-01T08:10:33Z"), beginTime can be empty, to use current time
+# dateTo can be ether in full string format("2022-04-01T08:10:33Z") or in delta format(+5d or +20h)
+_convertValidaty() {
+ _beginTime="$1"
+ _dateTo="$2"
+ _debug2 "_beginTime" "$_beginTime"
+ _debug2 "_dateTo" "$_dateTo"
+
+ if _startswith "$_dateTo" "+"; then
+ _v_begin=$(_time)
+ if [ "$_beginTime" ]; then
+ _v_begin="$(_date2time "$_beginTime")"
+ fi
+ _debug2 "_v_begin" "$_v_begin"
+ if _endswith "$_dateTo" "h"; then
+ _v_end=$(_math "$_v_begin + 60 * 60 * $(echo "$_dateTo" | tr -d '+h')")
+ elif _endswith "$_dateTo" "d"; then
+ _v_end=$(_math "$_v_begin + 60 * 60 * 24 * $(echo "$_dateTo" | tr -d '+d')")
+ else
+ _err "Not recognized format for _dateTo: $_dateTo"
+ return 1
+ fi
+ _debug2 "_v_end" "$_v_end"
+ _time2str "$_v_end"
+ else
+ if [ "$(_time)" -gt "$(_date2time "$_dateTo")" ]; then
+ _err "The validaty to is in the past: _dateTo = $_dateTo"
+ return 1
+ fi
+ echo "$_dateTo"
+ fi
+}
+
#webroot, domain domainlist keylength
issue() {
if [ -z "$2" ]; then
_local_addr="${13}"
_challenge_alias="${14}"
_preferred_chain="${15}"
+ _valid_from="${16}"
+ _valid_to="${17}"
if [ -z "$_ACME_IS_RENEW" ]; then
_initpath "$_main_domain" "$_key_length"
Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
_debug Le_NextRenewTime "$Le_NextRenewTime"
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
+ _valid_to_saved=$(_readdomainconf Le_Valid_to)
+ if [ "$_valid_to_saved" ] && ! _startswith "$_valid_to_saved" "+"; then
+ _info "The domain is set to be valid to: $_valid_to_saved"
+ _info "It can not be renewed automatically"
+ _info "See: $_VALIDITY_WIKI"
+ return $RENEW_SKIP
+ fi
_saved_domain=$(_readdomainconf Le_Domain)
_debug _saved_domain "$_saved_domain"
_saved_alt=$(_readdomainconf Le_Alt)
_debug _saved_alt "$_saved_alt"
- if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
+ _normized_saved_domains="$(echo "$_saved_domain,$_saved_alt" | tr "," "\n" | sort | tr '\n' ',')"
+ _debug _normized_saved_domains "$_normized_saved_domains"
+
+ _normized_domains="$(echo "$_main_domain,$_alt_domains" | tr "," "\n" | sort | tr '\n' ',')"
+ _debug _normized_domains "$_normized_domains"
+
+ if [ "$_normized_saved_domains" = "$_normized_domains" ]; then
_info "Domains not changed."
_info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
_info "Add '$(__red '--force')' to force to renew."
_alt_domains=""
fi
- if [ "$_key_length" = "$NO_VALUE" ]; then
- _key_length=""
- fi
-
if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
_err "_on_before_issue."
return 1
if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
_info "Signing from existing CSR."
else
+ # When renewing from an old version, the empty Le_Keylength means 2048.
+ # Note, do not use DEFAULT_DOMAIN_KEY_LENGTH as that value may change over
+ # time but an empty value implies 2048 specifically.
_key=$(_readdomainconf Le_Keylength)
+ if [ -z "$_key" ]; then
+ _key=2048
+ fi
_debug "Read key length:$_key"
if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then
if ! createDomainKey "$_main_domain" "$_key_length"; then
_identifiers="$_identifiers,{\"type\":\"$(_getIdType "$d")\",\"value\":\"$(_idn "$d")\"}"
done
_debug2 _identifiers "$_identifiers"
- if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
+ _notBefore=""
+ _notAfter=""
+
+ if [ "$_valid_from" ]; then
+ _savedomainconf "Le_Valid_From" "$_valid_from"
+ _debug2 "_valid_from" "$_valid_from"
+ _notBefore="$(_convertValidaty "" "$_valid_from")"
+ if [ "$?" != "0" ]; then
+ _err "Can not parse _valid_from: $_valid_from"
+ return 1
+ fi
+ if [ "$(_time)" -gt "$(_date2time "$_notBefore")" ]; then
+ _notBefore=""
+ fi
+ else
+ _cleardomainconf "Le_Valid_From"
+ fi
+ _debug2 _notBefore "$_notBefore"
+
+ if [ "$_valid_to" ]; then
+ _debug2 "_valid_to" "$_valid_to"
+ _savedomainconf "Le_Valid_To" "$_valid_to"
+ _notAfter="$(_convertValidaty "$_notBefore" "$_valid_to")"
+ if [ "$?" != "0" ]; then
+ _err "Can not parse _valid_to: $_valid_to"
+ return 1
+ fi
+ else
+ _cleardomainconf "Le_Valid_To"
+ fi
+ _debug2 "_notAfter" "$_notAfter"
+
+ _newOrderObj="{\"identifiers\": [$_identifiers]"
+ if [ "$_notBefore" ]; then
+ _newOrderObj="$_newOrderObj,\"notBefore\": \"$_notBefore\""
+ fi
+ if [ "$_notAfter" ]; then
+ _newOrderObj="$_newOrderObj,\"notAfter\": \"$_notAfter\""
+ fi
+ if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
_err "Create new order error."
_clearup
_on_issue_err "$_post_hook"
return 1
fi
+
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
_debug Le_LinkOrder "$Le_LinkOrder"
Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
- _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
+ _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2- | tr -d ' "')"
if _contains "$response" "\"wildcard\" *: *true"; then
_d="*.$_d"
fi
_dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
fi
_d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
+ test "$_d_alias" = "$NO_VALUE" && _d_alias=""
_alias_index="$(_math "$_alias_index" + 1)"
_debug "_d_alias" "$_d_alias"
if [ "$_d_alias" ]; then
_err "Please add the TXT records to the domains, and re-run with --renew."
_on_issue_err "$_post_hook"
_clearup
- return 1
+ # If asked to be in manual DNS mode, flag this exit with a separate
+ # error so it can be distinguished from other failures.
+ return $CODE_DNS_MANUAL
fi
fi
return 1
fi
_debug "sleep 2 secs to verify again"
- sleep 2
+ _sleep 2
_debug "checking"
_send_signed_request "$uri"
Le_CertCreateTime=$(_time)
_savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
- Le_CertCreateTimeStr=$(date -u)
+ Le_CertCreateTimeStr=$(_time2str "$Le_CertCreateTime")
_savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then
else
_cleardomainconf Le_ForceNewDomainKey
fi
-
- Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
-
- Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
+ if [ "$_notAfter" ]; then
+ Le_NextRenewTime=$(_date2time "$_notAfter")
+ Le_NextRenewTimeStr="$_notAfter"
+ if [ "$_valid_to" ] && ! _startswith "$_valid_to" "+"; then
+ _info "The domain is set to be valid to: $_valid_to"
+ _info "It can not be renewed automatically"
+ _info "See: $_VALIDITY_WIKI"
+ fi
+ else
+ Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
+ Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
+ Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
+ fi
_savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
-
- Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
fi
}
-#domain [isEcc]
+#domain [isEcc] [server]
renew() {
Le_Domain="$1"
if [ -z "$Le_Domain" ]; then
- _usage "Usage: $PROJECT_ENTRY --renew --domain <domain.tld> [--ecc]"
+ _usage "Usage: $PROJECT_ENTRY --renew --domain <domain.tld> [--ecc] [--server server]"
return 1
fi
_isEcc="$2"
+ _renewServer="$3"
+ _debug "_renewServer" "$_renewServer"
_initpath "$Le_Domain" "$_isEcc"
+
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
_info "$(__green "Renew: '$Le_Domain'")"
if [ ! -f "$DOMAIN_CONF" ]; then
. "$DOMAIN_CONF"
_debug Le_API "$Le_API"
- if [ -z "$Le_API" ] || [ "$CA_LETSENCRYPT_V1" = "$Le_API" ]; then
- #if this is from an old version, Le_API is empty,
- #so, we force to use letsencrypt server
+
+ case "$Le_API" in
+ "$CA_LETSENCRYPT_V2_TEST")
+ _info "Switching back to $CA_LETSENCRYPT_V2"
Le_API="$CA_LETSENCRYPT_V2"
- fi
+ ;;
+ "$CA_BUYPASS_TEST")
+ _info "Switching back to $CA_BUYPASS"
+ Le_API="$CA_BUYPASS"
+ ;;
+ "$CA_GOOGLE_TEST")
+ _info "Switching back to $CA_GOOGLE"
+ Le_API="$CA_GOOGLE"
+ ;;
+ esac
- if [ "$Le_API" ]; then
- if [ "$Le_API" != "$ACME_DIRECTORY" ]; then
- _clearAPI
- fi
- export ACME_DIRECTORY="$Le_API"
- #reload ca configs
- ACCOUNT_KEY_PATH=""
- ACCOUNT_JSON_PATH=""
- CA_CONF=""
- _debug3 "initpath again."
- _initpath "$Le_Domain" "$_isEcc"
+ if [ "$_server" ]; then
+ Le_API="$_server"
fi
+ _info "Renew to Le_API=$Le_API"
+
+ _clearAPI
+ _clearCA
+ export ACME_DIRECTORY="$Le_API"
+
+ #reload ca configs
+ _debug2 "initpath again."
+ _initpath "$Le_Domain" "$_isEcc"
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
_info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
Le_PostHook="$(_readdomainconf Le_PostHook)"
Le_RenewHook="$(_readdomainconf Le_RenewHook)"
Le_Preferred_Chain="$(_readdomainconf Le_Preferred_Chain)"
- 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"
+ # When renewing from an old version, the empty Le_Keylength means 2048.
+ # Note, do not use DEFAULT_DOMAIN_KEY_LENGTH as that value may change over
+ # time but an empty value implies 2048 specifically.
+ Le_Keylength="$(_readdomainconf Le_Keylength)"
+ if [ -z "$Le_Keylength" ]; then
+ Le_Keylength=2048
+ fi
+ 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" "$Le_Valid_From" "$Le_Valid_To"
res="$?"
if [ "$res" != "0" ]; then
return "$res"
return "$res"
}
-#renewAll [stopRenewOnError]
+#renewAll [stopRenewOnError] [server]
renewAll() {
_initpath
+ _clearCA
_stopRenewOnError="$1"
_debug "_stopRenewOnError" "$_stopRenewOnError"
+
+ _server="$2"
+ _debug "_server" "$_server"
+
_ret="0"
_success_msg=""
_error_msg=""
_isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2)
d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
fi
- renew "$d" "$_isEcc"
+ renew "$d" "$_isEcc" "$_server"
)
rc="$?"
_debug "Return code: $rc"
_initpath
_csrsubj=$(_readSubjectFromCSR "$_csrfile")
- if [ "$?" != "0" ] || [ -z "$_csrsubj" ]; then
+ if [ "$?" != "0" ]; then
_err "Can not read subject from csr: $_csrfile"
return 1
fi
+ if [ -z "$_csrsubj" ]; then
+ _info "The Subject is empty"
+ fi
_info "Subject=$_csrsubj"
if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_cert" "$_backup_path/cert.bak"
fi
- cat "$CERT_PATH" >"$_real_cert" || return 1
+ if [ "$CERT_PATH" != "$_real_cert" ]; then
+ cat "$CERT_PATH" >"$_real_cert" || return 1
+ fi
fi
if [ "$_real_ca" ]; then
if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_ca" "$_backup_path/ca.bak"
fi
- cat "$CA_CERT_PATH" >"$_real_ca" || return 1
+ if [ "$CA_CERT_PATH" != "$_real_ca" ]; then
+ cat "$CA_CERT_PATH" >"$_real_ca" || return 1
+ fi
fi
fi
if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_key" "$_backup_path/key.bak"
fi
- if [ -f "$_real_key" ]; then
- cat "$CERT_KEY_PATH" >"$_real_key" || return 1
- else
- cat "$CERT_KEY_PATH" >"$_real_key" || return 1
- chmod 600 "$_real_key"
+ if [ "$CERT_KEY_PATH" != "$_real_key" ]; then
+ if [ -f "$_real_key" ]; then
+ cat "$CERT_KEY_PATH" >"$_real_key" || return 1
+ else
+ touch "$_real_key" || return 1
+ chmod 600 "$_real_key"
+ cat "$CERT_KEY_PATH" >"$_real_key" || return 1
+ fi
fi
fi
if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_fullchain" "$_backup_path/fullchain.bak"
fi
- cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1
+ if [ "$_real_fullchain" != "$CERT_FULLCHAIN_PATH" ]; then
+ cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1
+ fi
fi
if [ "$_reload_cmd" ]; then
If no match, the default offered chain will be used. (default: empty)
See: $_PREFERRED_CHAIN_WIKI
+ --valid-to <date-time> Request the NotAfter field of the cert.
+ See: $_VALIDITY_WIKI
+ --valid-from <date-time> Request the NotBefore field of the cert.
+ See: $_VALIDITY_WIKI
+
-f, --force Force install, force cert renewal or override sudo restrictions.
--staging, --test Use staging server, for testing.
--debug [0|1|2|3] Output debug info. Defaults to 1 if argument is omitted.
}
_checkSudo() {
+ if [ -z "__INTERACTIVE" ]; then
+ #don't check if it's not in an interactive shell
+ return 0
+ fi
if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
#it's root using sudo, no matter it's using sudo or not, just fine
_altdomains="$NO_VALUE"
_webroot=""
_challenge_alias=""
- _keylength=""
- _accountkeylength=""
+ _keylength="$DEFAULT_DOMAIN_KEY_LENGTH"
+ _accountkeylength="$DEFAULT_ACCOUNT_KEY_LENGTH"
_cert_file=""
_key_file=""
_ca_file=""
_eab_kid=""
_eab_hmac_key=""
_preferred_chain=""
+ _valid_from=""
+ _valid_to=""
while [ ${#} -gt 0 ]; do
case "${1}" in
Le_RenewalDays="$_days"
shift
;;
+ --valid-from)
+ _valid_from="$2"
+ shift
+ ;;
+ --valid-to)
+ _valid_to="$2"
+ shift
+ ;;
--httpport)
_httpport="$2"
Le_HTTPPort="$_httpport"
if [ "$_server" ]; then
_selectServer "$_server" "${_ecc:-$_keylength}"
+ _server="$ACME_DIRECTORY"
fi
if [ "${_CMD}" != "install" ]; then
uninstall) uninstall "$_nocron" ;;
upgrade) upgrade ;;
issue)
- 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"
+ 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" "$_valid_from" "$_valid_to"
;;
deploy)
deploy "$_domain" "$_deploy_hook" "$_ecc"
installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
;;
renew)
- renew "$_domain" "$_ecc"
+ renew "$_domain" "$_ecc" "$_server"
;;
renewAll)
- renewAll "$_stopRenewOnError"
+ renewAll "$_stopRenewOnError" "$_server"
;;
revoke)
revoke "$_domain" "$_ecc" "$_revoke_reason"