From e5079b9dad0b8e12d4d043daacd5da93e8422d4a Mon Sep 17 00:00:00 2001 From: Paul Koppen Date: Mon, 2 Jan 2017 23:39:00 +0000 Subject: [PATCH 1/7] Add Alwaysdata DNS API. --- dnsapi/dns_ad.sh | 139 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 dnsapi/dns_ad.sh diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh new file mode 100644 index 00000000..158ea951 --- /dev/null +++ b/dnsapi/dns_ad.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env sh + +# +#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" + +#This is the Alwaysdata api wrapper for acme.sh + +AD_HOST="api.alwaysdata.com" +AD_URL="https://$AD_API_KEY:@$AD_HOST" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ad_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$AD_API_KEY" ]; then + AD_API_KEY="" + _err "You didn't specify the AD api key yet." + _err "Please create you key and try again." + return 1 + fi + + _saveaccountconf AD_API_KEY "$AD_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" + + _ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}" + + if ad_rest POST "record/" "" "$_ad_tmpl_json" && [ -z "$response" ]; then + _info "txt record updated success." + return 0 + fi + + return 1 +} + +#fulldomain txtvalue +dns_ad_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" + + if ad_rest DELETE "record/" "domain=$_domain_id&name=$_sub_domain" "" && [ -z "$response" ]; then + _info "txt record deleted success." + return 0 + fi + _debug response "$response" + + return 1 +} + +#################### Private functions below ################################## + +_get_root() { + domain=$1 + i=2 + p=1 + + if ad_rest GET "domain/"; then + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if _contains "$response" "$h"; then + hostedzone="$(echo "$response" | tr -d "\n" | sed 's//\n&/g' | _egrep_o ".*$h<.name>.*<.object>")" + if [ -z "$hostedzone" ]; then + _err "Error, can not get domain record." + return 1 + fi + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o ".*<.id>" | head -n 1 | _egrep_o ">.*<" | 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 +ad_rest() { + mtd="$1" + ep="$2" + qsr="$3" + data="$4" + + _debug mtd "$mtd" + _debug ep "$ep" + _debug qsr "$qsr" + _debug data "$data" + + _H1="Accept: application/xml" + + url="$AD_URL/v1/$ep?$qsr" + + if [ "$mtd" = "GET" ]; then + response="$(_get "$url")" + elif [ "$mtd" = "DELETE" ]; then + response="$(_delete "$url")" + else + response="$(_post "$data" "$url")" + fi + + _ret="$?" + if [ "$_ret" = "0" ]; then + # Errors usually 404, otherwise just empty response. How to detect 404? + if _contains "$response" " Date: Tue, 3 Jan 2017 11:41:11 +0000 Subject: [PATCH 2/7] Use _post for DELETE and switch to JSON API (Alwaysdata default). --- dnsapi/dns_ad.sh | 81 +++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index 158ea951..379b51f9 100644 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -5,8 +5,7 @@ #This is the Alwaysdata api wrapper for acme.sh -AD_HOST="api.alwaysdata.com" -AD_URL="https://$AD_API_KEY:@$AD_HOST" +AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1" ######## Public functions ##################### @@ -35,7 +34,7 @@ dns_ad_add() { _ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}" - if ad_rest POST "record/" "" "$_ad_tmpl_json" && [ -z "$response" ]; then + if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then _info "txt record updated success." return 0 fi @@ -57,37 +56,51 @@ dns_ad_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - if ad_rest DELETE "record/" "domain=$_domain_id&name=$_sub_domain" "" && [ -z "$response" ]; then - _info "txt record deleted success." - return 0 + _debug "Getting txt records" + _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain" + + if [ -n "$response" ]; then + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d \ | head -n 1) + _debug record_id "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if _ad_rest DELETE "record/$record_id/" && [ -z "$response" ]; then + _info "txt record deleted success." + return 0 + fi + _debug response "$response" + return 1 fi - _debug response "$response" 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 ad_rest GET "domain/"; then + if _ad_rest GET "domain/"; 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 - if _contains "$response" "$h"; then - hostedzone="$(echo "$response" | tr -d "\n" | sed 's//\n&/g' | _egrep_o ".*$h<.name>.*<.object>")" - if [ -z "$hostedzone" ]; then - _err "Error, can not get domain record." - return 1 - fi - _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o ".*<.id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>") + 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 @@ -103,37 +116,29 @@ _get_root() { } #method uri qstr data -ad_rest() { +_ad_rest() { mtd="$1" ep="$2" - qsr="$3" - data="$4" + data="$3" _debug mtd "$mtd" _debug ep "$ep" - _debug qsr "$qsr" - _debug data "$data" - _H1="Accept: application/xml" + _H1="Accept: application/json" + _H2="Content-Type: application/json" - url="$AD_URL/v1/$ep?$qsr" - - if [ "$mtd" = "GET" ]; then - response="$(_get "$url")" - elif [ "$mtd" = "DELETE" ]; then - response="$(_delete "$url")" + if [ "$mtd" != "GET" ]; then + # both POST and DELETE. + _debug data "$data" + response="$(_post "$data" "$AD_API_URL/$ep" "" "$mtd")" else - response="$(_post "$data" "$url")" + response="$(_get "$AD_API_URL/$ep")" fi - _ret="$?" - if [ "$_ret" = "0" ]; then - # Errors usually 404, otherwise just empty response. How to detect 404? - if _contains "$response" " Date: Tue, 3 Jan 2017 12:09:57 +0000 Subject: [PATCH 3/7] Merge suggested improvements. * Use `_head_n`. * Add link to GitHub repo for bug reporting. --- dnsapi/dns_ad.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index 379b51f9..81c20e09 100644 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -4,6 +4,9 @@ #AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" #This is the Alwaysdata api wrapper for acme.sh +# +#Author: Paul Koppen +#Report Bugs here: https://github.com/wpk-/acme.sh AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1" @@ -60,7 +63,7 @@ dns_ad_rm() { _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain" if [ -n "$response" ]; then - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d \ | head -n 1) + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d \ | _head_n 1) _debug record_id "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id to remove." @@ -100,7 +103,7 @@ _get_root() { 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 \ ) + _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 From b2686e5b6da57b51f2ddb55e8ca06e69777c8cab Mon Sep 17 00:00:00 2001 From: Paul Koppen Date: Tue, 3 Jan 2017 12:13:27 +0000 Subject: [PATCH 4/7] Add Alwaysdata.com to list of supported API's. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dde2eeae..9b5891c9 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,7 @@ You don't have to do anything manually! 1. nsupdate API 1. aliyun.com(阿里云) API 1. ISPConfig 3.1 API +1. Alwaysdata.com API **More APIs coming soon...** From 180f05f6f08bdfbec46d53c6b1b4bdcc7538e527 Mon Sep 17 00:00:00 2001 From: Paul Koppen Date: Tue, 3 Jan 2017 12:16:22 +0000 Subject: [PATCH 5/7] Add instructions for the Alwaysdata API. --- dnsapi/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index c4245701..e32b4655 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -240,6 +240,23 @@ acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 13. Use Alwaysdata domain API + +First you need to login to your Alwaysdata account to get your API Key. + +```sh +export AD_API_KEY="myalwaysdataapikey" +``` + +Ok, let's issue a cert now: + +```sh +acme.sh --issue --dns dns_ad -d example.com -d www.example.com +``` + +The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused +when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From b90917a52951e85cb5eda4ae51d6daedd921fbf7 Mon Sep 17 00:00:00 2001 From: Paul Koppen Date: Tue, 3 Jan 2017 12:33:10 +0000 Subject: [PATCH 6/7] Improve legibility. --- dnsapi/dns_ad.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index 81c20e09..cbec6c9b 100644 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -63,7 +63,7 @@ dns_ad_rm() { _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain" if [ -n "$response" ]; then - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d \ | _head_n 1) + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) _debug record_id "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id to remove." From 7b4be7be4094176098d889d7a37feab0dd45ace9 Mon Sep 17 00:00:00 2001 From: Paul Koppen Date: Tue, 3 Jan 2017 12:35:10 +0000 Subject: [PATCH 7/7] Remove spaces from blank lines. --- dnsapi/dns_ad.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index cbec6c9b..1c7e85f9 100644 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -36,12 +36,12 @@ dns_ad_add() { _debug _domain "$_domain" _ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}" - + if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then _info "txt record updated success." return 0 fi - + return 1 } @@ -61,7 +61,7 @@ dns_ad_rm() { _debug "Getting txt records" _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain" - + if [ -n "$response" ]; then record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) _debug record_id "$record_id" @@ -129,7 +129,7 @@ _ad_rest() { _H1="Accept: application/json" _H2="Content-Type: application/json" - + if [ "$mtd" != "GET" ]; then # both POST and DELETE. _debug data "$data"