From 5f944743304976a55c1e974c5835c5b2996c2ce2 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sat, 13 Jul 2019 20:01:06 +0800 Subject: [PATCH 1/5] support jdcloud.com (#2390) fix https://github.com/Neilpang/acme.sh/pull/2390 --- dnsapi/dns_jd.sh | 286 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 dnsapi/dns_jd.sh diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh new file mode 100644 index 0000000..d0f2a50 --- /dev/null +++ b/dnsapi/dns_jd.sh @@ -0,0 +1,286 @@ +#!/usr/bin/env sh + +# +#JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje" +#JD_ACCESS_KEY_SECRET="xxxxxxx" +#JD_REGION="cn-north-1" + +_JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey" + +_JD_PROD="clouddnsservice" +_JD_API="jdcloud-api.com" + +_JD_API_VERSION="v1" +_JD_DEFAULT_REGION="cn-north-1" + +_JD_HOST="$_JD_PROD.$_JD_API" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_jd_add() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + + if [ -z "$JD_ACCESS_KEY_ID" ] || [ -z "$JD_ACCESS_KEY_SECRET" ]; then + JD_ACCESS_KEY_ID="" + JD_ACCESS_KEY_SECRET="" + _err "You haven't specifed the jdcloud api key id or api key secret yet." + _err "Please create your key and try again. see $(__green $_JD_ACCOUNT)" + return 1 + fi + + _saveaccountconf_mutable JD_ACCESS_KEY_ID "$JD_ACCESS_KEY_ID" + _saveaccountconf_mutable JD_ACCESS_KEY_SECRET "$JD_ACCESS_KEY_SECRET" + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + else + _saveaccountconf_mutable JD_REGION "$JD_REGION" + fi + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + #_debug "Getting getViewTree" + + _debug "Adding records" + + _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":300,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + #_addrr='{"req":{"hostRecord":"xx","hostValue":"\"value4\"","jcloudRes":false,"mxPriority":null,"port":null,"ttl":300,"type":"TXT","weight":null,"viewValue":-1},"regionId":"cn-north-1","domainId":"8824"}' + if jd_rest POST "domain/$_domain_id/RRAdd" "" "$_addrr"; then + _rid="$(echo "$response" | tr '{},' '\n' | grep '"id":' | cut -d : -f 2)" + if [ -z "$_rid" ]; then + _err "Can not find record id from the result." + return 1 + fi + _info "TXT record added successfully." + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + if [ "$_srid" ]; then + _rid="$_srid,$_rid" + fi + _savedomainconf "JD_CLOUD_RIDS" "$_rid" + return 0 + fi + + return 1 +} + +dns_jd_rm() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + fi + + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + _info "Getting existing records for $fulldomain" + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + _debug _srid "$_srid" + + if [ -z "$_srid" ]; then + _err "Not rid skip" + return 0 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _cleardomainconf JD_CLOUD_RIDS + + _aws_tmpl_xml="{\"ids\":[$_srid],\"action\":\"del\",\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + + if jd_rest POST "domain/$_domain_id/RROperate" "" "$_aws_tmpl_xml" && _contains "$response" "\"code\":\"OK\""; then + _info "TXT record deleted successfully." + return 0 + fi + return 1 + +} + +#################### Private functions below ################################## + +_get_root() { + domain=$1 + i=1 + p=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" + if ! jd_rest GET "domain"; then + _err "error get domain list" + return 1 + fi + if [ -z "$h" ]; then + #not valid + _err "Invalid domain" + return 1 + fi + + if _contains "$response" "\"domainName\":\"$h\""; then + hostedzone="$(echo "$response" | tr '{}' '\n' | grep "\"domainName\":\"$h\"")" + _debug hostedzone "$hostedzone" + if [ "$hostedzone" ]; then + _domain_id="$(echo "$hostedzone" | tr ',' '\n' | grep "\"id\":" | cut -d : -f 2)" + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + fi + _err "Can't find domain with id: $h" + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +#method uri qstr data +jd_rest() { + mtd="$1" + ep="$2" + qsr="$3" + data="$4" + + _debug mtd "$mtd" + _debug ep "$ep" + _debug qsr "$qsr" + _debug data "$data" + + CanonicalURI="/$_JD_BASE_URI/$ep" + _debug2 CanonicalURI "$CanonicalURI" + + CanonicalQueryString="$qsr" + _debug2 CanonicalQueryString "$CanonicalQueryString" + + RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")" + #RequestDate="20190713T082155Z" ###################################################### + _debug2 RequestDate "$RequestDate" + export _H1="X-Jdcloud-Date: $RequestDate" + + RequestNonce="2bd0852a-8bae-4087-b2d5-$(_time)" + #RequestNonce="894baff5-72d4-4244-883a-7b2eb51e7fbe" ################################# + _debug2 RequestNonce "$RequestNonce" + export _H2="X-Jdcloud-Nonce: $RequestNonce" + + if [ "$data" ]; then + CanonicalHeaders="content-type:application/json\n" + SignedHeaders="content-type;" + else + CanonicalHeaders="" + SignedHeaders="" + fi + CanonicalHeaders="${CanonicalHeaders}host:$_JD_HOST\nx-jdcloud-date:$RequestDate\nx-jdcloud-nonce:$RequestNonce\n" + SignedHeaders="${SignedHeaders}host;x-jdcloud-date;x-jdcloud-nonce" + + _debug2 CanonicalHeaders "$CanonicalHeaders" + _debug2 SignedHeaders "$SignedHeaders" + + Hash="sha256" + + RequestPayload="$data" + _debug2 RequestPayload "$RequestPayload" + + RequestPayloadHash="$(printf "%s" "$RequestPayload" | _digest "$Hash" hex | _lower_case)" + _debug2 RequestPayloadHash "$RequestPayloadHash" + + CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$RequestPayloadHash" + _debug2 CanonicalRequest "$CanonicalRequest" + + HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)" + _debug2 HashedCanonicalRequest "$HashedCanonicalRequest" + + Algorithm="JDCLOUD2-HMAC-SHA256" + _debug2 Algorithm "$Algorithm" + + RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)" + _debug2 RequestDateOnly "$RequestDateOnly" + + Region="$JD_REGION" + Service="$_JD_PROD" + + CredentialScope="$RequestDateOnly/$Region/$Service/jdcloud2_request" + _debug2 CredentialScope "$CredentialScope" + + StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest" + + _debug2 StringToSign "$StringToSign" + + kSecret="JDCLOUD2$JD_ACCESS_KEY_SECRET" + + _secure_debug2 kSecret "$kSecret" + + kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")" + _secure_debug2 kSecretH "$kSecretH" + + kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" + _debug2 kDateH "$kDateH" + + kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)" + _debug2 kRegionH "$kRegionH" + + kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" + _debug2 kServiceH "$kServiceH" + + kSigningH="$(printf "%s" "jdcloud2_request" | _hmac "$Hash" "$kServiceH" hex)" + _debug2 kSigningH "$kSigningH" + + signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" + _debug2 signature "$signature" + + Authorization="$Algorithm Credential=$JD_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" + _debug2 Authorization "$Authorization" + + _H3="Authorization: $Authorization" + _debug _H3 "$_H3" + + url="https://$_JD_HOST$CanonicalURI" + if [ "$qsr" ]; then + url="https://$_JD_HOST$CanonicalURI?$qsr" + fi + + if [ "$mtd" = "GET" ]; then + response="$(_get "$url")" + else + response="$(_post "$data" "$url" "" "$mtd" "application/json")" + fi + + _ret="$?" + _debug2 response "$response" + if [ "$_ret" = "0" ]; then + if _contains "$response" "\"error\""; then + _err "Response error:$response" + return 1 + fi + fi + + return "$_ret" +} From 55dea4ee9d2187912ecf97689e12a6e8183a1091 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sat, 27 Jul 2019 11:48:29 +0800 Subject: [PATCH 2/5] sync (#2404) * support jdcloud.com * fix format * ttl 3000 * Escape slashes (#2375) * Change 1.1.1.1 to 1.0.0.1 to probe compatibility (#2330) As we can see, 1.1.1.1 is not routed or routed to an Intranet devices due to historical reason. Change 1.1.1.1 to 1.0.0.1 will have a better compatibility. I found this problem on my Tencent Cloud server. * check empty id * fix error * Add dnsapi for Vultr (#2370) * Add Vultr dns api * PushOver notifications (#2325) * PushOver notifications, using AppToken, UserKey, and optional sounds * fix errors * added dns api support for hexonet (#1776) * update * minor * support new Cloudflare Token format fix https://github.com/Neilpang/acme.sh/issues/2398 * fix wildcard domain name * add more info * fix https://github.com/Neilpang/acme.sh/issues/2377 * fix format * fix format --- acme.sh | 6 +- deploy/docker.sh | 4 +- dnsapi/dns_cf.sh | 87 ++++++++++----------- dnsapi/dns_hexonet.sh | 156 ++++++++++++++++++++++++++++++++++++++ dnsapi/dns_namecheap.sh | 2 +- dnsapi/dns_namesilo.sh | 11 ++- dnsapi/dns_vultr.sh | 163 ++++++++++++++++++++++++++++++++++++++++ notify/pushover.sh | 63 ++++++++++++++++ 8 files changed, 443 insertions(+), 49 deletions(-) create mode 100755 dnsapi/dns_hexonet.sh create mode 100644 dnsapi/dns_vultr.sh create mode 100644 notify/pushover.sh diff --git a/acme.sh b/acme.sh index 67fcdcb..891d2e1 100755 --- a/acme.sh +++ b/acme.sh @@ -3035,11 +3035,13 @@ _clearupdns() { d=$(_getfield "$entry" 1) txtdomain=$(_getfield "$entry" 2) aliasDomain=$(_getfield "$entry" 3) + _currentRoot=$(_getfield "$entry" 4) txt=$(_getfield "$entry" 5) d_api=$(_getfield "$entry" 6) _debug "d" "$d" _debug "txtdomain" "$txtdomain" _debug "aliasDomain" "$aliasDomain" + _debug "_currentRoot" "$_currentRoot" _debug "txt" "$txt" _debug "d_api" "$d_api" if [ "$d_api" = "$txt" ]; then @@ -3621,7 +3623,7 @@ _ns_purge_cf() { _cf_d="$1" _cf_d_type="$2" _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" - _cf_purl="https://1.1.1.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" + _cf_purl="https://1.0.0.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" response="$(_post "" "$_cf_purl")" _debug2 response "$response" } @@ -6787,7 +6789,7 @@ _process() { _debug "Using server: $_server" fi fi - + _debug "Running cmd: ${_CMD}" case "${_CMD}" in install) install "$_nocron" "$_confighome" "$_noprofile" ;; uninstall) uninstall "$_nocron" ;; diff --git a/deploy/docker.sh b/deploy/docker.sh index 4e55099..05333b3 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -126,6 +126,7 @@ docker_deploy() { fi if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then + _info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then return 1 fi @@ -223,7 +224,8 @@ _docker_cp() { _debug2 "_frompath" "$_frompath" _toname="$(basename "$_to")" _debug2 "_toname" "$_toname" - if ! tar --transform="s,$_frompath,$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then + _debug2 "_from" "$_from" + if ! tar --transform="s,$(printf "%s" "$_frompath" | tr '*' .),$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then _err "copy error" return 1 fi diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index cd93189..62e40ca 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -5,6 +5,9 @@ # #CF_Email="xxxx@sss.com" +#CF_Token="xxxx" +#CF_Account_ID="xxxx" + CF_Api="https://api.cloudflare.com/client/v4" ######## Public functions ##################### @@ -14,25 +17,32 @@ dns_cf_add() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi - if ! _contains "$CF_Email" "@"; then - _err "It seems that the CF_Email=$CF_Email is not a valid email address." - _err "Please check and retry." - return 1 - fi + if [ "$CF_Token" ]; then + _saveaccountconf_mutable CF_Token "$CF_Token" + _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID" + else + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then + CF_Key="" + CF_Email="" + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." + return 1 + fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable CF_Key "$CF_Key" - _saveaccountconf_mutable CF_Email "$CF_Email" + if ! _contains "$CF_Email" "@"; then + _err "It seems that the CF_Email=$CF_Email is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _saveaccountconf_mutable CF_Key "$CF_Key" + _saveaccountconf_mutable CF_Email "$CF_Email" + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -71,19 +81,6 @@ dns_cf_add() { fi _err "Add txt record error." return 1 - # else - # _info "Updating record" - # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - # _debug "record_id" "$record_id" - # - # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - # if [ "$?" = "0" ]; then - # _info "Updated, OK" - # return 0 - # fi - # _err "Update error" - # return 1 - # fi } @@ -92,15 +89,10 @@ dns_cf_rm() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -157,8 +149,14 @@ _get_root() { return 1 fi - if ! _cf_rest GET "zones?name=$h"; then - return 1 + if [ "$CF_Account_ID" ]; then + if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then + return 1 + fi + else + if ! _cf_rest GET "zones?name=$h"; then + return 1 + fi fi if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then @@ -182,12 +180,17 @@ _cf_rest() { data="$3" _debug "$ep" - email_trimmed=$(echo $CF_Email | tr -d '"') - key_trimmed=$(echo $CF_Key | tr -d '"') + email_trimmed=$(echo "$CF_Email" | tr -d '"') + key_trimmed=$(echo "$CF_Key" | tr -d '"') + token_trimmed=$(echo "$CF_Token" | tr -d '"') - export _H1="X-Auth-Email: $email_trimmed" - export _H2="X-Auth-Key: $key_trimmed" - export _H3="Content-Type: application/json" + export _H1="Content-Type: application/json" + if [ "$token_trimmed" ]; then + export _H2="Authorization: Bearer $token_trimmed" + else + export _H2="X-Auth-Email: $email_trimmed" + export _H3="X-Auth-Key: $key_trimmed" + fi if [ "$m" != "GET" ]; then _debug data "$data" diff --git a/dnsapi/dns_hexonet.sh b/dnsapi/dns_hexonet.sh new file mode 100755 index 0000000..f150311 --- /dev/null +++ b/dnsapi/dns_hexonet.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env sh + +# +# Hexonet_Login="username!roleId" +# +# Hexonet_Password="rolePassword" + +Hexonet_Api="https://coreapi.1api.net/api/call.cgi" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_hexonet_add() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Login and Hexonet_Password" + return 1 + fi + + if ! _contains "$Hexonet_Login" "!"; then + _err "It seems that the Hexonet_Login=$Hexonet_Login is not a restrivteed user." + _err "Please check and retry." + return 1 + fi + + #save the username and password to the account conf file. + _saveaccountconf_mutable Hexonet_Login "$Hexonet_Login" + _saveaccountconf_mutable Hexonet_Password "$Hexonet_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + _info "Adding record" + if _hexonet_rest "command=UpdateDNSZone&dnszone=${_domain}.&addrr0=${_sub_domain}%20IN%20TXT%20${txtvalue}"; then + if _contains "$response" "CODE=200"; then + _info "Added, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + return 1 + +} + +#fulldomain txtvalue +dns_hexonet_rm() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Login and Hexonet_Password" + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT&RR=${txtvalue}" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "PROPERTY[TOTAL][0]=" | cut -d = -f 2) + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Don't need to remove." + else + if ! _hexonet_rest "&command=UpdateDNSZone&dnszone=${_domain}.&delrr0='${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""; then + _err "Delete record error." + return 1 + fi + _contains "$response" "CODE=200" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=1 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if ! _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}."; then + return 1 + fi + + if _contains "$response" "CODE=200"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_hexonet_rest() { + query_params="$1" + _debug "$query_params" + + response="$(_get "${Hexonet_Api}?s_login=${Hexonet_Login}&s_pw=${Hexonet_Password}&${query_params}")" + + if [ "$?" != "0" ]; then + _err "error $query_params" + return 1 + fi + _debug2 response "$response" + return 0 +} diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 6553deb..a82e12d 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -164,7 +164,7 @@ _namecheap_set_publicip() { _debug sourceip "$NAMECHEAP_SOURCEIP" ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*') + addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https):\/\/.*') _debug2 ip "$ip" _debug2 addr "$addr" diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh index dc1a4fd..ed6d0e0 100755 --- a/dnsapi/dns_namesilo.sh +++ b/dnsapi/dns_namesilo.sh @@ -59,9 +59,14 @@ dns_namesilo_rm() { if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then retcode=$(printf "%s\n" "$response" | _egrep_o "300") if [ "$retcode" ]; then - _record_id=$(printf "%s\n" "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1) - _debug record_id "$_record_id" - _info "Successfully retrieved the record id for ACME challenge." + _record_id=$(echo "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1) + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi else _err "Unable to retrieve the record id." return 1 diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh new file mode 100644 index 0000000..f15e7c4 --- /dev/null +++ b/dnsapi/dns_vultr.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env sh + +# +#VULTR_API_KEY=000011112222333344445555666677778888 + +VULTR_Api="https://api.vultr.com/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_vultr_add() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY='' + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + if ! _vultr_rest POST 'dns/create_record' "domain=$_domain&name=$_sub_domain&data=\"$txtvalue\"&type=TXT"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#fulldomain txtvalue +dns_vultr_rm() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY="" + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep "$txtvalue" | tr ',' '\n' | grep -i 'RECORDID' | cut -d : -f 2)" + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi + + if ! _vultr_rest POST 'dns/delete_record' "domain=$_domain&RECORDID=$_record_id"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + return 1 + fi + + if ! _vultr_rest GET "dns/list"; then + return 1 + fi + + if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then + if _contains "$response" "\"domain\":\"$_domain\""; then + _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" + _domain=$_domain + return 0 + else + _err 'Invalid domain' + return 1 + fi + else + _err "$response" + return 1 + fi + i=$(_math "$i" + 1) + done + + return 1 +} + +_vultr_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"') + + export _H1="Api-Key: $api_key_trimmed" + export _H2='Content-Type: application/x-www-form-urlencoded' + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$VULTR_Api/$ep" "" "$m")" + else + response="$(_get "$VULTR_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "Error $ep" + return 1 + fi + + _debug2 response "$response" + return 0 +} diff --git a/notify/pushover.sh b/notify/pushover.sh new file mode 100644 index 0000000..a07cbd3 --- /dev/null +++ b/notify/pushover.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env sh + +#Support for pushover.net's api. Push notification platform for multiple platforms +#PUSHOVER_TOKEN="" Required, pushover application token +#PUSHOVER_USER="" Required, pushover userkey +#PUSHOVER_DEVICE="" Optional, Specific device or devices by hostnames, joining multiples with a comma (such as device=iphone,nexus5) +#PUSHOVER_PRIORITY="" Optional, Lowest Priority (-2), Low Priority (-1), Normal Priority (0), High Priority (1) + +PUSHOVER_URI="https://api.pushover.net/1/messages.json" + +pushover_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + PUSHOVER_TOKEN="${PUSHOVER_TOKEN:-$(_readaccountconf_mutable PUSHOVER_TOKEN)}" + if [ -z "$PUSHOVER_TOKEN" ]; then + PUSHOVER_TOKEN="" + _err "You didn't specify a PushOver application token yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_TOKEN "$PUSHOVER_TOKEN" + + PUSHOVER_USER="${PUSHOVER_USER:-$(_readaccountconf_mutable PUSHOVER_USER)}" + if [ -z "$PUSHOVER_USER" ]; then + PUSHOVER_USER="" + _err "You didn't specify a PushOver UserKey yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_USER "$PUSHOVER_USER" + + PUSHOVER_DEVICE="${PUSHOVER_DEVICE:-$(_readaccountconf_mutable PUSHOVER_DEVICE)}" + if [ "$PUSHOVER_DEVICE" ]; then + _saveaccountconf_mutable PUSHOVER_DEVICE "$PUSHOVER_DEVICE" + fi + + PUSHOVER_PRIORITY="${PUSHOVER_PRIORITY:-$(_readaccountconf_mutable PUSHOVER_PRIORITY)}" + if [ "$PUSHOVER_PRIORITY" ]; then + _saveaccountconf_mutable PUSHOVER_PRIORITY "$PUSHOVER_PRIORITY" + fi + + PUSHOVER_SOUND="${PUSHOVER_SOUND:-$(_readaccountconf_mutable PUSHOVER_SOUND)}" + if [ "$PUSHOVER_SOUND" ]; then + _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" + fi + + export _H1="Content-Type: application/json" + _content="$(printf "*%s*\n" "$_content" | _json_encode)" + _subject="$(printf "*%s*\n" "$_subject" | _json_encode)" + _data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}" + + response="" #just make shellcheck happy + if _post "$_data" "$PUSHOVER_URI"; then + if _contains "$response" "{\"status\":1"; then + _info "PUSHOVER send success." + return 0 + fi + fi + _err "PUSHOVER send error." + _err "$response" + return 1 +} From ee38cccad8f76b807206165324e7bf771aa981dc Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sun, 11 Aug 2019 11:56:59 +0800 Subject: [PATCH 3/5] sync (#2436) * fix https://github.com/Neilpang/acme.sh/issues/2409 (#2430) * Add variable exports for Successful Post Hook and Renew Hook calls (#2431) * fixed json parse regex for support api gcore_cdn (#2381) --- acme.sh | 10 ++++++++++ deploy/gcore_cdn.sh | 3 +-- dnsapi/dns_he.sh | 12 ++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 8452d58..4742e61 100755 --- a/acme.sh +++ b/acme.sh @@ -3265,6 +3265,11 @@ _on_issue_success() { if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_post_hook" ); then _err "Error when run post hook." @@ -3276,6 +3281,11 @@ _on_issue_success() { if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then _info "Run renew hook:'$_chk_renew_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_renew_hook" ); then _err "Error when run renew hook." diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index e0921bc..bbda58e 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh # Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). -# Uses command line curl for send requests and jq for parse responses. # Returns 0 when success. # # Written by temoffey @@ -117,7 +116,7 @@ gcore_cdn_deploy() { _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" - _regex=".*\"sslData\":\([0-9]*\)}.*$" + _regex=".*\"sslData\":\([0-9]*\).*$" _debug _regex "$_regex" _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataNew "$_sslDataNew" diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index df00c74..caa4d2c 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -134,9 +134,9 @@ _find_zone() { _zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&') _zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2) _debug2 "These are the zones on this HE account:" - _debug2 "$_zone_names" + _debug2 "_zone_names" "$_zone_names" _debug2 "And these are their respective IDs:" - _debug2 "$_zone_ids" + _debug2 "_zone_ids" "$_zone_ids" if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then _err "Can not get zone names." return 1 @@ -154,10 +154,14 @@ _find_zone() { _debug "Looking for zone \"${_attempted_zone}\"" - line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone" | cut -d : -f 1)" - + line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone\$" | _head_n 1 | cut -d : -f 1)" + _debug2 line_num "$line_num" if [ "$line_num" ]; then _zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p") + if [ -z "$_zone_id" ]; then + _err "Can not find zone id." + return 1 + fi _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." return 0 fi From a3361806ab49043fca46f81a0edc2357b7d3947c Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sun, 11 Aug 2019 22:43:07 +0800 Subject: [PATCH 4/5] sync (#2437) * fix https://github.com/Neilpang/acme.sh/issues/2409 (#2430) * Add variable exports for Successful Post Hook and Renew Hook calls (#2431) * fixed json parse regex for support api gcore_cdn (#2381) * start 2.8.3 Forbidden sudo --- acme.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4742e61..72bf070 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.2 +VER=2.8.3 PROJECT_NAME="acme.sh" @@ -135,6 +135,8 @@ _DNS_MANUAL_WIKI="https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode" _NOTIFY_WIKI="https://github.com/Neilpang/acme.sh/wiki/notify" +_SUDO_WIKI="https://github.com/Neilpang/acme.sh/wiki/sudo" + _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" @@ -6233,6 +6235,23 @@ _processAccountConf() { } +_checkSudo() { + 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 + return 0 + fi + if [ "$SUDO_COMMAND" = "/bin/su" ]; then + #it's a normal user doing "sudo su" + #fine + return 0 + fi + #otherwise + return 1 + fi + return 0 +} + _process() { _CMD="" _domain="" @@ -6761,6 +6780,14 @@ _process() { done if [ "${_CMD}" != "install" ]; then + if [ "$__INTERACTIVE" ] && ! _checkSudo; then + if [ -z "$FORCE" ]; then + #Use "echo" here, instead of _info. it's too early + echo "It seems that you are using sudo, please read this link first:" + echo "$_SUDO_WIKI" + return 1 + fi + fi __initHome if [ "$_log" ]; then if [ -z "$_logfile" ]; then From 0ca46774ac207517724eb48338c04a4dbde0728a Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Mon, 2 Sep 2019 10:36:10 +0800 Subject: [PATCH 5/5] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..24be2c4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: acmesh +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']