#!/usr/bin/env sh
-VER=2.7.6
+VER=2.7.7
PROJECT_NAME="acme.sh"
DEFAULT_CA=$LETSENCRYPT_CA_V1
DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1
-
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_EMAIL=""
NO_VALUE="no"
W_TLS="tls"
+DNS_ALIAS_PREFIX="="
MODE_STATELESS="stateless"
_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
+_DNS_ALIAS_WIKI="https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode"
+
_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."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
_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"
if _contains "$domainlist" ","; then
- alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")"
+ alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")"
else
alt="DNS:$domain,DNS:$domainlist"
fi
else
#generate account key
_createkey "$length" "$ACCOUNT_KEY_PATH"
+ chmod 600 "$ACCOUNT_KEY_PATH"
fi
}
_ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
fi
+ if _contains "$(curl --help 2>&1)" "--globoff"; then
+ _ACME_CURL="$_ACME_CURL -g "
+ fi
fi
if [ -z "$_ACME_WGET" ] && _exists "wget"; then
onlyheader="$2"
t="$3"
_debug url "$url"
- _debug "timeout" "$t"
+ _debug "timeout=$t"
_inithttp
_debug2 nonce "$nonce"
if [ "$ACME_VERSION" = "2" ]; then
- if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then
+ if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
else
- protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}'
+ protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
fi
else
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
_body="$response"
if [ "$needbase64" ]; then
_body="$(echo "$_body" | _dbase64)"
- _debug2 _body "$_body"
+ _debug3 _body "$_body"
fi
if _contains "$_body" "JWS has invalid anti-replay nonce"; then
_NC="$_NC -6"
fi
- _debug "_NC" "$_NC"
- #todo listen address
- $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" &
+ if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
+ _NC="$_NC -d -d -v"
+ fi
+
+ SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork
+
+ #Adding bind to local-address
+ if [ "$ncaddr" ]; then
+ SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}"
+ fi
+
+ _debug "_NC" "$_NC $SOCAT_OPTIONS"
+ $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" &
serverproc="$!"
}
#[domain] [keylength or isEcc flag]
_initpath() {
+ domain="$1"
+ _ilength="$2"
__initHome
CA_HOME="$DEFAULT_CA_HOME"
fi
+ if [ "$ACME_VERSION" = "2" ]; then
+ DEFAULT_CA="$LETSENCRYPT_CA_V2"
+ DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2"
+ fi
+
if [ -z "$ACME_DIRECTORY" ]; then
if [ -z "$STAGE" ]; then
ACME_DIRECTORY="$DEFAULT_CA"
ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
fi
- if [ -z "$1" ]; then
+ if [ -z "$domain" ]; then
return 0
fi
- domain="$1"
- _ilength="$2"
-
if [ -z "$DOMAIN_PATH" ]; then
domainhome="$CERT_HOME/$domain"
domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
_d="$1"
_croot="$2"
_thumbpt="$3"
- if ! _exists "nginx"; then
- _err "nginx command is not found."
- return 1
- fi
+
FOUND_REAL_NGINX_CONF=""
FOUND_REAL_NGINX_CONF_LN=""
BACKUP_NGINX_CONF=""
if [ -z "$_start_f" ]; then
_debug "find start conf from nginx command"
if [ -z "$NGINX_CONF" ]; then
+ if ! _exists "nginx"; then
+ _err "nginx command is not found."
+ return 1
+ fi
NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
_debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
return 1
fi
+ if ! _exists "nginx"; then
+ _err "nginx command is not found."
+ return 1
+ fi
_info "Check the nginx conf before setting up."
if ! _exec "nginx -t" >/dev/null; then
_exec_err
for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
_debug _fln "$_fln"
if [ "$_fln" ]; then
- _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1)
+ _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *" | grep -v server_name | _tail_n 1)
_debug "_start" "$_start"
_start_n=$(echo "$_start" | cut -d : -f 1)
_start_nn=$(_math $_start_n + 1)
_left="$(sed -n "${_start_nn},99999p" "$2")"
_debug2 _left "$_left"
- if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then
- _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1)
- _debug "_end" "$_end"
+ _end="$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | grep -v server_name | _head_n 1)"
+ _debug "_end" "$_end"
+ if [ "$_end" ]; then
_end_n=$(echo "$_end" | cut -d : -f 1)
_debug "_end_n" "$_end_n"
_seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
_debug "_seg_n" "$_seg_n"
- if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \
- || [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then
+ _skip_ssl=1
+ for _listen_i in $(echo "$_seg_n" | tr "\t" ' ' | grep "^ *listen" | tr -d " "); do
+ if [ "$_listen_i" ]; then
+ if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then
+ _debug2 "$_listen_i is ssl"
+ else
+ _debug2 "$_listen_i is plain text"
+ _skip_ssl=""
+ break
+ fi
+ fi
+ done
+
+ if [ "$_skip_ssl" = "1" ]; then
_debug "ssl on, skip"
else
FOUND_REAL_NGINX_CONF_LN=$_fln
_debug "skip dns."
return
fi
-
+ _info "Removing DNS records."
ventries=$(echo "$vlist" | tr ',' ' ')
+ _alias_index=1
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
fi
if [ "$vtype" != "$VTYPE_DNS" ]; then
- _info "Skip $d for $vtype"
+ _debug "Skip $d for $vtype"
continue
fi
return 1
fi
- txtdomain="_acme-challenge.$d"
+ _dns_root_d="$d"
+ if _startswith "$_dns_root_d" "*."; then
+ _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
+ fi
+
+ _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
+ _alias_index="$(_math "$_alias_index" + 1)"
+ _debug "_d_alias" "$_d_alias"
+ if [ "$_d_alias" ]; then
+ if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
+ txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
+ else
+ txtdomain="_acme-challenge.$_d_alias"
+ fi
+ else
+ txtdomain="_acme-challenge.$_dns_root_d"
+ fi
if ! $rmcommand "$txtdomain" "$txt"; then
_err "Error removing txt for domain:$txtdomain"
_chk_post_hook="$1"
_chk_vlist="$2"
_debug _on_issue_err
- _cleardomainconf "ORDER_FINALIZE"
+
if [ "$LOG_FILE" ]; then
_err "Please check log file for more details: $LOG_FILE"
else
if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
return 1
fi
-
+
if [ "$ACME_VERSION" = "2" ]; then
regjson='{"termsOfServiceAgreed": true}'
if [ "$ACCOUNT_EMAIL" ]; then
if [ "$code" = "" ] || [ "$code" = '201' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH"
_info "Registered"
- elif [ "$code" = '409' ]; then
+ elif [ "$code" = '409' ] || [ "$code" = '200' ]; then
_info "Already registered"
else
_err "Register account Error: $response"
fi
_initAPI
- if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
+ if [ "$ACME_VERSION" = "2" ]; then
+ _djson="{\"status\":\"deactivated\"}"
+ else
+ _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
+ fi
+ if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate account success for $_accUri."
_accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
elif [ "$code" = "403" ]; then
_err "new-authz retry reach the max $_Max_new_authz_retry_times times."
fi
- if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then
+ if [ "$code" ] && [ "$code" != '201' ]; then
_err "new-authz error: $response"
return 1
fi
_web_roots="$1"
_main_domain="$2"
_alt_domains="$3"
+
if _contains "$_main_domain" ","; then
_main_domain=$(echo "$2,$3" | cut -d , -f 1)
_alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
_post_hook="${11}"
_renew_hook="${12}"
_local_addr="${13}"
-
+ _challenge_alias="${14}"
#remove these later.
if [ "$_web_roots" = "dns-cf" ]; then
_web_roots="dns_cf"
else
_cleardomainconf "Le_LocalAddress"
fi
+ if [ "$_challenge_alias" ]; then
+ _savedomainconf "Le_ChallengeAlias" "$_challenge_alias"
+ else
+ _cleardomainconf "Le_ChallengeAlias"
+ fi
Le_API="$ACME_DIRECTORY"
_savedomainconf "Le_API" "$Le_API"
sep='#'
dvsep=','
if [ -z "$vlist" ]; then
- if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then
+ if [ "$ACME_VERSION" = "2" ]; then
#make new order request
_identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}"
for d in $(echo "$_alt_domains" | tr ',' ' '); do
- #todo: check wildcard ?
if [ "$d" ]; then
_identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}"
fi
return 1
fi
- ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
- _debug ORDER_FINALIZE "$ORDER_FINALIZE"
- if [ -z "$ORDER_FINALIZE" ]; then
- _err "ORDER_FINALIZE not found."
+ Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
+ _debug Le_OrderFinalize "$Le_OrderFinalize"
+ if [ -z "$Le_OrderFinalize" ]; then
+ _err "Create new order error. Le_OrderFinalize not found. $response"
_clearup
_on_issue_err "$_post_hook"
return 1
fi
#for dns manual mode
- _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE"
+ _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize"
- _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
+ _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
_debug2 _authorizations_seg "$_authorizations_seg"
if [ -z "$_authorizations_seg" ]; then
_err "_authorizations_seg not found."
#domain and authz map
_authorizations_map=""
- for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do
+ for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
_debug2 "_authz_url" "$_authz_url"
if ! response="$(_get "$_authz_url")"; then
_err "get to authz error."
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
_d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
+ if _contains "$response" "\"wildcard\" *: *true"; then
+ _d="*.$_d"
+ fi
_debug2 _d "$_d"
_authorizations_map="$_d,$response
$_authorizations_map"
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
- _err "Error, can not get domain token $d"
+ _err "Error, can not get domain token entry $d"
+ _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
+ if [ "$_supported_vtypes" ]; then
+ _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
+ fi
_clearup
_on_issue_err "$_post_hook"
return 1
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
+ if [ -z "$token" ]; then
+ _err "Error, can not get domain token $entry"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
if [ "$ACME_VERSION" = "2" ]; then
uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
else
fi
_debug uri "$uri"
+ if [ -z "$uri" ]; then
+ _err "Error, can not get domain uri. $entry"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization"
#add entry
dnsadded=""
ventries=$(echo "$vlist" | tr "$dvsep" ' ')
+ _alias_index=1
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
-
+ _debug d "$d"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_debug "$d is already verified, skip $vtype."
continue
if [ "$vtype" = "$VTYPE_DNS" ]; then
dnsadded='0'
- txtdomain="_acme-challenge.$d"
+ _dns_root_d="$d"
+ if _startswith "$_dns_root_d" "*."; then
+ _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
+ fi
+ _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
+ _alias_index="$(_math "$_alias_index" + 1)"
+ _debug "_d_alias" "$_d_alias"
+ if [ "$_d_alias" ]; then
+ if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
+ txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
+ else
+ txtdomain="_acme-challenge.$_d_alias"
+ fi
+ else
+ txtdomain="_acme-challenge.$_dns_root_d"
+ fi
_debug txtdomain "$txtdomain"
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt"
- d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
+ d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")"
_debug d_api "$d_api"
der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
if [ "$ACME_VERSION" = "2" ]; then
- if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then
+ if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
_err "Sign failed."
_on_issue_err "$_post_hook"
return 1
_on_issue_err "$_post_hook"
return 1
fi
- Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
+ Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
- if ! _get "$Le_LinkCert" > "$CERT_PATH"; then
+ if ! _get "$Le_LinkCert" >"$CERT_PATH"; then
_err "Sign failed, code is not 200."
_on_issue_err "$_post_hook"
return 1
fi
+
+ if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then
+ _debug "Found cert chain"
+ cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH"
+ _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)"
+ _debug _end_n "$_end_n"
+ sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" >"$CERT_PATH"
+ _end_n="$(_math $_end_n + 1)"
+ sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH"
+ fi
else
if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
_err "Sign failed."
_info "Your cert key is in $(__green " $CERT_KEY_PATH ")"
fi
- cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
+ if [ "$ACME_VERSION" != "2" ]; then
+ cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
+ fi
if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then
USER_PATH="$PATH"
_cleardomainconf "Le_Vlist"
- Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
-
- if [ "$Le_LinkIssuer" ]; then
- if ! _contains "$Le_LinkIssuer" ":"; then
- _info "$(__red "Relative issuer link found.")"
- Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
- fi
- _debug Le_LinkIssuer "$Le_LinkIssuer"
- _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
-
- _link_issuer_retry=0
- _MAX_ISSUER_RETRY=5
- while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
- _debug _link_issuer_retry "$_link_issuer_retry"
-
- if [ "$ACME_VERSION" = "2" ]; then
- if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then
- break
- fi
- else
- if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
- echo "$BEGIN_CERT" >"$CA_CERT_PATH"
- _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
- echo "$END_CERT" >>"$CA_CERT_PATH"
+ if [ "$ACME_VERSION" = "2" ]; then
+ _debug "v2 chain."
+ else
+ Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
- _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
- cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
- _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
+ if [ "$Le_LinkIssuer" ]; then
+ if ! _contains "$Le_LinkIssuer" ":"; then
+ _info "$(__red "Relative issuer link found.")"
+ Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
+ fi
+ _debug Le_LinkIssuer "$Le_LinkIssuer"
+ _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
- rm -f "$CA_CERT_PATH.der"
- break
+ _link_issuer_retry=0
+ _MAX_ISSUER_RETRY=5
+ while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
+ _debug _link_issuer_retry "$_link_issuer_retry"
+ if [ "$ACME_VERSION" = "2" ]; then
+ if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then
+ break
+ fi
+ else
+ if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
+ echo "$BEGIN_CERT" >"$CA_CERT_PATH"
+ _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
+ echo "$END_CERT" >>"$CA_CERT_PATH"
+ cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
+ rm -f "$CA_CERT_PATH.der"
+ break
+ fi
fi
+ _link_issuer_retry=$(_math $_link_issuer_retry + 1)
+ _sleep "$_link_issuer_retry"
+ done
+ if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
+ _err "Max retry for issuer ca cert is reached."
fi
- _link_issuer_retry=$(_math $_link_issuer_retry + 1)
- _sleep "$_link_issuer_retry"
- done
- if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
- _err "Max retry for issuer ca cert is reached."
+ else
+ _debug "No Le_LinkIssuer header found."
fi
- else
- _debug "No Le_LinkIssuer header found."
fi
+ [ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
+ [ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
Le_CertCreateTime=$(_time)
_savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
fi
IS_RENEW="1"
- 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"
+ 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"
res="$?"
if [ "$res" != "0" ]; then
return "$res"
return 1
fi
- _initpath
+ _real_cert="$3"
+ _real_key="$4"
+ _real_ca="$5"
+ _reload_cmd="$6"
+ _real_fullchain="$7"
+ _pre_hook="${8}"
+ _post_hook="${9}"
+ _renew_hook="${10}"
+ _local_addr="${11}"
+ _challenge_alias="${12}"
_csrsubj=$(_readSubjectFromCSR "$_csrfile")
if [ "$?" != "0" ]; then
return 1
fi
+ if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then
+ export ACME_VERSION=2
+ fi
_initpath "$_csrsubj" "$_csrkeylength"
mkdir -p "$DOMAIN_PATH"
_info "Copy csr to: $CSR_PATH"
cp "$_csrfile" "$CSR_PATH"
- issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength"
+ 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"
}
cat "$CERT_KEY_PATH" >"$_real_key"
else
cat "$CERT_KEY_PATH" >"$_real_key"
- chmod 700 "$_real_key"
+ chmod 600 "$_real_key"
fi
fi
_initAPI
- data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
+ if [ "$ACME_VERSION" = "2" ]; then
+ data="{\"certificate\": \"$cert\"}"
+ else
+ data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
+ fi
uri="${ACME_REVOKE_CERT}"
if [ -f "$CERT_KEY_PATH" ]; then
_d_type="$2"
_initpath
- if ! __get_domain_new_authz "$_d_domain"; then
- _err "Can not get domain new authz token."
- return 1
- fi
+ if [ "$ACME_VERSION" = "2" ]; then
+ _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
+ if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
+ _err "Can not get domain new order."
+ return 1
+ fi
+ _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
+ _debug2 _authorizations_seg "$_authorizations_seg"
+ if [ -z "$_authorizations_seg" ]; then
+ _err "_authorizations_seg not found."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
- authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
- _debug "authzUri" "$authzUri"
+ authzUri="$_authorizations_seg"
+ _debug2 "authzUri" "$authzUri"
+ if ! response="$(_get "$authzUri")"; then
+ _err "get to authz error."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
- if [ "$code" ] && [ ! "$code" = '201' ]; then
- _err "new-authz error: $response"
- return 1
+ response="$(echo "$response" | _normalizeJson)"
+ _debug2 response "$response"
+ _URL_NAME="url"
+ else
+ if ! __get_domain_new_authz "$_d_domain"; then
+ _err "Can not get domain new authz token."
+ return 1
+ fi
+
+ authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
+ _debug "authzUri" "$authzUri"
+ if [ "$code" ] && [ ! "$code" = '201' ]; then
+ _err "new-authz error: $response"
+ return 1
+ fi
+ _URL_NAME="uri"
fi
- entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')"
+ entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
if [ -z "$entries" ]; then
_info "No valid entries found."
if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)"
fi
_debug "Trigger validation."
- vtype="$VTYPE_HTTP"
+ vtype="$VTYPE_DNS"
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
- uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
+ uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
_debug uri "$uri"
keyauthorization="$token.$thumbprint"
_debug _vtype "$_vtype"
_info "Found $_vtype"
- uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
+ uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
_debug uri "$uri"
if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
_info "Deactivate: $_vtype"
- if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
+ if [ "$ACME_VERSION" = "2" ]; then
+ _djson="{\"status\":\"deactivated\"}"
+ else
+ _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
+ fi
+
+ if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate: $_vtype success."
else
_err "Can not deactivate $_vtype."
}
-# nocron confighome
+# nocron confighome noprofile
install() {
if [ -z "$LE_WORKING_DIR" ]; then
_nocron="$1"
_c_home="$2"
+ _noprofile="$3"
if ! _initpath; then
_err "Install failed."
return 1
_info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
- if [ "$IN_CRON" != "1" ]; then
+ if [ "$IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
_installalias "$_c_home"
fi
if [ -z "$NO_DETECT_SH" ]; then
#Modify shebang
if _exists bash; then
+ _bash_path="$(bash -c "command -v bash 2>/dev/null")"
+ if [ -z "$_bash_path" ]; then
+ _bash_path="$(bash -c 'echo $SHELL')"
+ fi
+ fi
+ if [ "$_bash_path" ]; then
_info "Good, bash is found, so change the shebang to use bash as preferred."
- _shebang='#!'"$(env bash -c "command -v bash")"
+ _shebang='#!'"$_bash_path"
_setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
for subf in $_SUB_FOLDERS; do
if [ -d "$LE_WORKING_DIR/$subf" ]; then
--renew, -r Renew a cert.
--renew-all Renew all the certs.
--revoke Revoke a cert.
- --remove Remove the cert from $PROJECT
+ --remove Remove the cert from list of certs known to $PROJECT_NAME.
--list List all the certs.
--showcsr Show the content of a csr.
--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.
Parameters:
--domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
+ --challenge-alias domain.tld The challenge domain alias for DNS alias mode: $_DNS_ALIAS_WIKI
+ --domain-alias domain.tld The domain alias for DNS alias mode: $_DNS_ALIAS_WIKI
--force, -f Used to force to install or force to renew a cert immediately.
--staging, --test Use staging server, just for test.
--debug Output debug info.
"
}
-# nocron
+# nocron noprofile
_installOnline() {
_info "Installing from online archive."
_nocron="$1"
+ _noprofile="$2"
if [ ! "$BRANCH" ]; then
BRANCH="master"
fi
cd "$PROJECT_NAME-$BRANCH"
chmod +x $PROJECT_ENTRY
- if ./$PROJECT_ENTRY install "$_nocron"; then
+ if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
_info "Install success!"
fi
_initpath
export LE_WORKING_DIR
cd "$LE_WORKING_DIR"
- _installOnline "nocron"
+ _installOnline "nocron" "noprofile"
); then
_info "Upgrade success!"
exit 0
_domain=""
_altdomains="$NO_VALUE"
_webroot=""
+ _challenge_alias=""
_keylength=""
_accountkeylength=""
_cert_file=""
return 1
fi
+ if _startswith "$_dvalue" "*."; then
+ _debug "Wildcard domain"
+ export ACME_VERSION=2
+ fi
if [ -z "$_domain" ]; then
_domain="$_dvalue"
else
fi
shift
;;
+ --challenge-alias)
+ cvalue="$2"
+ _challenge_alias="$_challenge_alias$cvalue,"
+ shift
+ ;;
+ --domain-alias)
+ cvalue="$DNS_ALIAS_PREFIX$2"
+ _challenge_alias="$_challenge_alias$cvalue,"
+ shift
+ ;;
--standalone)
wvalue="$NO_VALUE"
if [ -z "$_webroot" ]; then
HTTPS_INSECURE="1"
;;
--ca-bundle)
- _ca_bundle="$(_readlink -f "$2")"
+ _ca_bundle="$(_readlink "$2")"
CA_BUNDLE="$_ca_bundle"
shift
;;
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"
+ issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
;;
deploy)
deploy "$_domain" "$_deploy_hook" "$_ecc"
;;
signcsr)
- signcsr "$_csr" "$_webroot"
+ signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
;;
showcsr)
showcsr "$_csr" "$_domain"