From 7044236824af45431413297c6224e11dcc785733 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Wed, 15 Mar 2017 11:40:32 -0700 Subject: [PATCH 01/29] Create dns_dynu.sh Add DNS API support for Dynu. --- dnsapi/dns_dynu.sh | 135 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 dnsapi/dns_dynu.sh diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh new file mode 100644 index 0000000..963bdd2 --- /dev/null +++ b/dnsapi/dns_dynu.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env sh + +my_dir="$(dirname "$0")" +source "$my_dir/acme.sh" + +#Client ID +Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" +# +#Secret +Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9" +# +#Token +Dynu_Token="" +# +#Endpoint +Dynu_EndPoint="https://api.dynu.com/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_dynu_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then + Dynu_ClientId="" + Dynu_Secret="" + _err "Dynu client id and secret is not specified." + _err "Please create you API client id and secret and try again." + return 1 + fi + + #save the client id and secret to the account conf file. + _saveaccountconf Dynu_ClientId "$Dynu_ClientId" + _saveaccountconf Dynu_Secret "$Dynu_Secret" + + if [ -z "$Dynu_Token" ]; then + _info "Getting Dynu token" + if ! _dynu_authentication; then + _err "Can not get token." + fi + fi + + _debug "Detect root zone" + if ! _get_root "$fulldomain"; then + _err "Invalid domain" + return 1 + fi + + _debug _node "$_node" + _debug _domain_name "$_domain_name" + + _info "Creating TXT record" + if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then + return 1 + fi + + if ! _contains "$response" "text_data"; then + _err "Could not add TXT record" + return 1 + fi + + return 0 +} + +#fulldomain +dns_dynu_rm() { + fulldomain=$1 +} + +######## Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _node=_acme-challenge.www +# _domain_name=domain.com +_get_root() { + domain=$1 + if ! _dynu_rest GET "dns/getroot/$domain"; then + return 1 + fi + + if ! _contains "$response" "domain_name"; then + _debug "Domain name not found" + return 1 + fi + + _domain_name=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2) + _node=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 3 | cut -d : -f 2 | cut -d '"' -f 2) + return 0 +} + +_dynu_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="Authorization: Bearer $Dynu_Token" + export _H2="Content-Type: application/json" + + if [ "$data" ]; then + _debug data "$data" + response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" + else + echo "Getting $Dynu_EndPoint/$ep" + response="$(_get "$Dynu_EndPoint/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} + +_dynu_authentication() { + export _H1="Authorization: Basic $(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" + export _H2="Content-Type: application/json" + + response="$(_get "$Dynu_EndPoint/oauth2/token")" + if [ "$?" != "0" ]; then + _err "Authentication failed." + return 1 + fi + if _contains "$response" "accessToken"; then + Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) + fi + if _contains "$Dynu_Token" "null"; then + Dynu_Token="" + fi + + _debug2 response "$response" + return 0 +} From 5c78e0a462178ea5424b241b486252c331c2bd53 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Thu, 16 Mar 2017 13:42:30 -0700 Subject: [PATCH 02/29] removed source acme.sh --- dnsapi/dns_dynu.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 963bdd2..b12b0bb 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -1,8 +1,5 @@ #!/usr/bin/env sh -my_dir="$(dirname "$0")" -source "$my_dir/acme.sh" - #Client ID Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" # From d0300d4443caf45f0619cba436b40128e09dc71d Mon Sep 17 00:00:00 2001 From: shar0119 Date: Mon, 27 Mar 2017 12:27:21 -0700 Subject: [PATCH 03/29] Changes as requested per Commit 9c90b21 In dnsapi/dns_dynu.sh line 115: export _H1="Authorization: Basic $(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" ^-- SC2155: Declare and assign separately to avoid masking return values. --- dnsapi/dns_dynu.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index b12b0bb..c50c7a1 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -112,7 +112,9 @@ _dynu_rest() { } _dynu_authentication() { - export _H1="Authorization: Basic $(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" + realm = "$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" + + export _H1="Authorization: Basic $realm" export _H2="Content-Type: application/json" response="$(_get "$Dynu_EndPoint/oauth2/token")" From cd8fcbf9c63a9a60726fb2bc3a889df55fdf87dd Mon Sep 17 00:00:00 2001 From: shar0119 Date: Mon, 27 Mar 2017 12:38:12 -0700 Subject: [PATCH 04/29] Spaces in assignment removed. --- dnsapi/dns_dynu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index c50c7a1..19a2447 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -112,7 +112,7 @@ _dynu_rest() { } _dynu_authentication() { - realm = "$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" + realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" export _H1="Authorization: Basic $realm" export _H2="Content-Type: application/json" From b7b934913ef0375700e3808801db5565b36ed193 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Mon, 27 Mar 2017 13:39:31 -0700 Subject: [PATCH 05/29] Removed unnecessary spaces --- dnsapi/dns_dynu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 19a2447..b81840d 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -42,7 +42,7 @@ dns_dynu_add() { if ! _get_root "$fulldomain"; then _err "Invalid domain" return 1 - fi + fi _debug _node "$_node" _debug _domain_name "$_domain_name" @@ -83,7 +83,7 @@ _get_root() { _domain_name=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2) _node=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 3 | cut -d : -f 2 | cut -d '"' -f 2) - return 0 + return 0 } _dynu_rest() { @@ -113,7 +113,7 @@ _dynu_rest() { _dynu_authentication() { realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" - + export _H1="Authorization: Basic $realm" export _H2="Content-Type: application/json" @@ -128,7 +128,7 @@ _dynu_authentication() { if _contains "$Dynu_Token" "null"; then Dynu_Token="" fi - + _debug2 response "$response" return 0 } From e137792efdb0986192b0b96cf728e670c4719c1b Mon Sep 17 00:00:00 2001 From: shar0119 Date: Tue, 28 Mar 2017 08:11:04 -0700 Subject: [PATCH 06/29] Commented out Dynu_ClientId and Dynu_Secret --- dnsapi/dns_dynu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index b81840d..03a45c1 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -1,10 +1,10 @@ #!/usr/bin/env sh #Client ID -Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" +#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" # #Secret -Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9" +#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9" # #Token Dynu_Token="" From 1f4e64f81d7650e7811153cf73e527b94fc252c1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Apr 2017 00:21:39 +0800 Subject: [PATCH 07/29] Truncated request for more than 100 domains. --- dnsapi/dns_aws.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index d5d52de..6fffd09 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -88,6 +88,19 @@ _get_root() { while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) if [ -z "$h" ]; then + if _contains "$response" "true" && _contains "$response" ""; then + _debug "IsTruncated" + _nextMarker="$(echo "$response" | _egrep_o ".*" | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug "NextMarker" "$_nextMarker" + if aws_rest GET "2013-04-01/hostedzone?marker=$_nextMarker"; then + _debug "Truncated request OK" + i=2 + p=1 + continue + else + _err "Truncated request error." + fi + fi #not valid return 1 fi From fc9649dbc48bc89626d0f80fd7db20442cb17f0e Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Apr 2017 10:02:45 +0800 Subject: [PATCH 08/29] fix aws --- dnsapi/dns_aws.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 6fffd09..137af89 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -92,7 +92,7 @@ _get_root() { _debug "IsTruncated" _nextMarker="$(echo "$response" | _egrep_o ".*" | cut -d '>' -f 2 | cut -d '<' -f 1)" _debug "NextMarker" "$_nextMarker" - if aws_rest GET "2013-04-01/hostedzone?marker=$_nextMarker"; then + if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then _debug "Truncated request OK" i=2 p=1 @@ -220,7 +220,7 @@ aws_rest() { _H2="Authorization: $Authorization" _debug _H2 "$_H2" - url="$AWS_URL/$ep" + url="$AWS_URL/$ep?$qsr" if [ "$mtd" = "GET" ]; then response="$(_get "$url")" From 695482ded7248b25ecaa23e00571dd32d04f4ad4 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Mon, 3 Apr 2017 21:21:50 -0700 Subject: [PATCH 09/29] Added author name and link to report bugs --- dnsapi/dns_dynu.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 03a45c1..9bf2c06 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -11,7 +11,10 @@ Dynu_Token="" # #Endpoint Dynu_EndPoint="https://api.dynu.com/v1" - +# +#Author: David Kerr +#Report Bugs here: https://github.com/dkerr64/acme.sh +# ######## Public functions ##################### #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" From 6d7f6750e94bc969fed6469bff6402be538987a8 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Mon, 3 Apr 2017 21:22:58 -0700 Subject: [PATCH 10/29] Updated author name and link to report bugs --- dnsapi/dns_dynu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 9bf2c06..63992ed 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -12,8 +12,8 @@ Dynu_Token="" #Endpoint Dynu_EndPoint="https://api.dynu.com/v1" # -#Author: David Kerr -#Report Bugs here: https://github.com/dkerr64/acme.sh +#Author: Dynu Systems, Inc. +#Report Bugs here: https://github.com/shar0119/acme.sh # ######## Public functions ##################### From fd77e463a150e2253aa7ca6658c17bddb62fef79 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Apr 2017 14:34:23 +0800 Subject: [PATCH 11/29] fix aws --- dnsapi/dns_aws.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 137af89..6a72353 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -220,7 +220,10 @@ aws_rest() { _H2="Authorization: $Authorization" _debug _H2 "$_H2" - url="$AWS_URL/$ep?$qsr" + url="$AWS_URL/$ep" + if [ "$qsr" ]; then + url="$AWS_URL/$ep?$qsr" + fi if [ "$mtd" = "GET" ]; then response="$(_get "$url")" From f7217c5f2629d541a4584039a7d9c9313afdda7f Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Apr 2017 15:54:45 +0800 Subject: [PATCH 12/29] fix format --- dnsapi/dns_aws.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 6a72353..21e8668 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -88,7 +88,7 @@ _get_root() { while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) if [ -z "$h" ]; then - if _contains "$response" "true" && _contains "$response" ""; then + if _contains "$response" "true" && _contains "$response" ""; then _debug "IsTruncated" _nextMarker="$(echo "$response" | _egrep_o ".*" | cut -d '>' -f 2 | cut -d '<' -f 1)" _debug "NextMarker" "$_nextMarker" From 7df062b7d7d9ff95f6a3b58f005431ccd3cd4ec8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Apr 2017 22:33:26 +0800 Subject: [PATCH 13/29] add more debug info --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index e2bc928..8f47f8c 100755 --- a/acme.sh +++ b/acme.sh @@ -1102,6 +1102,7 @@ _readKeyLengthFromCSR() { fi _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")" + _debug2 _outcsr "$_outcsr" if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then _debug "ECC CSR" echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' From 49d75a0cd4ba5112ca947a2b54ba72719ac4f73b Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 5 Apr 2017 20:46:17 +0800 Subject: [PATCH 14/29] minor, add more error message --- acme.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acme.sh b/acme.sh index 8f47f8c..850ac4b 100755 --- a/acme.sh +++ b/acme.sh @@ -3121,6 +3121,10 @@ issue() { _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ " return 1 fi + if [ -z "$1" ]; then + _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc." + return 1 + fi _web_roots="$1" _main_domain="$2" _alt_domains="$3" From 3576754c217c643d9b66cba0e91d858ada5781bf Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 5 Apr 2017 20:54:53 +0800 Subject: [PATCH 15/29] fix https://github.com/Neilpang/acme.sh/issues/549 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 850ac4b..b6b0c84 100755 --- a/acme.sh +++ b/acme.sh @@ -107,7 +107,7 @@ __green() { if [ "$__INTERACTIVE" ]; then printf '\033[1;31;32m' fi - printf -- "$1" + printf -- "%b" "$1" if [ "$__INTERACTIVE" ]; then printf '\033[0m' fi @@ -117,7 +117,7 @@ __red() { if [ "$__INTERACTIVE" ]; then printf '\033[1;31;40m' fi - printf -- "$1" + printf -- "%b" "$1" if [ "$__INTERACTIVE" ]; then printf '\033[0m' fi From 482cb737025d691c730594abf8084c0bd1936bc6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 6 Apr 2017 19:29:09 +0800 Subject: [PATCH 16/29] fix https://github.com/Neilpang/acme.sh/issues/758 --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index b6b0c84..11a30fa 100755 --- a/acme.sh +++ b/acme.sh @@ -1105,10 +1105,10 @@ _readKeyLengthFromCSR() { _debug2 _outcsr "$_outcsr" if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then _debug "ECC CSR" - echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' + echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' else _debug "RSA CSR" - echo "$_outcsr" | _egrep_o "(^ *|^RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 + echo "$_outcsr" | tr "\t" " " | _egrep_o "(^ *|RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 fi } @@ -3865,7 +3865,7 @@ renewAll() { return "$rc" else _ret="$rc" - _err "Error renew $d, Go ahead to next one." + _err "Error renew $d." fi fi done From 7f618e7ecc698f171135558b34b6980219b902d7 Mon Sep 17 00:00:00 2001 From: Kok Suan Lim Date: Sat, 8 Apr 2017 14:50:39 +0800 Subject: [PATCH 17/29] fix missing space bug on if statement --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 11a30fa..f434f68 100755 --- a/acme.sh +++ b/acme.sh @@ -2565,7 +2565,7 @@ _checkConf() { if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then _debug "wildcard" for _w_f in $2; do - if [ -f "$_w_f"] && _checkConf "$1" "$_w_f"; then + if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then return 0 fi done From fcdf41ba293658060e82c45b3c3ec368db270385 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 11 Apr 2017 21:37:56 +0800 Subject: [PATCH 18/29] support change account conf from env --- acme.sh | 20 +++++++++++++++++++- dnsapi/dns_cf.sh | 6 ++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index f434f68..3498fe7 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.6.8 +VER=2.6.9 PROJECT_NAME="acme.sh" @@ -1847,6 +1847,24 @@ _saveaccountconf() { _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2" } +#key value +_saveaccountconf_mutable() { + _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" + #remove later + _clearaccountconf "$1" +} + +#key +_readaccountconf() { + _read_conf "$ACCOUNT_CONF_PATH" "$1" +} + +#key +_readaccountconf_mutable() { + _rac_key="$1" + _readaccountconf "SAVED_$_rac_key" +} + #_clearaccountconf key _clearaccountconf() { _clear_conf "$ACCOUNT_CONF_PATH" "$1" diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 3718f9d..0442fbe 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -14,6 +14,8 @@ dns_cf_add() { fulldomain=$1 txtvalue=$2 + 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="" @@ -29,8 +31,8 @@ dns_cf_add() { fi #save the api key and email to the account conf file. - _saveaccountconf CF_Key "$CF_Key" - _saveaccountconf CF_Email "$CF_Email" + _saveaccountconf_mutable CF_Key "$CF_Key" + _saveaccountconf_mutable CF_Email "$CF_Email" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then From eb0fc67461c07ed5eeae8605d616e88ec9b985dc Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 11 Apr 2017 22:29:49 +0800 Subject: [PATCH 19/29] fix format --- dnsapi/dns_cf.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 0442fbe..228caa8 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -14,8 +14,8 @@ dns_cf_add() { fulldomain=$1 txtvalue=$2 - CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key )}" - CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email )}" + 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="" From 9a61d6293d14a401f57fed793b9b829788c5b134 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Thu, 13 Apr 2017 20:48:39 -0700 Subject: [PATCH 20/29] Implemented dns_dynu_rm() Implemented dns_dynu_rm() method. --- dnsapi/dns_dynu.sh | 93 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 63992ed..a3a604b 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -35,7 +35,7 @@ dns_dynu_add() { _saveaccountconf Dynu_Secret "$Dynu_Secret" if [ -z "$Dynu_Token" ]; then - _info "Getting Dynu token" + _info "Getting Dynu token." if ! _dynu_authentication; then _err "Can not get token." fi @@ -43,29 +43,76 @@ dns_dynu_add() { _debug "Detect root zone" if ! _get_root "$fulldomain"; then - _err "Invalid domain" + _err "Invalid domain." return 1 fi _debug _node "$_node" _debug _domain_name "$_domain_name" - _info "Creating TXT record" + _info "Creating TXT record." if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then return 1 fi if ! _contains "$response" "text_data"; then - _err "Could not add TXT record" + _err "Could not add TXT record." return 1 fi return 0 } -#fulldomain +#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_dynu_rm() { fulldomain=$1 + txtvalue=$2 + + if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then + Dynu_ClientId="" + Dynu_Secret="" + _err "Dynu client id and secret is not specified." + _err "Please create you API client id and secret and try again." + return 1 + fi + + #save the client id and secret to the account conf file. + _saveaccountconf Dynu_ClientId "$Dynu_ClientId" + _saveaccountconf Dynu_Secret "$Dynu_Secret" + + if [ -z "$Dynu_Token" ]; then + _info "Getting Dynu token." + if ! _dynu_authentication; then + _err "Can not get token." + fi + fi + + _debug "Detect root zone." + if ! _get_root "$fulldomain"; then + _err "Invalid domain." + return 1 + fi + + _debug _node "$_node" + _debug _domain_name "$_domain_name" + + _info "Checking for TXT record." + if ! _get_recordid "$fulldomain" "$txtvalue"; then + _err "Could not get TXT record id." + return 1 + fi + + if [ "$_dns_record_id" = "" ]; then + _err "TXT record not found." + return 1 + fi + + _info "Removing TXT record." + if ! _delete_txt_record "$_dns_record_id"; then + _err "Could not remove TXT record $_dns_record_id." + fi + + return 0 } ######## Private functions below ################################## @@ -80,7 +127,7 @@ _get_root() { fi if ! _contains "$response" "domain_name"; then - _debug "Domain name not found" + _debug "Domain name not found." return 1 fi @@ -89,6 +136,38 @@ _get_root() { return 0 } +_get_recordid() { + fulldomain=$1 + txtvalue=$2 + + if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then + return 1 + fi + + if ! _contains "$response" "$txtvalue"; then + _dns_record_id=0 + return 0 + fi + + _dns_record_id=$(printf "%s" "$response" grep -o -e "{[^}]*}" | grep '"text_data":"This is another TXT record."' | grep -o -e ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) + + return 0 +} + +_delete_txt_record() { + _dns_record_id=$1 + + if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then + return 1 + fi + + if ! _contains "$response" "true"; then + return 1 + fi + + return 0 +} + _dynu_rest() { m=$1 ep="$2" @@ -102,7 +181,7 @@ _dynu_rest() { _debug data "$data" response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" else - echo "Getting $Dynu_EndPoint/$ep" + _info "Getting $Dynu_EndPoint/$ep" response="$(_get "$Dynu_EndPoint/$ep")" fi From 394b1002b344db16ef8568f2933b162d9e87c9c5 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Thu, 13 Apr 2017 20:54:57 -0700 Subject: [PATCH 21/29] Corrected formatting error. Part of dns_dynu_rm() impementation. --- dnsapi/dns_dynu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index a3a604b..e784c3b 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -112,7 +112,7 @@ dns_dynu_rm() { _err "Could not remove TXT record $_dns_record_id." fi - return 0 + return 0 } ######## Private functions below ################################## From 8470c60e067b522e3322f90bca980313ac110729 Mon Sep 17 00:00:00 2001 From: shar0119 Date: Fri, 14 Apr 2017 12:46:00 -0700 Subject: [PATCH 22/29] Using _egrep_o() instead of grep -o -e Modified code to use egrep instead of grep -o -e. --- dnsapi/dns_dynu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index e784c3b..19e7fc7 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -149,7 +149,7 @@ _get_recordid() { return 0 fi - _dns_record_id=$(printf "%s" "$response" grep -o -e "{[^}]*}" | grep '"text_data":"This is another TXT record."' | grep -o -e ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) + _dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | grep -Po '"id":\K[0-9]+') return 0 } From cd98951001ca5f9bbd79a6e35bca3653b1b1e0d8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 16 Apr 2017 09:36:59 +0800 Subject: [PATCH 23/29] fix https://github.com/Neilpang/acme.sh/issues/794#issuecomment-294314606 --- dnsapi/dns_cf.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 228caa8..57a2e88 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -85,6 +85,17 @@ dns_cf_add() { dns_cf_rm() { fulldomain=$1 txtvalue=$2 + + 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 don't specify cloudflare api key and email yet." + _err "Please create you key and try again." + return 1 + fi + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From 7883cc58912418d96cf4f2eff5dac8e15015aa4d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 16 Apr 2017 11:16:48 +0800 Subject: [PATCH 24/29] fix docker cronjob --- Dockerfile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8d0f618..feb89b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,5 +48,12 @@ RUN for verb in help \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ ; done -ENTRYPOINT ["/root/.acme.sh/acme.sh", "--config-home", "/acme.sh"] +RUN printf "%b" '#!'"/usr/bin/env sh\n \ +if [ \"\$1\" = \"daemon\" ]; then \n \ + crond; tail -f /dev/null;\n \ +else \n \ + /root/.acme.sh/acme.sh --config-home /acme.sh \"\$@\"\n \ +fi" >/entry.sh && chmod +x /entry.sh + +ENTRYPOINT ["/entry.sh"] CMD ["--help"] From 3c07f57aade6d3995f16faa5f3ed3fac8d962d01 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 17 Apr 2017 19:08:34 +0800 Subject: [PATCH 25/29] minor remove spaces --- acme.sh | 26 +++++++++++++------------- dnsapi/dns_freedns.sh | 6 +++--- dnsapi/dns_ovh.sh | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/acme.sh b/acme.sh index 3498fe7..27bc4fb 100755 --- a/acme.sh +++ b/acme.sh @@ -347,7 +347,7 @@ _hasfield() { fi done _debug2 "'$_str' does not contain '$_field'" - return 1 #not contains + return 1 #not contains } _getfield() { @@ -722,7 +722,7 @@ _url_encode() { "7e") printf "%s" "~" ;; - #other hex + #other hex *) printf '%%%s' "$_hex_code" ;; @@ -1025,7 +1025,7 @@ _createcsr() { else alt="DNS:$domainlist" fi - #multi + #multi _info "Multi domain" "$alt" printf -- "\nsubjectAltName=$alt" >>"$csrconf" fi @@ -1093,7 +1093,7 @@ _readSubjectAltNamesFromCSR() { printf "%s" "$_dnsAltnames" | sed "s/DNS://g" } -#_csrfile +#_csrfile _readKeyLengthFromCSR() { _csrfile="$1" if [ -z "$_csrfile" ]; then @@ -1192,7 +1192,7 @@ toPkcs8() { } -#[2048] +#[2048] createAccountKey() { _info "Creating account key" if [ -z "$1" ]; then @@ -2546,7 +2546,7 @@ _setNginx() { location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" { default_type text/plain; return 200 \"\$1.$_thumbpt\"; -} +} #NGINX_START " >>"$FOUND_REAL_NGINX_CONF" @@ -3133,7 +3133,7 @@ __trigger_validation() { _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" } -#webroot, domain domainlist keylength +#webroot, domain domainlist keylength issue() { if [ -z "$2" ]; then _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ " @@ -3666,7 +3666,7 @@ issue() { #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then # _debug "Get cert failed. Let's try last response." - # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH" + # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH" #fi if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then @@ -4807,7 +4807,7 @@ Commands: --create-domain-key Create an domain private key, professional use. --createCSR, -ccsr Create CSR , professional use. --deactivate Deactivate the domain authz, professional use. - + Parameters: --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. --force, -f Used to force to install or force to renew a cert immediately. @@ -4821,20 +4821,20 @@ Parameters: --apache Use apache mode. --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api. --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds. - + --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384. --accountkeylength, -ak [2048] Specifies the account key length. --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here. --log-level 1|2 Specifies the log level, default is 1. --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug. - + These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert: - + --cert-file After issue/renew, the cert will be copied to this path. --key-file After issue/renew, the key will be copied to this path. --ca-file After issue/renew, the intermediate cert will be copied to this path. --fullchain-file After issue/renew, the fullchain cert will be copied to this path. - + --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server. --accountconf Specifies a customized account config file. diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 272a124..53da411 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -53,7 +53,7 @@ dns_freedns_add() { i="$(_math "$i" - 1)" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" - # Sometimes FreeDNS does not return the subdomain page but rather + # Sometimes FreeDNS does not return the subdomain page but rather # returns a page regarding becoming a premium member. This usually # happens after a period of inactivity. Immediately trying again # returns the correct subdomain page. So, we will try twice to @@ -72,7 +72,7 @@ dns_freedns_add() { fi # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv + # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv subdomain_csv="$(echo "$htmlpage" \ | grep -i -e ' Date: Wed, 19 Apr 2017 23:12:37 +0800 Subject: [PATCH 26/29] update doc --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e0c1e7d..cf82562 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - DOES NOT require `root/sudoer` access. - Docker friendly -It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. +It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. Wiki: https://github.com/Neilpang/acme.sh/wiki @@ -31,6 +31,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) - [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) +- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) # Tested OS From 27a05ff27112a29e98aa51661040ef3176c07531 Mon Sep 17 00:00:00 2001 From: LAV45 Date: Sat, 15 Apr 2017 14:34:37 +0300 Subject: [PATCH 27/29] Add dns_vscale.sh --- README.md | 1 + dnsapi/README.md | 15 +++++ dnsapi/dns_vscale.sh | 149 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100755 dnsapi/dns_vscale.sh diff --git a/README.md b/README.md index e0c1e7d..47ea012 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ You don't have to do anything manually! 1. DigitalOcean API (native) 1. ClouDNS.net API 1. Infoblox NIOS API (https://www.infoblox.com/) +1. VSCALE (https://vscale.io/) **More APIs coming soon...** diff --git a/dnsapi/README.md b/dnsapi/README.md index 9eb7791..896dfcb 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -438,6 +438,21 @@ acme.sh --issue --dns dns_infoblox -d example.com -d www.example.com Note: This script will automatically create and delete the ephemeral txt record. The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +## 22. Use VSCALE API + +First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/). + +``` +VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns vscale -d example.com -d www.example.com +``` + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh new file mode 100755 index 0000000..e50b7d8 --- /dev/null +++ b/dnsapi/dns_vscale.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env sh + +#This is the vscale.io api wrapper for acme.sh +# +#Author: Alex Loban +#Report Bugs here: https://github.com/LAV45/acme.sh + +#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" +VSCALE_API_URL="https://api.vscale.io/v1" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_vscale_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$VSCALE_API_KEY" ]; then + VSCALE_API_KEY="" + _err "You didn't specify the VSCALE api key yet." + _err "Please create you key and try again." + return 1 + fi + + _saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY" + + _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" + + _vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}" + + if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then + response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2) + if [ -z "$response" ]; then + _info "txt record updated success." + return 0 + fi + fi + + return 1 +} + +#fulldomain txtvalue +dns_vscale_rm() { + fulldomain=$1 + txtvalue=$2 + + _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 txt records" + _vscale_rest GET "domains/$_domain_id/records/" + + if [ -n "$response" ]; then + record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"") + _debug record_id "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then + _info "txt record deleted success." + return 0 + fi + _debug response "$response" + return 1 + fi + + return 1 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=12345 +_get_root() { + domain=$1 + i=2 + p=1 + + if _vscale_rest GET "domains/"; then + response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" + if [ "$hostedzone" ]; then + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + fi + return 1 +} + +#method uri qstr data +_vscale_rest() { + mtd="$1" + ep="$2" + data="$3" + + _debug mtd "$mtd" + _debug ep "$ep" + + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + export _H3="X-Token: ${VSCALE_API_KEY}" + + if [ "$mtd" != "GET" ]; then + # both POST and DELETE. + _debug data "$data" + response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")" + else + response="$(_get "$VSCALE_API_URL/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From dbe68684a054a11a6232803bafd0ba952380bc54 Mon Sep 17 00:00:00 2001 From: Aleksey Loban Date: Fri, 21 Apr 2017 12:30:01 +0300 Subject: [PATCH 28/29] Fix readme 'Use VSCALE API' [skip ci] --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 896dfcb..82b4764 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -449,7 +449,7 @@ VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" Ok, let's issue a cert now: ``` -acme.sh --issue --dns vscale -d example.com -d www.example.com +acme.sh --issue --dns dns_vscale -d example.com -d www.example.com ``` From 020f9cd2a640a5347e46a650fdc4a3e059868ddc Mon Sep 17 00:00:00 2001 From: Aleksey Loban Date: Fri, 21 Apr 2017 13:15:39 +0300 Subject: [PATCH 29/29] small Fix readme [skip ci] --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 82b4764..d9f5c27 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -439,7 +439,7 @@ Note: This script will automatically create and delete the ephemeral txt record. The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 22. Use VSCALE API +## 23. Use VSCALE API First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/).