From 0781e8cf12f09fad71d0d63ce59c49c701b35d50 Mon Sep 17 00:00:00 2001 From: Marcel Waldvogel Date: Mon, 12 Oct 2020 13:52:57 +0200 Subject: [PATCH 01/28] Use random hour for cron job The hour for the cron job isn't really random (as is the minute), but assuming acme.sh installation times are not correlated, neither will be the resulting cron start times. --- acme.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 3be3849de1..eb8aca4956 100755 --- a/acme.sh +++ b/acme.sh @@ -5590,6 +5590,7 @@ installcronjob() { fi _t=$(_time) random_minute=$(_math $_t % 60) + random_hour=$(_math $_t / 60 % 24) if ! _exists "$_CRONTAB" && _exists "fcrontab"; then _CRONTAB="fcrontab" @@ -5616,12 +5617,12 @@ installcronjob() { if _exists uname && uname -a | grep SunOS >/dev/null; then $_CRONTAB -l | { cat - echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" + echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" } | $_CRONTAB -- else $_CRONTAB -l | { cat - echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" + echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" } | $_CRONTAB - fi fi From 92dbe6cdf8eb7d3b07b84876db79032c1d41f9c6 Mon Sep 17 00:00:00 2001 From: Marcel Waldvogel Date: Mon, 12 Oct 2020 14:20:40 +0200 Subject: [PATCH 02/28] Simplify and clarify SunOS crontab differences --- acme.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index eb8aca4956..2ea5ccd782 100755 --- a/acme.sh +++ b/acme.sh @@ -5615,16 +5615,14 @@ installcronjob() { _info "Installing cron job" if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then if _exists uname && uname -a | grep SunOS >/dev/null; then - $_CRONTAB -l | { - cat - echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" - } | $_CRONTAB -- + _CRONTAB_STDIN="$_CRONTAB --" else - $_CRONTAB -l | { - cat - echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" - } | $_CRONTAB - + _CRONTAB_STDIN="$_CRONTAB -" fi + $_CRONTAB -l | { + cat + echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" + } | $_CRONTAB_STDIN fi if [ "$?" != "0" ]; then _err "Install cron job failed. You need to manually renew your certs." From bcf63b5d270a4535347e64e7f25f24970e474e6f Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sun, 27 Feb 2022 14:17:34 +0100 Subject: [PATCH 03/28] Add ArtFiles.de DNS API plugin --- dnsapi/dns_artfiles.sh | 176 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 dnsapi/dns_artfiles.sh diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh new file mode 100644 index 0000000000..c857060a2b --- /dev/null +++ b/dnsapi/dns_artfiles.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env sh + +################################################################################ +# ACME.sh 3rd party DNS API plugin for ArtFiles.de +################################################################################ +# Author: Martin Arndt, https://troublezone.net/ +# Released: 2022-02-27 +# Issues: https://github.com/acmesh-official/acme.sh/issues/XXXX +################################################################################ +# Usage: +# 1. export AF_API_Username="api12345678" +# 2. export AF_API_Password="apiPassword" +# 3. acme.sh --issue -d example.com --dns dns_artfiles +################################################################################ + +########## API configuration ################################################### +AF_API_SUCCESS='status":"OK' +AF_URL_DCP='https://dcp.c.artfiles.de/api/' +AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain=' +AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html' + +########## Public functions #################################################### + +# Adds a new TXT record for given ACME challenge value & domain. +# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value" +dns_artfiles_add() { + domain="$1" + txtValue="$2" + _info 'Using ArtFiles.de DNS addition API' + _debug 'Domain' "$domain" + _debug 'txtValue' "$txtValue" + + AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}" + AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}" + if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then + _err 'Missing ArtFiles.de username and/or password.' + _err 'Please ensure both are set via export command & try again.' + + return 1 + fi + + _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME" + _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD" + + _set_headers + _get_zone "$domain" + _dns 'GET' + if ! _contains "$response" 'TXT'; then + _err 'Retrieving TXT records failed.' + + return 1 + fi + + _clean_records + _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")" + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err 'Adding ACME challenge value failed.' + + return 1 + fi +} + +# Removes the existing TXT record for given ACME challenge value & domain. +# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value" +dns_artfiles_rm() { + domain="$1" + txtValue="$2" + _info 'Using ArtFiles.de DNS removal API' + _debug 'Domain' "$domain" + _debug 'txtValue' "$txtValue" + + _set_headers + _get_zone "$domain" + if ! _dns 'GET'; then + return 1 + fi + + if ! _contains "$response" "$txtValue"; then + _err 'Retrieved TXT records are missing given ACME challenge value.' + + return 1 + fi + + _clean_records + response="$(printf -- '%s' "$response" | sed '$d')" + _dns 'SET' "$response" + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err 'Removing ACME challenge value failed.' + + return 1 + fi +} + +########## Private functions ################################################### + +# Cleans awful TXT records response of ArtFiles's API & pretty prints it. +# Usage: _clean_records +_clean_records() +{ + # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid + # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\' + # from '\"' & turn '\n' into real LF characters. + # Yup, awful API to use - but that's all we got to get this working, so... ;) + _debug2 'Raw ' "$response" + response="$(printf -- '%s' "$response" + \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')" + _debug2 'Clean' "$response" +} + +# Executes an HTTP GET or POST request for getting or setting DNS records, +# containing given payload upon POST. +# Usage: _dns [GET | SET] [payload] +_dns() +{ + action="$1" + payload="$(printf -- '%s' "$2" | _url_encode)" + url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" + \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')" + + if [ "$action" = 'SET' ]; then + _debug2 'Payload' "$payload" + response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')" + else + response="$(_get "$url" '' 10)" + fi + + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err "DNS API error: $response" + + return 1 + fi + + _debug 'Response' "$response" + + return 0 +} + +# Gets the root domain zone for given domain. +# Usage: _get_zone _acme-challenge.www.example.com +_get_zone() +{ + _info 'Getting domain zone...' + _debug2 'Initial FQDN' "$1" + fqdn="$1" + fqdn="${fqdn#*.}" # Strip "_acme-challenge" right away + _debug2 'Reduced FQDN' "$fqdn" + + domains="$(_get "$AF_URL_DOMAINS" '' 10)" + while true; do + if _contains "$domains" "$fqdn"; then + domain="$fqdn" + _info "Found root domain zone: $domain" + break + else + fqdn="${fqdn#*.}" + _debug2 'FQDN' "$fqdn" + fi + done + + if [ "$domain" = "$fqdn" ]; then + return 0 + fi + + _err "Couldn't find root domain zone." + + return 1 +} + +# Adds the HTTP Authorization & Content-Type headers to a follow-up request. +# Usage: _set_headers +_set_headers() +{ + encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" + export _H1="Authorization: Basic $encoded" + export _H2='Content-Type: application/json' +} From 72d02f442e2e8528afb04a0e55c88185bb99a7e9 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sun, 27 Feb 2022 14:35:21 +0100 Subject: [PATCH 04/28] Fix formatting according to Shellcheck --- dnsapi/dns_artfiles.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index c857060a2b..4598854da9 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -95,8 +95,7 @@ dns_artfiles_rm() { # Cleans awful TXT records response of ArtFiles's API & pretty prints it. # Usage: _clean_records -_clean_records() -{ +_clean_records() { # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\' # from '\"' & turn '\n' into real LF characters. @@ -110,8 +109,7 @@ _clean_records() # Executes an HTTP GET or POST request for getting or setting DNS records, # containing given payload upon POST. # Usage: _dns [GET | SET] [payload] -_dns() -{ +_dns() { action="$1" payload="$(printf -- '%s' "$2" | _url_encode)" url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" @@ -137,8 +135,7 @@ _dns() # Gets the root domain zone for given domain. # Usage: _get_zone _acme-challenge.www.example.com -_get_zone() -{ +_get_zone() { _info 'Getting domain zone...' _debug2 'Initial FQDN' "$1" fqdn="$1" @@ -168,8 +165,7 @@ _get_zone() # Adds the HTTP Authorization & Content-Type headers to a follow-up request. # Usage: _set_headers -_set_headers() -{ +_set_headers() { encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" export _H1="Authorization: Basic $encoded" export _H2='Content-Type: application/json' From 0bea2e2b94e49f59e05c7507bcec548c50a4abac Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sun, 27 Feb 2022 14:37:22 +0100 Subject: [PATCH 05/28] Fix formatting according to Shellcheck 2/2 --- dnsapi/dns_artfiles.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 4598854da9..f83ce64a55 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -101,8 +101,9 @@ _clean_records() { # from '\"' & turn '\n' into real LF characters. # Yup, awful API to use - but that's all we got to get this working, so... ;) _debug2 'Raw ' "$response" - response="$(printf -- '%s' "$response" - \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')" + response="$( + printf -- '%s' "$response" + \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')" _debug2 'Clean' "$response" } @@ -112,8 +113,9 @@ _clean_records() { _dns() { action="$1" payload="$(printf -- '%s' "$2" | _url_encode)" - url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" - \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')" + url="$( + printf -- '%s%s' "$AF_URL_DNS" "$domain" + \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')" if [ "$action" = 'SET' ]; then _debug2 'Payload' "$payload" From fb457968ecae380bc17914f22d134bcfba93d304 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sun, 27 Feb 2022 14:38:24 +0100 Subject: [PATCH 06/28] Fix formatting according to Shellcheck 3/3 --- dnsapi/dns_artfiles.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index f83ce64a55..9c1a4338d4 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -103,7 +103,8 @@ _clean_records() { _debug2 'Raw ' "$response" response="$( printf -- '%s' "$response" - \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')" + \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g' + )" _debug2 'Clean' "$response" } @@ -115,7 +116,8 @@ _dns() { payload="$(printf -- '%s' "$2" | _url_encode)" url="$( printf -- '%s%s' "$AF_URL_DNS" "$domain" - \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')" + \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/' + )" if [ "$action" = 'SET' ]; then _debug2 'Payload' "$payload" From ed56d52af309a4d90747169a321dbd3698a4b71a Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sun, 27 Feb 2022 15:12:05 +0100 Subject: [PATCH 07/28] Changed GitHub issues URL --- dnsapi/dns_artfiles.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 9c1a4338d4..8e0a6151bb 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -5,7 +5,7 @@ ################################################################################ # Author: Martin Arndt, https://troublezone.net/ # Released: 2022-02-27 -# Issues: https://github.com/acmesh-official/acme.sh/issues/XXXX +# Issues: https://github.com/Eagle3386/acme.sh/issues ################################################################################ # Usage: # 1. export AF_API_Username="api12345678" From 13c71829485aac03e1d0da98784ea50e983e73cd Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sat, 18 Jun 2022 17:32:56 +0200 Subject: [PATCH 08/28] Fix usage docs in file's header comment --- dnsapi/dns_artfiles.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 8e0a6151bb..2f8e158f02 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -8,8 +8,8 @@ # Issues: https://github.com/Eagle3386/acme.sh/issues ################################################################################ # Usage: -# 1. export AF_API_Username="api12345678" -# 2. export AF_API_Password="apiPassword" +# 1. export AF_API_USERNAME="api12345678" +# 2. export AF_API_PASSWORD="apiPassword" # 3. acme.sh --issue -d example.com --dns dns_artfiles ################################################################################ From 63fca33b0459b1483c91c38a9ae3060bb6ef621a Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Mon, 29 May 2023 20:12:52 +0200 Subject: [PATCH 09/28] Fix retrieval of domain zone --- dnsapi/dns_artfiles.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 2f8e158f02..703c02ac35 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -140,14 +140,13 @@ _dns() { # Gets the root domain zone for given domain. # Usage: _get_zone _acme-challenge.www.example.com _get_zone() { - _info 'Getting domain zone...' - _debug2 'Initial FQDN' "$1" fqdn="$1" - fqdn="${fqdn#*.}" # Strip "_acme-challenge" right away - _debug2 'Reduced FQDN' "$fqdn" + domains="$(_get "$AF_URL_DOMAINS" "" 10)" + _info 'Getting domain zone...' + _debug2 'FQDN' "$fqdn" + _debug2 'Domains' "$domains" - domains="$(_get "$AF_URL_DOMAINS" '' 10)" - while true; do + while _contains "$fqdn" "."; do if _contains "$domains" "$fqdn"; then domain="$fqdn" _info "Found root domain zone: $domain" @@ -162,7 +161,7 @@ _get_zone() { return 0 fi - _err "Couldn't find root domain zone." + _err 'Couldn\'t find root domain zone.' return 1 } From fb33ea2a0b6fc8ff260da123709b0872c6b91e79 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Mon, 29 May 2023 20:21:16 +0200 Subject: [PATCH 10/28] Fix single quote escaping --- dnsapi/dns_artfiles.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 703c02ac35..ad547e3f9d 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -161,7 +161,7 @@ _get_zone() { return 0 fi - _err 'Couldn\'t find root domain zone.' + _err 'Couldn'\''t find root domain zone.' return 1 } From d108072bfbc057f895d29620a8ecd6752ebe47f6 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Tue, 30 May 2023 09:24:17 +0200 Subject: [PATCH 11/28] Add ArtFiles.de DNS API plugin --- dnsapi/dns_artfiles.sh | 175 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 dnsapi/dns_artfiles.sh diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh new file mode 100644 index 0000000000..ad547e3f9d --- /dev/null +++ b/dnsapi/dns_artfiles.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env sh + +################################################################################ +# ACME.sh 3rd party DNS API plugin for ArtFiles.de +################################################################################ +# Author: Martin Arndt, https://troublezone.net/ +# Released: 2022-02-27 +# Issues: https://github.com/Eagle3386/acme.sh/issues +################################################################################ +# Usage: +# 1. export AF_API_USERNAME="api12345678" +# 2. export AF_API_PASSWORD="apiPassword" +# 3. acme.sh --issue -d example.com --dns dns_artfiles +################################################################################ + +########## API configuration ################################################### +AF_API_SUCCESS='status":"OK' +AF_URL_DCP='https://dcp.c.artfiles.de/api/' +AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain=' +AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html' + +########## Public functions #################################################### + +# Adds a new TXT record for given ACME challenge value & domain. +# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value" +dns_artfiles_add() { + domain="$1" + txtValue="$2" + _info 'Using ArtFiles.de DNS addition API' + _debug 'Domain' "$domain" + _debug 'txtValue' "$txtValue" + + AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}" + AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}" + if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then + _err 'Missing ArtFiles.de username and/or password.' + _err 'Please ensure both are set via export command & try again.' + + return 1 + fi + + _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME" + _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD" + + _set_headers + _get_zone "$domain" + _dns 'GET' + if ! _contains "$response" 'TXT'; then + _err 'Retrieving TXT records failed.' + + return 1 + fi + + _clean_records + _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")" + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err 'Adding ACME challenge value failed.' + + return 1 + fi +} + +# Removes the existing TXT record for given ACME challenge value & domain. +# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value" +dns_artfiles_rm() { + domain="$1" + txtValue="$2" + _info 'Using ArtFiles.de DNS removal API' + _debug 'Domain' "$domain" + _debug 'txtValue' "$txtValue" + + _set_headers + _get_zone "$domain" + if ! _dns 'GET'; then + return 1 + fi + + if ! _contains "$response" "$txtValue"; then + _err 'Retrieved TXT records are missing given ACME challenge value.' + + return 1 + fi + + _clean_records + response="$(printf -- '%s' "$response" | sed '$d')" + _dns 'SET' "$response" + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err 'Removing ACME challenge value failed.' + + return 1 + fi +} + +########## Private functions ################################################### + +# Cleans awful TXT records response of ArtFiles's API & pretty prints it. +# Usage: _clean_records +_clean_records() { + # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid + # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\' + # from '\"' & turn '\n' into real LF characters. + # Yup, awful API to use - but that's all we got to get this working, so... ;) + _debug2 'Raw ' "$response" + response="$( + printf -- '%s' "$response" + \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g' + )" + _debug2 'Clean' "$response" +} + +# Executes an HTTP GET or POST request for getting or setting DNS records, +# containing given payload upon POST. +# Usage: _dns [GET | SET] [payload] +_dns() { + action="$1" + payload="$(printf -- '%s' "$2" | _url_encode)" + url="$( + printf -- '%s%s' "$AF_URL_DNS" "$domain" + \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/' + )" + + if [ "$action" = 'SET' ]; then + _debug2 'Payload' "$payload" + response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')" + else + response="$(_get "$url" '' 10)" + fi + + if ! _contains "$response" "$AF_API_SUCCESS"; then + _err "DNS API error: $response" + + return 1 + fi + + _debug 'Response' "$response" + + return 0 +} + +# Gets the root domain zone for given domain. +# Usage: _get_zone _acme-challenge.www.example.com +_get_zone() { + fqdn="$1" + domains="$(_get "$AF_URL_DOMAINS" "" 10)" + _info 'Getting domain zone...' + _debug2 'FQDN' "$fqdn" + _debug2 'Domains' "$domains" + + while _contains "$fqdn" "."; do + if _contains "$domains" "$fqdn"; then + domain="$fqdn" + _info "Found root domain zone: $domain" + break + else + fqdn="${fqdn#*.}" + _debug2 'FQDN' "$fqdn" + fi + done + + if [ "$domain" = "$fqdn" ]; then + return 0 + fi + + _err 'Couldn'\''t find root domain zone.' + + return 1 +} + +# Adds the HTTP Authorization & Content-Type headers to a follow-up request. +# Usage: _set_headers +_set_headers() { + encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" + export _H1="Authorization: Basic $encoded" + export _H2='Content-Type: application/json' +} From db8a2d0c653ba19df024c8289942166d7e5bb025 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:05:06 +0200 Subject: [PATCH 12/28] Fix & improve DNS API for ArtFiles.de --- dnsapi/dns_artfiles.sh | 80 ++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index ad547e3f9d..6224c41652 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -5,40 +5,34 @@ ################################################################################ # Author: Martin Arndt, https://troublezone.net/ # Released: 2022-02-27 -# Issues: https://github.com/Eagle3386/acme.sh/issues +# Issues: https://github.com/acmesh-official/acme.sh/issues/XXXX ################################################################################ # Usage: -# 1. export AF_API_USERNAME="api12345678" -# 2. export AF_API_PASSWORD="apiPassword" +# 1. export AF_API_USERNAME='api12345678' +# 2. export AF_API_PASSWORD='apiPassword' # 3. acme.sh --issue -d example.com --dns dns_artfiles ################################################################################ ########## API configuration ################################################### + AF_API_SUCCESS='status":"OK' AF_URL_DCP='https://dcp.c.artfiles.de/api/' AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain=' -AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html' +AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html' ########## Public functions #################################################### # Adds a new TXT record for given ACME challenge value & domain. # Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value" -dns_artfiles_add() { +dns_artfiles_add() +{ domain="$1" txtValue="$2" - _info 'Using ArtFiles.de DNS addition API' + _info 'Using ArtFiles.de DNS addition API…' _debug 'Domain' "$domain" _debug 'txtValue' "$txtValue" - AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}" - AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}" - if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then - _err 'Missing ArtFiles.de username and/or password.' - _err 'Please ensure both are set via export command & try again.' - - return 1 - fi - + _set_credentials _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME" _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD" @@ -62,13 +56,15 @@ dns_artfiles_add() { # Removes the existing TXT record for given ACME challenge value & domain. # Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value" -dns_artfiles_rm() { +dns_artfiles_rm() +{ domain="$1" txtValue="$2" - _info 'Using ArtFiles.de DNS removal API' + _info 'Using ArtFiles.de DNS removal API…' _debug 'Domain' "$domain" _debug 'txtValue' "$txtValue" + _set_credentials _set_headers _get_zone "$domain" if ! _dns 'GET'; then @@ -95,29 +91,28 @@ dns_artfiles_rm() { # Cleans awful TXT records response of ArtFiles's API & pretty prints it. # Usage: _clean_records -_clean_records() { +_clean_records() +{ + _info 'Cleaning TXT records…' # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\' # from '\"' & turn '\n' into real LF characters. - # Yup, awful API to use - but that's all we got to get this working, so... ;) + # Yup, awful API to use - but that's all we got to get this working, so… ;) _debug2 'Raw ' "$response" - response="$( - printf -- '%s' "$response" - \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g' - )" + + response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')" _debug2 'Clean' "$response" } # Executes an HTTP GET or POST request for getting or setting DNS records, # containing given payload upon POST. # Usage: _dns [GET | SET] [payload] -_dns() { +_dns() +{ + _info 'Executing HTTP request…' action="$1" payload="$(printf -- '%s' "$2" | _url_encode)" - url="$( - printf -- '%s%s' "$AF_URL_DNS" "$domain" - \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/' - )" + url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')" if [ "$action" = 'SET' ]; then _debug2 'Payload' "$payload" @@ -139,10 +134,11 @@ _dns() { # Gets the root domain zone for given domain. # Usage: _get_zone _acme-challenge.www.example.com -_get_zone() { +_get_zone() +{ fqdn="$1" - domains="$(_get "$AF_URL_DOMAINS" "" 10)" - _info 'Getting domain zone...' + domains="$(_get "$AF_URL_DOMAINS" '' 10)" + _info 'Getting domain zone…' _debug2 'FQDN' "$fqdn" _debug2 'Domains' "$domains" @@ -166,10 +162,26 @@ _get_zone() { return 1 } +# Sets the credentials for accessing ArtFiles's API +# Usage: _set_credentials +_set_credentials() +{ + _info 'Setting credentials…' + AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}" + AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}" + if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then + _err 'Missing ArtFiles.de username and/or password.' + _err 'Please ensure both are set via export command & try again.' + + return 1 + fi +} + # Adds the HTTP Authorization & Content-Type headers to a follow-up request. # Usage: _set_headers -_set_headers() { - encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" - export _H1="Authorization: Basic $encoded" +_set_headers() +{ + _info 'Setting headers…' + export _H1="$(printf -- 'Authorization: Basic %s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" export _H2='Content-Type: application/json' } From 2961a90e7ffb37ad498a8d841d00dbb2a08d251c Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:14:49 +0200 Subject: [PATCH 13/28] Make ShellCheck & ShellFormat happy --- dnsapi/dns_artfiles.sh | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 6224c41652..04cdf0262d 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -24,8 +24,7 @@ AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html' # Adds a new TXT record for given ACME challenge value & domain. # Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value" -dns_artfiles_add() -{ +dns_artfiles_add() { domain="$1" txtValue="$2" _info 'Using ArtFiles.de DNS addition API…' @@ -56,8 +55,7 @@ dns_artfiles_add() # Removes the existing TXT record for given ACME challenge value & domain. # Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value" -dns_artfiles_rm() -{ +dns_artfiles_rm() { domain="$1" txtValue="$2" _info 'Using ArtFiles.de DNS removal API…' @@ -91,15 +89,13 @@ dns_artfiles_rm() # Cleans awful TXT records response of ArtFiles's API & pretty prints it. # Usage: _clean_records -_clean_records() -{ +_clean_records() { _info 'Cleaning TXT records…' # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\' # from '\"' & turn '\n' into real LF characters. # Yup, awful API to use - but that's all we got to get this working, so… ;) _debug2 'Raw ' "$response" - response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')" _debug2 'Clean' "$response" } @@ -107,8 +103,7 @@ _clean_records() # Executes an HTTP GET or POST request for getting or setting DNS records, # containing given payload upon POST. # Usage: _dns [GET | SET] [payload] -_dns() -{ +_dns() { _info 'Executing HTTP request…' action="$1" payload="$(printf -- '%s' "$2" | _url_encode)" @@ -134,8 +129,7 @@ _dns() # Gets the root domain zone for given domain. # Usage: _get_zone _acme-challenge.www.example.com -_get_zone() -{ +_get_zone() { fqdn="$1" domains="$(_get "$AF_URL_DOMAINS" '' 10)" _info 'Getting domain zone…' @@ -164,8 +158,7 @@ _get_zone() # Sets the credentials for accessing ArtFiles's API # Usage: _set_credentials -_set_credentials() -{ +_set_credentials() { _info 'Setting credentials…' AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}" AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}" @@ -179,9 +172,9 @@ _set_credentials() # Adds the HTTP Authorization & Content-Type headers to a follow-up request. # Usage: _set_headers -_set_headers() -{ +_set_headers() { _info 'Setting headers…' - export _H1="$(printf -- 'Authorization: Basic %s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" + encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)" + export _H1="Authorization: Basic $encoded" export _H2='Content-Type: application/json' } From 8b3acb719eb6eee82ef40edff94ed2372ecdc1df Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:04:08 +0200 Subject: [PATCH 14/28] Fix TXT record removal --- dnsapi/dns_artfiles.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index 04cdf0262d..faf8c2aec5 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -76,7 +76,7 @@ dns_artfiles_rm() { fi _clean_records - response="$(printf -- '%s' "$response" | sed '$d')" + response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')" _dns 'SET' "$response" if ! _contains "$response" "$AF_API_SUCCESS"; then _err 'Removing ACME challenge value failed.' From 80006f47300bd0559729a2800ae014475f7499ce Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Sat, 29 Jul 2023 10:26:59 +0200 Subject: [PATCH 15/28] Added bug report url --- dnsapi/dns_artfiles.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh index faf8c2aec5..a762837e65 100644 --- a/dnsapi/dns_artfiles.sh +++ b/dnsapi/dns_artfiles.sh @@ -5,7 +5,7 @@ ################################################################################ # Author: Martin Arndt, https://troublezone.net/ # Released: 2022-02-27 -# Issues: https://github.com/acmesh-official/acme.sh/issues/XXXX +# Issues: https://github.com/acmesh-official/acme.sh/issues/4718 ################################################################################ # Usage: # 1. export AF_API_USERNAME='api12345678' From d52b38777aaf19bc49ef6edd5d23546ddd3a4f03 Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Wed, 9 Aug 2023 19:52:37 +0200 Subject: [PATCH 16/28] Fix Auth API access for DSM 6 --- deploy/synology_dsm.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh index 7398b35006..ba35d7847a 100644 --- a/deploy/synology_dsm.sh +++ b/deploy/synology_dsm.sh @@ -91,8 +91,10 @@ synology_dsm_deploy() { _debug "Getting API version" response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth") + api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([0-9]*\)".*/\1/p') api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p') _debug3 response "$response" + _debug3 api_path "$api_path" _debug3 api_version "$api_version" # Login, get the session ID & SynoToken from JSON @@ -133,12 +135,12 @@ synology_dsm_deploy() { [ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal" fi - response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name") + response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name") _debug3 response "$response" SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p') _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID" else - response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID") + response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID") _debug3 response "$response" fi From b793dbf977dbf5d6b6829d185e0b2a0fa130941a Mon Sep 17 00:00:00 2001 From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:55:45 +0200 Subject: [PATCH 17/28] Fix device ID property name for DSM 6 --- deploy/synology_dsm.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh index ba35d7847a..21c7ac9b76 100644 --- a/deploy/synology_dsm.sh +++ b/deploy/synology_dsm.sh @@ -136,8 +136,11 @@ synology_dsm_deploy() { fi response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name") - _debug3 response "$response" - SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p') + _secure_debug3 response "$response" + + id_property='device_id' + [ "${api_version}" -gt '6' ] || id_property='did' + SYNO_Device_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p') _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID" else response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID") From 3abcfd8fa926fb1ee03b45826f8a12644e63ff05 Mon Sep 17 00:00:00 2001 From: KincaidYang <91786638+KincaidYang@users.noreply.github.com> Date: Sat, 2 Sep 2023 18:47:59 +0800 Subject: [PATCH 18/28] Add dns_tencent.sh Adapt to Tencent Cloud (DNSPod) API 3.0 --- dnsapi/dns_tencent.sh | 210 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 dnsapi/dns_tencent.sh diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh new file mode 100644 index 0000000000..b4b4cb2452 --- /dev/null +++ b/dnsapi/dns_tencent.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env sh +Tencent_API="https://dnspod.tencentcloudapi.com" + +#Tencent_SecretId="AKIDz81d2cd22cdcdc2dcd1cc1d1A" +#Tencent_SecretKey="Gu5t9abcabcaabcbabcbbbcbcbbccbbcb" + +#Usage: dns_tencent_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_tencent_add() { + fulldomain=$1 + txtvalue=$2 + + Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}" + Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}" + if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then + Tencent_SecretId="" + Tencent_SecretKey="" + _err "You don't specify tencent api SecretId and SecretKey yet." + return 1 + fi + + #save the api SecretId and SecretKey to the account conf file. + _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId" + _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + return 1 + fi + + _debug "Add record" + _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord" +} + +dns_tencent_rm() { + fulldomain=$1 + txtvalue=$2 + Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}" + Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + return 1 + fi + + _debug "Get record list" + attempt=1 + max_attempts=5 + while [ -z "$record_id" ] && [ $attempt -le $max_attempts ]; do + _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList" + record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")" + _debug2 record_id "$record_id" + if [ -z "$record_id" ]; then + _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying" + sleep 10 + attempt=$((attempt + 1)) + fi + done + + record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")" + _debug2 record_id "$record_id" + + if [ -z "$record_id" ]; then + _debug "record not found after $max_attempts attempts, skip" + else + _debug "Delete record" + _delete_record_query "$record_id" && _tencent_rest "DeleteRecord" + fi +} + +#################### Private functions below ################################## + +_get_root() { + domain=$1 + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + if [ -z "$h" ]; then + #not valid + return 1 + fi + + _describe_records_query "$h" "@" + if ! _tencent_rest "DescribeRecordList" "ignore"; then + return 1 + fi + + if _contains "$response" "\"TotalCount\":"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + _debug _sub_domain "$_sub_domain" + _domain="$h" + _debug _domain "$_domain" + return 0 + fi + p="$i" + i=$(_math "$i" + 1) + done + return 1 +} + +_tencent_rest() { + action=$1 + service="dnspod" + payload="${query}" + timestamp=$(date -u +%s) + + token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp") + version="2021-03-23" + + if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then + _err "Error <$1>" + return 1 + fi + + _debug2 response "$response" + if [ -z "$2" ]; then + message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + if [ "$message" ]; then + _err "$message" + return 1 + fi + fi +} + +_add_record_query() { + query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}" +} + +_describe_records_query() { + query="{\"Domain\":\"$1\",\"Limit\":3000}" +} + +_delete_record_query() { + query="{\"Domain\":\"$_domain\",\"RecordId\":$1}" +} + +_check_exist_query() { + _domain="$1" + _subdomain="$2" + _value="$3" + query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}" +} + +# shell client for tencent cloud api v3 | @author: rehiy + +tencent_sha256() { + printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hex | sed 's/^.* //' +} + +tencent_hmac_sha256() { + k=$1 + shift + printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hmac "$k" | sed 's/^.* //' +} + +tencent_hmac_sha256_hexkey() { + k=$1 + shift + printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //' +} + +tencent_signature_v3() { + service=$1 + action=$(echo "$2" | tr '[:upper:]' '[:lower:]') + payload=${3:-'{}'} + timestamp=${4:-$(date +%s)} + + domain="$service.tencentcloudapi.com" + secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'} + secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'} + + algorithm='TC3-HMAC-SHA256' + date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null) + [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d) + + canonicalUri='/' + canonicalQuery='' + canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n" + + signedHeaders='content-type;host;x-tc-action' + canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")" + + credentialScope="$date/$service/tc3_request" + stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")" + + secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date") + secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service") + secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request') + signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign") + + echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature" +} + +tencent_api_request() { + service=$1 + version=$2 + action=$3 + payload=${4:-'{}'} + timestamp=${5:-$(date +%s)} + + token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp") + + _H1="Content-Type: application/json" + _H2="Authorization: $token" + _H3="X-TC-Version: $version" + _H4="X-TC-Timestamp: $timestamp" + _H5="X-TC-Action: $action" + + _post "$payload" "$Tencent_API" "" "POST" "application/json" +} From 27b1dd04c4d240d368d79216e84debbaeed813ae Mon Sep 17 00:00:00 2001 From: LJea Date: Sun, 3 Sep 2023 01:02:16 +0900 Subject: [PATCH 19/28] improve the compatibility Fixed an issue where some embedded devices could not obtain nanoseconds resulting in abnormal parameter coding --- dnsapi/dns_ali.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh index c21056726f..c69839dcbd 100755 --- a/dnsapi/dns_ali.sh +++ b/dnsapi/dns_ali.sh @@ -117,7 +117,7 @@ _ali_urlencode() { _ali_nonce() { #_head_n 1 Date: Sun, 3 Sep 2023 01:31:57 +0800 Subject: [PATCH 20/28] Following Neilpang's suggestions and project standards, replace some functions. --- dnsapi/dns_tencent.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh index b4b4cb2452..746f00f05f 100644 --- a/dnsapi/dns_tencent.sh +++ b/dnsapi/dns_tencent.sh @@ -51,7 +51,7 @@ dns_tencent_rm() { _debug2 record_id "$record_id" if [ -z "$record_id" ]; then _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying" - sleep 10 + _sleep 10 attempt=$((attempt + 1)) fi done @@ -71,7 +71,7 @@ dns_tencent_rm() { _get_root() { domain=$1 - i=2 + i=1 p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) @@ -144,19 +144,20 @@ _check_exist_query() { # shell client for tencent cloud api v3 | @author: rehiy tencent_sha256() { - printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hex | sed 's/^.* //' + printf %b "$@" | _digest sha256 hex } tencent_hmac_sha256() { k=$1 shift - printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hmac "$k" | sed 's/^.* //' + hex_key=$(_ascii_hex "$k" | tr -d ' ') + printf %b "$@" | _hmac sha256 "$hex_key" hex } tencent_hmac_sha256_hexkey() { k=$1 shift - printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //' + printf %b "$@" | _hmac sha256 "$k" hex } tencent_signature_v3() { From e3c4c9265dfe2d2816e68dfe310d5f6fcda4f25b Mon Sep 17 00:00:00 2001 From: KincaidYang <91786638+KincaidYang@users.noreply.github.com> Date: Sun, 3 Sep 2023 21:21:05 +0800 Subject: [PATCH 21/28] Replace some functions. --- dnsapi/dns_tencent.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh index 746f00f05f..5cce4aeb86 100644 --- a/dnsapi/dns_tencent.sh +++ b/dnsapi/dns_tencent.sh @@ -45,14 +45,14 @@ dns_tencent_rm() { _debug "Get record list" attempt=1 max_attempts=5 - while [ -z "$record_id" ] && [ $attempt -le $max_attempts ]; do + while [ -z "$record_id" ] && [ "$attempt" -le $max_attempts ]; do _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList" record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")" _debug2 record_id "$record_id" if [ -z "$record_id" ]; then _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying" _sleep 10 - attempt=$((attempt + 1)) + attempt=$(_math "$attempt + 1") fi done @@ -162,7 +162,7 @@ tencent_hmac_sha256_hexkey() { tencent_signature_v3() { service=$1 - action=$(echo "$2" | tr '[:upper:]' '[:lower:]') + action=$(echo "$2" | _lower_case) payload=${3:-'{}'} timestamp=${4:-$(date +%s)} From 772bbdc862777cf06c649271c9319f45b9075313 Mon Sep 17 00:00:00 2001 From: KincaidYang <91786638+KincaidYang@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:57:19 +0800 Subject: [PATCH 22/28] Replace some functions --- dnsapi/dns_tencent.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh index 5cce4aeb86..b926225b06 100644 --- a/dnsapi/dns_tencent.sh +++ b/dnsapi/dns_tencent.sh @@ -150,7 +150,7 @@ tencent_sha256() { tencent_hmac_sha256() { k=$1 shift - hex_key=$(_ascii_hex "$k" | tr -d ' ') + hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ') printf %b "$@" | _hmac sha256 "$hex_key" hex } @@ -183,6 +183,7 @@ tencent_signature_v3() { credentialScope="$date/$service/tc3_request" stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")" + _debug "stringToSign: $stringToSign" secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date") secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service") From af534a73fc75cddf5a60f0ec1b13f79056b51940 Mon Sep 17 00:00:00 2001 From: KincaidYang <91786638+KincaidYang@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:09:52 +0800 Subject: [PATCH 23/28] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=95=8F=E6=84=9Fdebug=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dnsapi/dns_tencent.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh index b926225b06..2f8d3b67e6 100644 --- a/dnsapi/dns_tencent.sh +++ b/dnsapi/dns_tencent.sh @@ -183,7 +183,6 @@ tencent_signature_v3() { credentialScope="$date/$service/tc3_request" stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")" - _debug "stringToSign: $stringToSign" secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date") secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service") From ae4c186f55b10b26d9bb0528bc90893413bd967e Mon Sep 17 00:00:00 2001 From: Tobias Grave Date: Thu, 7 Sep 2023 08:40:46 +0200 Subject: [PATCH 24/28] Fix Variomedia API --- dnsapi/dns_variomedia.sh | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index a35b8f0f93..200b3f502e 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -69,7 +69,7 @@ dns_variomedia_rm() { return 1 fi - _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" + _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" _debug _record_id "$_record_id" if [ "$_record_id" ]; then _info "Successfully retrieved the record id for ACME challenge." @@ -93,11 +93,11 @@ dns_variomedia_rm() { # _sub_domain=_acme-challenge.www # _domain=domain.com _get_root() { - fulldomain=$1 - i=1 + domain=$1 + i=2 + p=1 while true; do - h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) - _debug h "$h" + h=$(printf "%s" "$domain" | cut -d . -f $i-100) if [ -z "$h" ]; then return 1 fi @@ -106,17 +106,14 @@ _get_root() { return 1 fi - if _startswith "$response" "\{\"data\":"; then - if _contains "$response" "\"id\":\"$h\""; then - _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")" - _domain=$h - return 0 - fi + if _contains "$response" "\"id\":\"$h\""; then + _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p) + _domain="$h" + return 0 fi + p=$i i=$(_math "$i" + 1) done - - _debug "root domain not found" return 1 } From 1a08be0a3fca85413421cedcf598ac4d3f5ded04 Mon Sep 17 00:00:00 2001 From: Julien Furgerot Date: Mon, 11 Sep 2023 15:05:12 +0200 Subject: [PATCH 25/28] dns_gandi: implements personal access token in addition to the (deprecated) API key --- dnsapi/dns_gandi_livedns.sh | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index 931da883fc..f299779e15 100644 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -1,7 +1,8 @@ #!/usr/bin/env sh # Gandi LiveDNS v5 API -# https://doc.livedns.gandi.net/ +# https://api.gandi.net/docs/livedns/ +# https://api.gandi.net/docs/authentication/ for token + apikey (deprecated) authentication # currently under beta # # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable @@ -19,13 +20,23 @@ dns_gandi_livedns_add() { fulldomain=$1 txtvalue=$2 - if [ -z "$GANDI_LIVEDNS_KEY" ]; then - _err "No API key specified for Gandi LiveDNS." - _err "Create your key and export it as GANDI_LIVEDNS_KEY" + if [ -z "$GANDI_LIVEDNS_KEY" -a -z "$GANDI_LIVEDNS_TOKEN" ]; then + _err "No Token or API key (deprecated) specified for Gandi LiveDNS." + _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively" return 1 fi - _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY" + # Keep only one secret in configuration + if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then + _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN" + _clearaccountconf GANDI_LIVEDNS_KEY + elif [ -n "$GANDI_LIVEDNS_KEY" ]; then + _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY" + _clearaccountconf GANDI_LIVEDNS_TOKEN + fi + + + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -157,7 +168,12 @@ _gandi_livedns_rest() { _debug "$ep" export _H1="Content-Type: application/json" - export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY" + + if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then + export _H2="Authorization: Bearer $GANDI_LIVEDNS_TOKEN" + else + export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY" + fi if [ "$m" = "GET" ]; then response="$(_get "$GANDI_LIVEDNS_API/$ep")" From 558e706bdeb395ce0164cb9b7a31078269413d33 Mon Sep 17 00:00:00 2001 From: Julien Furgerot Date: Tue, 12 Sep 2023 15:54:44 +0200 Subject: [PATCH 26/28] fix ci errors (shellcheck & shfmt) --- dnsapi/dns_gandi_livedns.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index f299779e15..14939d7c5a 100644 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -20,7 +20,7 @@ dns_gandi_livedns_add() { fulldomain=$1 txtvalue=$2 - if [ -z "$GANDI_LIVEDNS_KEY" -a -z "$GANDI_LIVEDNS_TOKEN" ]; then + if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then _err "No Token or API key (deprecated) specified for Gandi LiveDNS." _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively" return 1 @@ -35,9 +35,6 @@ dns_gandi_livedns_add() { _clearaccountconf GANDI_LIVEDNS_TOKEN fi - - - _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From dfd49e46ad268d6f1796cf850b0b353b667cc11d Mon Sep 17 00:00:00 2001 From: Tobias Grave Date: Thu, 14 Sep 2023 09:15:08 +0200 Subject: [PATCH 27/28] Fix root zone determination for Variomedia API --- dnsapi/dns_variomedia.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index 200b3f502e..aa743807f5 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -94,7 +94,7 @@ dns_variomedia_rm() { # _domain=domain.com _get_root() { domain=$1 - i=2 + i=1 p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) From 59f976dc48873787d089df34c3ebc092a14bc110 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 20 Sep 2023 18:07:16 +0800 Subject: [PATCH 28/28] fix https://github.com/acmesh-official/acme.sh/issues/4798 --- acme.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 067780d7e2..bce7a41d21 100755 --- a/acme.sh +++ b/acme.sh @@ -2118,12 +2118,7 @@ _tail_n() { } _tail_c() { - if _is_solaris; then - #fix for solaris - tail -"$1"c - else - tail -c "$1" - fi + tail -c "$1" 2>/dev/null || tail -"$1"c } # url payload needbase64 keyfile