Merge branch 'acmesh-official:dev' into dev

This commit is contained in:
Arnaud Launay
2023-07-09 20:00:52 +02:00
committed by GitHub
143 changed files with 13459 additions and 2686 deletions

View File

@@ -1,185 +1,175 @@
#!/usr/bin/env sh
#This file name is "dns_1984hosting.sh"
#So, here must be a method dns_1984hosting_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
# This file name is "dns_1984hosting.sh"
# So, here must be a method dns_1984hosting_add()
# Which will be called by acme.sh to add the txt record to your api system.
# returns 0 means success, otherwise error.
#Author: Adrian Fedoreanu
#Report Bugs here: https://github.com/acmesh-official/acme.sh
# Author: Adrian Fedoreanu
# Report Bugs here: https://github.com/acmesh-official/acme.sh
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
#
######## Public functions #####################
######## Public functions #####################
# Export 1984HOSTING username and password in following variables
#
# One984HOSTING_Username=username
# One984HOSTING_Password=password
#
# sessionid cookie is saved in ~/.acme.sh/account.conf
# username/password need to be set only when changed.
# username/password and csrftoken/sessionid cookies are saved in ~/.acme.sh/account.conf
#Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Add a text record.
dns_1984hosting_add() {
fulldomain=$1
txtvalue=$2
_info "Add TXT record using 1984Hosting"
_info "Add TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
_debug "First detect the root zone"
_debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
_err "invalid domain" "$fulldomain"
_err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_1984hosting_add_txt_record "$_domain" "$_sub_domain" "$txtvalue"
return $?
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_1984hosting_rm() {
fulldomain=$1
txtvalue=$2
_info "Delete TXT record using 1984Hosting"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain" "$fulldomain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_1984hosting_delete_txt_record "$_domain" "$_sub_domain"
return $?
}
#################### Private functions below ##################################
# usage _1984hosting_add_txt_record domain subdomain value
# returns 0 success
_1984hosting_add_txt_record() {
_debug "Add TXT record $1 with value '$3'"
domain="$1"
subdomain="$2"
value="$(printf '%s' "$3" | _url_encode)"
url="https://management.1984hosting.com/domains/entry/"
_debug "Add TXT record $fulldomain with value '$txtvalue'."
value="$(printf '%s' "$txtvalue" | _url_encode)"
url="https://1984.hosting/domains/entry/"
postdata="entry=new"
postdata="$postdata&type=TXT"
postdata="$postdata&ttl=3600"
postdata="$postdata&zone=$domain"
postdata="$postdata&host=$subdomain"
postdata="$postdata&ttl=900"
postdata="$postdata&zone=$_domain"
postdata="$postdata&host=$_sub_domain"
postdata="$postdata&rdata=%22$value%22"
_debug2 postdata "$postdata"
_authpost "$postdata" "$url"
response="$(echo "$_response" | _normalizeJson)"
_debug2 response "$response"
if _contains "$response" '"haserrors": true'; then
_err "1984Hosting failed to add TXT record for $subdomain bad RC from _post"
if _contains "$_response" '"haserrors": true'; then
_err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
return 1
elif _contains "$response" "<html>"; then
_err "1984Hosting failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
elif _contains "$_response" "html>"; then
_err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
return 1
elif _contains "$response" '"auth": false'; then
_err "1984Hosting failed to add TXT record for $subdomain. Invalid or expired cookie"
elif _contains "$_response" '"auth": false'; then
_err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
return 1
fi
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
# usage _1984hosting_delete_txt_record entry_id
# returns 0 success
_1984hosting_delete_txt_record() {
_debug "Delete $fulldomain TXT record"
domain="$1"
subdomain="$2"
url="https://management.1984hosting.com/domains"
# Usage: fulldomain txtvalue
# Remove the txt record after validation.
dns_1984hosting_rm() {
fulldomain=$1
txtvalue=$2
_htmlget "$url" "$domain"
_debug2 _response "$_response"
zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+')"
_debug2 zone_id "$zone_id"
if [ -z "$zone_id" ]; then
_err "Error getting zone_id for $1"
_info "Delete TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
_htmlget "$url/$zone_id" "$subdomain"
_debug2 _response "$_response"
_debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
_err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Delete $fulldomain TXT record."
url="https://1984.hosting/domains"
if ! _get_zone_id "$url" "$_domain"; then
_err "Invalid zone '$_domain'."
return 1
fi
_htmlget "$url/$_zone_id" "$txtvalue"
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
_debug2 entry_id "$entry_id"
if [ -z "$entry_id" ]; then
_err "Error getting TXT entry_id for $1"
_err "Error getting TXT entry_id for $1."
return 1
fi
_authpost "entry=$entry_id" "$url/delentry/"
response="$(echo "$_response" | _normalizeJson)"
_debug2 response "$response"
if ! _contains "$response" '"ok": true'; then
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
if ! _contains "$_response" '"ok": true'; then
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
return 1
fi
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
# usage: _1984hosting_login username password
# returns 0 success
#################### Private functions below ##################################
_1984hosting_login() {
if ! _check_credentials; then return 1; fi
if _check_cookie; then
_debug "Already logged in"
if _check_cookies; then
_debug "Already logged in."
return 0
fi
_debug "Login to 1984Hosting as user $One984HOSTING_Username"
_debug "Login to 1984Hosting as user $One984HOSTING_Username."
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
url="https://management.1984hosting.com/accounts/checkuserauth/"
url="https://1984.hosting/accounts/checkuserauth/"
response="$(_post "username=$username&password=$password&otpkey=" "$url")"
_get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
_err "One or more cookies are empty: '$csrftoken', '$sessionid'."
return 1
fi
export _H1="Cookie: $csrftoken; $sessionid"
export _H2="Referer: https://1984.hosting/accounts/login/"
csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
export _H3="X-CSRFToken: $csrf_header"
response="$(_post "username=$username&password=$password&otpkey=" $url)"
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
if _contains "$response" '"loggedin": true'; then
One984HOSTING_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _tail_n 1 | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
export One984HOSTING_COOKIE
_saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
export One984HOSTING_SESSIONID_COOKIE
export One984HOSTING_CSRFTOKEN_COOKIE
_saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
_saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
return 0
fi
return 1
}
_check_credentials() {
One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
One984HOSTING_Username=""
One984HOSTING_Password=""
_clearaccountconf_mutable One984HOSTING_Username
_clearaccountconf_mutable One984HOSTING_Password
_err "You haven't specified 1984Hosting username or password yet."
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
return 1
@@ -187,44 +177,47 @@ _check_credentials() {
return 0
}
_check_cookie() {
One984HOSTING_COOKIE="${One984HOSTING_COOKIE:-$(_readaccountconf_mutable One984HOSTING_COOKIE)}"
if [ -z "$One984HOSTING_COOKIE" ]; then
_debug "No cached cookie found"
_check_cookies() {
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
_debug "No cached cookie(s) found."
return 1
fi
_authget "https://management.1984hosting.com/accounts/loginstatus/"
response="$(echo "$_response" | _normalizeJson)"
if _contains "$response" '"ok": true'; then
_debug "Cached cookie still valid"
_authget "https://1984.hosting/accounts/loginstatus/"
if _contains "$_response" '"ok": true'; then
_debug "Cached cookies still valid."
return 0
fi
_debug "Cached cookie no longer valid"
One984HOSTING_COOKIE=""
_saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
_debug "Cached cookies no longer valid. Clearing cookies."
One984HOSTING_SESSIONID_COOKIE=""
One984HOSTING_CSRFTOKEN_COOKIE=""
_clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
_clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
return 1
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _acme-challenge.www.domain.com
# Returns
# _sub_domain=_acme-challenge.www
# _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)
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
# not valid
if [ -z "$h" ]; then
#not valid
return 1
fi
_authget "https://management.1984hosting.com/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
if _contains "$_response" "serial"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -234,21 +227,47 @@ _get_root() {
return 1
}
# add extra headers to request
# Usage: _get_zone_id url domain.com
# Returns zone id for domain.com
_get_zone_id() {
url=$1
domain=$2
_htmlget "$url" "$domain"
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
_debug2 _zone_id "$_zone_id"
if [ -z "$_zone_id" ]; then
_err "Error getting _zone_id for $2."
return 1
fi
return 0
}
# Add extra headers to request
_authget() {
export _H1="Cookie: $One984HOSTING_COOKIE"
_response=$(_get "$1")
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | _normalizeJson)
_debug2 _response "$_response"
}
# truncate huge HTML response
# echo: Argument list too long
# Truncate huge HTML response
# Echo: Argument list too long
_htmlget() {
export _H1="Cookie: $One984HOSTING_COOKIE"
_response=$(_get "$1" | grep "$2" | _head_n 1)
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | grep "$2")
if _contains "$_response" "@$2"; then
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
fi
_debug2 _response "$_response"
}
# add extra headers to request
# Add extra headers to request
_authpost() {
export _H1="Cookie: $One984HOSTING_COOKIE"
_response=$(_post "$1" "$2")
url="https://1984.hosting/domains"
_get_zone_id "$url" "$_domain"
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
export _H3="X-CSRFToken: $csrf_header"
_response="$(_post "$1" "$2" | _normalizeJson)"
_debug2 _response "$_response"
}

63
dnsapi/dns_acmedns.sh Normal file → Executable file
View File

@@ -1,31 +1,70 @@
#!/usr/bin/env sh
#
#Author: Wolfgang Ebner
#Report Bugs here: https://github.com/webner/acme.sh
#Author: Sven Neubuaer
#Report Bugs here: https://github.com/dampfklon/acme.sh
#
# Usage:
# export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
#
# You can optionally define an already existing account:
#
# export ACMEDNS_USERNAME="<username>"
# export ACMEDNS_PASSWORD="<password>"
# export ACMEDNS_SUBDOMAIN="<subdomain>"
#
######## Public functions #####################
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_acmedns_add() {
fulldomain=$1
txtvalue=$2
_info "Using acme-dns"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug "fulldomain $fulldomain"
_debug "txtvalue $txtvalue"
ACMEDNS_UPDATE_URL="${ACMEDNS_UPDATE_URL:-$(_readaccountconf_mutable ACMEDNS_UPDATE_URL)}"
#for compatiblity from account conf
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readaccountconf_mutable ACMEDNS_USERNAME)}"
_clearaccountconf_mutable ACMEDNS_USERNAME
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readaccountconf_mutable ACMEDNS_PASSWORD)}"
_clearaccountconf_mutable ACMEDNS_PASSWORD
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readaccountconf_mutable ACMEDNS_SUBDOMAIN)}"
_clearaccountconf_mutable ACMEDNS_SUBDOMAIN
if [ "$ACMEDNS_UPDATE_URL" = "" ]; then
ACMEDNS_UPDATE_URL="https://auth.acme-dns.io/update"
ACMEDNS_BASE_URL="${ACMEDNS_BASE_URL:-$(_readdomainconf ACMEDNS_BASE_URL)}"
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readdomainconf ACMEDNS_USERNAME)}"
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readdomainconf ACMEDNS_PASSWORD)}"
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readdomainconf ACMEDNS_SUBDOMAIN)}"
if [ "$ACMEDNS_BASE_URL" = "" ]; then
ACMEDNS_BASE_URL="https://auth.acme-dns.io"
fi
_saveaccountconf_mutable ACMEDNS_UPDATE_URL "$ACMEDNS_UPDATE_URL"
_saveaccountconf_mutable ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
_saveaccountconf_mutable ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
_saveaccountconf_mutable ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
ACMEDNS_UPDATE_URL="$ACMEDNS_BASE_URL/update"
ACMEDNS_REGISTER_URL="$ACMEDNS_BASE_URL/register"
if [ -z "$ACMEDNS_USERNAME" ] || [ -z "$ACMEDNS_PASSWORD" ]; then
response="$(_post "" "$ACMEDNS_REGISTER_URL" "" "POST")"
_debug response "$response"
ACMEDNS_USERNAME=$(echo "$response" | sed -n 's/^{.*\"username\":[ ]*\"\([^\"]*\)\".*}/\1/p')
_debug "received username: $ACMEDNS_USERNAME"
ACMEDNS_PASSWORD=$(echo "$response" | sed -n 's/^{.*\"password\":[ ]*\"\([^\"]*\)\".*}/\1/p')
_debug "received password: $ACMEDNS_PASSWORD"
ACMEDNS_SUBDOMAIN=$(echo "$response" | sed -n 's/^{.*\"subdomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
_debug "received subdomain: $ACMEDNS_SUBDOMAIN"
ACMEDNS_FULLDOMAIN=$(echo "$response" | sed -n 's/^{.*\"fulldomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
_info "##########################################################"
_info "# Create $fulldomain CNAME $ACMEDNS_FULLDOMAIN DNS entry #"
_info "##########################################################"
_info "Press enter to continue... "
read -r _
fi
_savedomainconf ACMEDNS_BASE_URL "$ACMEDNS_BASE_URL"
_savedomainconf ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
_savedomainconf ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
_savedomainconf ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
export _H1="X-Api-User: $ACMEDNS_USERNAME"
export _H2="X-Api-Key: $ACMEDNS_PASSWORD"
@@ -48,8 +87,8 @@ dns_acmedns_rm() {
fulldomain=$1
txtvalue=$2
_info "Using acme-dns"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug "fulldomain $fulldomain"
_debug "txtvalue $txtvalue"
}
#################### Private functions below ##################################

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env sh
## Acmeproxy DNS provider to be used with acmeproxy (http://github.com/mdbraber/acmeproxy)
## Acmeproxy DNS provider to be used with acmeproxy (https://github.com/mdbraber/acmeproxy)
## API integration by Maarten den Braber
##
## Report any bugs via https://github.com/mdbraber/acme.sh

150
dnsapi/dns_anx.sh Normal file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/env sh
# Anexia CloudDNS acme.sh hook
# Author: MA
#ANX_Token="xxxx"
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
######## Public functions #####################
dns_anx_add() {
fulldomain=$1
txtvalue=$2
_info "Using ANX CDNS API"
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if [ "$ANX_Token" ]; then
_saveaccountconf_mutable ANX_Token "$ANX_Token"
else
_err "You didn't specify a ANEXIA Engine API token."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
# Always add records, wildcard need two records with the same name
_anx_rest POST "zone.json/${_domain}/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"rdata\":\"$txtvalue\"}"
if _contains "$response" "$txtvalue"; then
return 0
else
return 1
fi
}
dns_anx_rm() {
fulldomain=$1
txtvalue=$2
_info "Using ANX CDNS API"
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_get_record_id
if _is_uuid "$_record_id"; then
if ! _anx_rest DELETE "zone.json/${_domain}/records/$_record_id"; then
_err "Delete record"
return 1
fi
else
_info "No record found."
fi
echo "$response" | tr -d " " | grep \"status\":\"OK\" >/dev/null
}
#################### Private functions below ##################################
_is_uuid() {
pattern='^\{?[A-Z0-9a-z]{8}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{12}\}?$'
if echo "$1" | _egrep_o "$pattern" >/dev/null; then
return 0
fi
return 1
}
_get_record_id() {
_debug subdomain "$_sub_domain"
_debug domain "$_domain"
if _anx_rest GET "zone.json/${_domain}/records?name=$_sub_domain&type=TXT"; then
_debug response "$response"
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
_record_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"identifier\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
else
_record_id=''
fi
else
_err "Search existing record"
fi
}
_anx_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/json"
export _H2="Authorization: Token $ANX_Token"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "${ANX_API}/$ep" "" "$m")"
else
response="$(_get "${ANX_API}/$ep")"
fi
# shellcheck disable=SC2181
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug response "$response"
return 0
}
_get_root() {
domain=$1
i=1
p=1
_anx_rest GET "zone.json"
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" "\"name\":\"$h\""; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}

View File

@@ -1,11 +1,10 @@
#!/usr/bin/env sh
#Arvan_Token="xxxx"
# Arvan_Token="Apikey xxxx"
ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
#Author: Ehsan Aliakbar
#Report Bugs here: https://github.com/Neilpang/acme.sh
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
# Author: Vahid Fardi
# Report Bugs here: https://github.com/Neilpang/acme.sh
#
######## Public functions #####################
@@ -19,7 +18,7 @@ dns_arvan_add() {
if [ -z "$Arvan_Token" ]; then
_err "You didn't specify \"Arvan_Token\" token yet."
_err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
_err "You can get yours from here https://npanel.arvancloud.ir/profile/api-keys"
return 1
fi
#save the api token to the account conf file.
@@ -38,9 +37,10 @@ dns_arvan_add() {
_info "Adding record"
if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
if _contains "$response" "$txtvalue"; then
_info "response id is $response"
_info "Added, OK"
return 0
elif _contains "$response" "Record Data is Duplicated"; then
elif _contains "$response" "Record Data is duplicate"; then
_info "Already exists, OK"
return 0
else
@@ -49,7 +49,7 @@ dns_arvan_add() {
fi
fi
_err "Add txt record error."
return 1
return 0
}
#Usage: fulldomain txtvalue
@@ -73,33 +73,21 @@ dns_arvan_rm() {
_debug _domain "$_domain"
_debug "Getting txt records"
shorted_txtvalue=$(printf "%s" "$txtvalue" | cut -d "-" -d "_" -f1)
_arvan_rest GET "${_domain}/dns-records?search=$shorted_txtvalue"
_arvan_rest GET "${_domain}/dns-records"
if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
_err "Error on Arvan Api"
_err "Please create a github issue with debbug log"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"total\":[^,]*" | cut -d : -f 2)
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | 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 ! _arvan_rest "DELETE" "${_domain}/dns-records/$record_id"; then
_err "Delete record error."
return 1
fi
_debug "$response"
_contains "$response" 'dns record deleted'
_record_id=$(echo "$response" | _egrep_o ".\"id\":\"[^\"]*\",\"type\":\"txt\",\"name\":\"_acme-challenge\",\"value\":{\"text\":\"$txtvalue\"}" | cut -d : -f 2 | cut -d , -f 1 | tr -d \")
if ! _arvan_rest "DELETE" "${_domain}/dns-records/${_record_id}"; then
_err "Error on Arvan Api"
return 1
fi
_debug "$response"
_contains "$response" 'dns record deleted'
return 0
}
#################### Private functions below ##################################
@@ -111,7 +99,7 @@ dns_arvan_rm() {
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
@@ -121,12 +109,11 @@ _get_root() {
return 1
fi
if ! _arvan_rest GET "?search=$h"; then
if ! _arvan_rest GET "$h"; then
return 1
fi
if _contains "$response" "\"domain\":\"$h\"" || _contains "$response" '"total":1'; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if _contains "$response" "\"domain\":\"$h\""; then
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
@@ -146,7 +133,6 @@ _arvan_rest() {
data="$3"
token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
export _H1="Authorization: $token_trimmed"
if [ "$mtd" = "DELETE" ]; then
@@ -155,9 +141,11 @@ _arvan_rest() {
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
elif [ "$mtd" = "POST" ]; then
export _H2="Content-Type: application/json"
export _H3="Accept: application/json"
_debug data "$data"
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
else
response="$(_get "$ARVAN_API_URL/$ep$data")"
fi
return 0
}

171
dnsapi/dns_aurora.sh Normal file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/env sh
#
#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje"
AURORA_Api="https://api.auroradns.eu"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_aurora_add() {
fulldomain=$1
txtvalue=$2
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
if [ -z "$AURORA_Key" ] || [ -z "$AURORA_Secret" ]; then
AURORA_Key=""
AURORA_Secret=""
_err "You didn't specify an Aurora api key and secret yet."
_err "You can get yours from here https://cp.pcextreme.nl/auroradns/users."
return 1
fi
#save the api key and secret to the account conf file.
_saveaccountconf_mutable AURORA_Key "$AURORA_Key"
_saveaccountconf_mutable AURORA_Secret "$AURORA_Secret"
_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"
_info "Adding record"
if _aurora_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
elif _contains "$response" "RecordExistsError"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_aurora_rm() {
fulldomain=$1
txtvalue=$2
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
_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 records"
_aurora_rest GET "zones/${_domain_id}/records"
if ! _contains "$response" "$txtvalue"; then
_info "Don't need to remove."
else
records=$(echo "$response" | _normalizeJson | tr -d "[]" | sed "s/},{/}|{/g" | tr "|" "\n")
if [ "$(echo "$records" | wc -l)" -le 2 ]; then
_err "Can not parse records."
return 1
fi
record_id=$(echo "$records" | grep "\"type\": *\"TXT\"" | grep "\"name\": *\"$_sub_domain\"" | grep "\"content\": *\"$txtvalue\"" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _aurora_rest DELETE "zones/$_domain_id/records/$record_id"; then
_err "Delete record error."
return 1
fi
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _aurora_rest GET "zones/$h"; then
return 1
fi
if _contains "$response" "\"name\": \"$h\""; then
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _domain_id "$_domain_id"
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
return 1
}
_aurora_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
key_trimmed=$(echo "$AURORA_Key" | tr -d '"')
secret_trimmed=$(echo "$AURORA_Secret" | tr -d '"')
timestamp=$(date -u +"%Y%m%dT%H%M%SZ")
signature=$(printf "%s/%s%s" "$m" "$ep" "$timestamp" | _hmac sha256 "$(printf "%s" "$secret_trimmed" | _hex_dump | tr -d " ")" | _base64)
authorization=$(printf "AuroraDNSv1 %s" "$(printf "%s:%s" "$key_trimmed" "$signature" | _base64)")
export _H1="Content-Type: application/json; charset=UTF-8"
export _H2="X-AuroraDNS-Date: $timestamp"
export _H3="Authorization: $authorization"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$AURORA_Api/$ep" "" "$m")"
else
response="$(_get "$AURORA_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -32,7 +32,7 @@ dns_aws_add() {
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
_err "You haven't specifed the aws route53 api key id and and api key secret yet."
_err "You haven't specified the aws route53 api key id and and api key secret yet."
_err "Please create your key and try again. see $(__green $AWS_WIKI)"
return 1
fi
@@ -152,34 +152,23 @@ dns_aws_rm() {
_get_root() {
domain=$1
i=2
i=1
p=1
if aws_rest GET "2013-04-01/hostedzone"; then
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated"
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | 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
_err "Invalid domain"
return 1
fi
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug "Checking domain: $h"
if [ -z "$h" ]; then
_error "invalid domain"
return 1
fi
# iterate over paginated result for list_hosted_zones
aws_rest GET "2013-04-01/hostedzone"
while true; do
if _contains "$response" "<Name>$h.</Name>"; then
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
hostedzone="$(echo "$response" | tr -d '\n' | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
_debug hostedzone "$hostedzone"
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
@@ -192,10 +181,19 @@ _get_root() {
return 1
fi
fi
p=$i
i=$(_math "$i" + 1)
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated"
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug "NextMarker" "$_nextMarker"
else
break
fi
_debug "Checking domain: $h - Next Page "
aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"
done
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}

204
dnsapi/dns_azion.sh Normal file
View File

@@ -0,0 +1,204 @@
#!/usr/bin/env sh
#
#AZION_Email=""
#AZION_Password=""
#
AZION_Api="https://api.azionapi.net"
######## Public functions ########
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_azion_add() {
fulldomain=$1
txtvalue=$2
_debug "Detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Domain not found"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _domain_id "$_domain_id"
_info "Add or update record"
_get_record "$_domain_id" "$_sub_domain"
if [ "$record_id" ]; then
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [$answers_list, \"$txtvalue\"], \"ttl\": 20}"
if _azion_rest PUT "intelligent_dns/$_domain_id/records/$record_id" "$_payload"; then
if _contains "$response" "$txtvalue"; then
_info "Record updated."
return 0
fi
fi
else
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [\"$txtvalue\"], \"ttl\": 20}"
if _azion_rest POST "intelligent_dns/$_domain_id/records" "$_payload"; then
if _contains "$response" "$txtvalue"; then
_info "Record added."
return 0
fi
fi
fi
_err "Failed to add or update record."
return 1
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_azion_rm() {
fulldomain=$1
txtvalue=$2
_debug "Detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Domain not found"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _domain_id "$_domain_id"
_info "Removing record"
_get_record "$_domain_id" "$_sub_domain"
if [ "$record_id" ]; then
if _azion_rest DELETE "intelligent_dns/$_domain_id/records/$record_id"; then
_info "Record removed."
return 0
else
_err "Failed to remove record."
return 1
fi
else
_info "Record not found or already removed."
return 0
fi
}
#################### Private functions below ##################################
# Usage: _acme-challenge.www.domain.com
# returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
if ! _azion_rest GET "intelligent_dns"; then
return 1
fi
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" "\"domain\":\"$h\""; then
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
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
return 1
}
_get_record() {
_domain_id=$1
_record=$2
if ! _azion_rest GET "intelligent_dns/$_domain_id/records"; then
return 1
fi
if _contains "$response" "\"entry\":\"$_record\""; then
_json_record=$(echo "$response" | tr '{' "\n" | grep "\"entry\":\"$_record\"")
if [ "$_json_record" ]; then
record_id=$(echo "$_json_record" | _egrep_o "\"record_id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
answers_list=$(echo "$_json_record" | _egrep_o "\"answers_list\":\[.*\]" | _head_n 1 | cut -d : -f 2 | tr -d \[\])
return 0
fi
return 1
fi
return 1
}
_get_token() {
AZION_Email="${AZION_Email:-$(_readaccountconf_mutable AZION_Email)}"
AZION_Password="${AZION_Password:-$(_readaccountconf_mutable AZION_Password)}"
if ! _contains "$AZION_Email" "@"; then
_err "It seems that the AZION_Email is not a valid email address. Revalidate your environments."
return 1
fi
if [ -z "$AZION_Email" ] || [ -z "$AZION_Password" ]; then
_err "You didn't specified a AZION_Email/AZION_Password to generate Azion token."
return 1
fi
_saveaccountconf_mutable AZION_Email "$AZION_Email"
_saveaccountconf_mutable AZION_Password "$AZION_Password"
_basic_auth=$(printf "%s:%s" "$AZION_Email" "$AZION_Password" | _base64)
_debug _basic_auth "$_basic_auth"
export _H1="Accept: application/json; version=3"
export _H2="Content-Type: application/json"
export _H3="Authorization: Basic $_basic_auth"
response="$(_post "" "$AZION_Api/tokens" "" "POST")"
if _contains "$response" "\"token\":\"" >/dev/null; then
_azion_token=$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
export AZION_Token="$_azion_token"
else
_err "Failed to generate Azion token"
return 1
fi
}
_azion_rest() {
_method=$1
_uri="$2"
_data="$3"
if [ -z "$AZION_Token" ]; then
_get_token
fi
_debug2 token "$AZION_Token"
export _H1="Accept: application/json; version=3"
export _H2="Content-Type: application/json"
export _H3="Authorization: token $AZION_Token"
if [ "$_method" != "GET" ]; then
_debug _data "$_data"
response="$(_post "$_data" "$AZION_Api/$_uri" "" "$_method")"
else
response="$(_get "$AZION_Api/$_uri")"
fi
_debug2 response "$response"
if [ "$?" != "0" ]; then
_err "error $_method $_uri $_data"
return 1
fi
return 0
}

View File

@@ -9,57 +9,72 @@ WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS"
#
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
#
dns_azure_add() {
fulldomain=$1
txtvalue=$2
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Subscription ID "
_err "You didn't specify the Azure Subscription ID"
return 1
fi
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
#save account details to account conf file.
#save subscription id to account conf file.
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
_info "Using Azure managed identity"
#save managed identity as preferred authentication method, clear service principal credentials from conf file.
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "$AZUREDNS_MANAGEDIDENTITY"
_saveaccountconf_mutable AZUREDNS_TENANTID ""
_saveaccountconf_mutable AZUREDNS_APPID ""
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
else
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
#save account details to account conf file, don't opt in for azure manages identity check.
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "false"
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
fi
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
@@ -116,10 +131,6 @@ dns_azure_rm() {
txtvalue=$2
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
@@ -129,34 +140,44 @@ dns_azure_rm() {
return 1
fi
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
_info "Using Azure managed identity"
else
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
@@ -258,9 +279,10 @@ _azure_rest() {
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
_azure_getaccess_token() {
tenantID=$1
clientID=$2
clientSecret=$3
managedIdentity=$1
tenantID=$2
clientID=$3
clientSecret=$4
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
@@ -278,17 +300,25 @@ _azure_getaccess_token() {
fi
_debug "getting new bearer token"
export _H1="accept: application/json"
export _H2="Content-Type: application/x-www-form-urlencoded"
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
_secure_debug2 "data $body"
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
_ret="$?"
_secure_debug2 "response $response"
response="$(echo "$response" | _normalizeJson)"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$managedIdentity" = true ]; then
# https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
export _H1="Metadata: true"
response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
response="$(echo "$response" | _normalizeJson)"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
else
export _H1="accept: application/json"
export _H2="Content-Type: application/x-www-form-urlencoded"
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
_secure_debug2 "data $body"
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
_ret="$?"
_secure_debug2 "response $response"
response="$(echo "$response" | _normalizeJson)"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
fi
if [ -z "$accesstoken" ]; then
_err "no acccess token received. Check your Azure settings see $WIKI"

248
dnsapi/dns_bunny.sh Normal file
View File

@@ -0,0 +1,248 @@
#!/usr/bin/env sh
## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
## returns 0 means success, otherwise error.
## Author: nosilver4u <nosilver4u at ewww.io>
## GitHub: https://github.com/nosilver4u/acme.sh
##
## Environment Variables Required:
##
## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
##
##################### Public functions #####################
## Create the text record for validation.
## Usage: fulldomain txtvalue
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
dns_bunny_add() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
# Check if API Key is set
if [ -z "$BUNNY_API_KEY" ]; then
BUNNY_API_KEY=""
_err "You did not specify Bunny.net API key."
_err "Please export BUNNY_API_KEY and try again."
return 1
fi
_info "Using Bunny.net dns validation - add record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## save the env vars (key and domain split location) for later automated use
_saveaccountconf_mutable BUNNY_API_KEY "$BUNNY_API_KEY"
## split the domain for Bunny API
if ! _get_base_domain "$fulldomain"; then
_err "domain not found in your account for addition"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _domain_id "$_domain_id"
## Set the header with our post type and auth key
export _H1="Accept: application/json"
export _H2="AccessKey: $BUNNY_API_KEY"
export _H3="Content-Type: application/json"
PURL="https://api.bunny.net/dnszone/$_domain_id/records"
PBODY='{"Id":'$_domain_id',"Type":3,"Name":"'$_sub_domain'","Value":"'$txtvalue'","ttl":120}'
_debug PURL "$PURL"
_debug PBODY "$PBODY"
## the create request - POST
## args: BODY, URL, [need64, httpmethod]
response="$(_post "$PBODY" "$PURL" "" "PUT")"
## check response
if [ "$?" != "0" ]; then
_err "error in response: $response"
return 1
fi
_debug2 response "$response"
## finished correctly
return 0
}
## Remove the txt record after validation.
## Usage: fulldomain txtvalue
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
dns_bunny_rm() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
# Check if API Key Exists
if [ -z "$BUNNY_API_KEY" ]; then
BUNNY_API_KEY=""
_err "You did not specify Bunny.net API key."
_err "Please export BUNNY_API_KEY and try again."
return 1
fi
_info "Using Bunny.net dns validation - remove record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## split the domain for Bunny API
if ! _get_base_domain "$fulldomain"; then
_err "Domain not found in your account for TXT record removal"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug _domain_id "$_domain_id"
## Set the header with our post type and key auth key
export _H1="Accept: application/json"
export _H2="AccessKey: $BUNNY_API_KEY"
## get URL for the list of DNS records
GURL="https://api.bunny.net/dnszone/$_domain_id"
## 1) Get the domain/zone records
## the fetch request - GET
## args: URL, [onlyheader, timeout]
domain_list="$(_get "$GURL")"
## check response
if [ "$?" != "0" ]; then
_err "error in domain_list response: $domain_list"
return 1
fi
_debug2 domain_list "$domain_list"
## 2) search through records
## check for what we are looking for: "Type":3,"Value":"$txtvalue","Name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"Id\"\s*\:\s*\"*[0-9]+\"*,\s*\"Type\"[^}]*\"Value\"\s*\:\s*\"$txtvalue\"[^}]*\"Name\"\s*\:\s*\"$_sub_domain\"")"
if [ -n "$record" ]; then
## We found records
rec_ids="$(echo "$record" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_ids "$rec_ids"
if [ -n "$rec_ids" ]; then
echo "$rec_ids" | while IFS= read -r rec_id; do
## delete the record
## delete URL for removing the one we dont want
DURL="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id"
## the removal request - DELETE
## args: BODY, URL, [need64, httpmethod]
response="$(_post "" "$DURL" "" "DELETE")"
## check response (sort of)
if [ "$?" != "0" ]; then
_err "error in remove response: $response"
return 1
fi
_debug2 response "$response"
done
fi
fi
## finished correctly
return 0
}
##################### Private functions below #####################
## Split the domain provided into the "base domain" and the "start prefix".
## This function searches for the longest subdomain in your account
## for the full domain given and splits it into the base domain (zone)
## and the prefix/record to be added/removed
## USAGE: fulldomain
## EG: "_acme-challenge.two.three.four.domain.com"
## returns
## _sub_domain="_acme-challenge.two"
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
## _domain_id=234
## if only "domain.com" exists it will return
## _sub_domain="_acme-challenge.two.three.four"
## _domain="domain.com"
## _domain_id=234
_get_base_domain() {
# args
fulldomain="$(echo "$1" | _lower_case)"
_debug fulldomain "$fulldomain"
# domain max legal length = 253
MAX_DOM=255
page=1
## get a list of domains for the account to check thru
## Set the headers
export _H1="Accept: application/json"
export _H2="AccessKey: $BUNNY_API_KEY"
_debug BUNNY_API_KEY "$BUNNY_API_KEY"
## get URL for the list of domains
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
DOMURL="https://api.bunny.net/dnszone"
## while we dont have a matching domain we keep going
while [ -z "$found" ]; do
## get the domain list (current page)
domain_list="$(_get "$DOMURL")"
## check response
if [ "$?" != "0" ]; then
_err "error in domain_list response: $domain_list"
return 1
fi
_debug2 domain_list "$domain_list"
i=1
while [ $i -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
## check we got something back from our cut (or are we at the end)
if [ -z "$_domain" ]; then
break
fi
## we got part of a domain back - grep it out
found="$(echo "$domain_list" | _egrep_o "\"Id\"\s*:\s*\"*[0-9]+\"*,\s*\"Domain\"\s*\:\s*\"$_domain\"")"
## check if it exists
if [ -n "$found" ]; then
## exists - exit loop returning the parts
sub_point=$(_math $i - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
_domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug _domain_id "$_domain_id"
_debug _domain "$_domain"
_debug _sub_domain "$_sub_domain"
found=""
return 0
fi
## increment cut point $i
i=$(_math $i + 1)
done
if [ -z "$found" ]; then
page=$(_math $page + 1)
nextpage="https://api.bunny.net/dnszone?page=$page"
## Find the next page if we don't have a match.
hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
if [ -z "$hasnextpage" ]; then
_err "No record and no nextpage in Bunny.net domain search."
found=""
return 1
fi
_debug2 nextpage "$nextpage"
DOMURL="$nextpage"
fi
done
## We went through the entire domain zone list and didn't find one that matched.
## If we ever get here, something is broken in the code...
_err "Domain not found in Bunny.net account, but we should never get here!"
found=""
return 1
}

View File

@@ -25,9 +25,16 @@ dns_cf_add() {
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ "$CF_Token" ]; then
_saveaccountconf_mutable CF_Token "$CF_Token"
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
_saveaccountconf_mutable CF_Zone_ID "$CF_Zone_ID"
if [ "$CF_Zone_ID" ]; then
_savedomainconf CF_Token "$CF_Token"
_savedomainconf CF_Account_ID "$CF_Account_ID"
_savedomainconf CF_Zone_ID "$CF_Zone_ID"
else
_saveaccountconf_mutable CF_Token "$CF_Token"
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
_clearaccountconf_mutable CF_Zone_ID
_clearaccountconf CF_Zone_ID
fi
else
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key=""
@@ -45,6 +52,14 @@ dns_cf_add() {
#save the api key and email to the account conf file.
_saveaccountconf_mutable CF_Key "$CF_Key"
_saveaccountconf_mutable CF_Email "$CF_Email"
_clearaccountconf_mutable CF_Token
_clearaccountconf_mutable CF_Account_ID
_clearaccountconf_mutable CF_Zone_ID
_clearaccountconf CF_Token
_clearaccountconf CF_Account_ID
_clearaccountconf CF_Zone_ID
fi
_debug "First detect the root zone"

View File

@@ -2,11 +2,14 @@
# Author: Boyan Peychev <boyan at cloudns dot net>
# Repository: https://github.com/ClouDNS/acme.sh/
# Editor: I Komang Suryadana
#CLOUDNS_AUTH_ID=XXXXX
#CLOUDNS_SUB_AUTH_ID=XXXXX
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
CLOUDNS_API="https://api.cloudns.net"
DOMAIN_TYPE=
DOMAIN_MASTER=
######## Public functions #####################
@@ -61,12 +64,21 @@ dns_cloudns_rm() {
host="$(echo "$1" | sed "s/\.$zone\$//")"
record=$2
_dns_cloudns_get_zone_info "$zone"
_debug "Type" "$DOMAIN_TYPE"
_debug "Cloud Master" "$DOMAIN_MASTER"
if _contains "$DOMAIN_TYPE" "cloud"; then
zone=$DOMAIN_MASTER
fi
_debug "ZONE" "$zone"
_dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT"
if ! _contains "$response" "\"id\":"; then
return 1
fi
for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
for i in $(echo "$response" | tr '{' "\n" | grep -- "$record"); do
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
if [ -n "$record_id" ]; then
@@ -134,6 +146,18 @@ _dns_cloudns_init_check() {
return 0
}
_dns_cloudns_get_zone_info() {
zone=$1
_dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zone"
if ! _contains "$response" "\"status\":\"Failed\""; then
DOMAIN_TYPE=$(echo "$response" | _egrep_o '"type":"[^"]*"' | cut -d : -f 2 | tr -d '"')
if _contains "$DOMAIN_TYPE" "cloud"; then
DOMAIN_MASTER=$(echo "$response" | _egrep_o '"cloud-master":"[^"]*"' | cut -d : -f 2 | tr -d '"')
fi
fi
return 0
}
_dns_cloudns_get_zone_name() {
i=2
while true; do

View File

@@ -30,16 +30,41 @@ dns_constellix_add() {
return 1
fi
_info "Adding TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":120,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
_info "Added"
return 0
# The TXT record might already exist when working with wildcard certificates. In that case, update the record by adding the new value.
_debug "Search TXT record"
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
_info "Adding TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":60,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
_info "Added"
return 0
else
_err "Error adding TXT record"
fi
fi
else
_err "Error adding TXT record"
return 1
_record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
if _constellix_rest GET "domains/${_domain_id}/records/TXT/${_record_id}"; then
_new_rr_values=$(printf "%s\n" "$response" | _egrep_o '"roundRobin":\[[^]]*\]' | sed "s/\]$/,{\"value\":\"${txtvalue}\"}]/")
_debug _new_rr_values "$_new_rr_values"
_info "Updating TXT record"
if _constellix_rest PUT "domains/${_domain_id}/records/TXT/${_record_id}" "{\"name\":\"${_sub_domain}\",\"ttl\":60,${_new_rr_values}}"; then
if printf -- "%s" "$response" | grep "{\"success\":\"Record.*updated successfully\"}" >/dev/null; then
_info "Updated"
return 0
elif printf -- "%s" "$response" | grep "{\"errors\":\[\"Contents are identical\"\]}" >/dev/null; then
_info "Already exists, no need to update"
return 0
else
_err "Error updating TXT record"
fi
fi
fi
fi
fi
return 1
}
# Usage: fulldomain txtvalue
@@ -61,16 +86,26 @@ dns_constellix_rm() {
return 1
fi
_info "Removing TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
# The TXT record might have been removed already when working with some wildcard certificates.
_debug "Search TXT record"
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
_info "Removed"
return 0
else
_err "Error removing TXT record"
return 1
_info "Removing TXT record"
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
_info "Removed"
return 0
else
_err "Error removing TXT record"
fi
fi
fi
fi
return 1
}
#################### Private functions below ##################################
@@ -91,7 +126,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+" | cut -d ':' -f 2)
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
_domain="$h"

160
dnsapi/dns_cpanel.sh Executable file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env sh
#
#Author: Bjarne Saltbaek
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732
#
#
######## Public functions #####################
#
# Export CPANEL username,api token and hostname in the following variables
#
# cPanel_Username=username
# cPanel_Apitoken=apitoken
# cPanel_Hostname=hostname
#
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_cpanel_add() {
fulldomain=$1
txtvalue=$2
_info "Adding TXT record to cPanel based system"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug cPanel_Username "$cPanel_Username"
_debug cPanel_Apitoken "$cPanel_Apitoken"
_debug cPanel_Hostname "$cPanel_Hostname"
if ! _cpanel_login; then
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "No matching root domain for $fulldomain found"
return 1
fi
# adding entry
_info "Adding the entry"
stripped_fulldomain=$(echo "$fulldomain" | sed "s/.$_domain//")
_debug "Adding $stripped_fulldomain to $_domain zone"
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&domain=$_domain&name=$stripped_fulldomain&type=TXT&txtdata=$txtvalue&ttl=1"
if _successful_update; then return 0; fi
_err "Couldn't create entry!"
return 1
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_cpanel_rm() {
fulldomain=$1
txtvalue=$2
_info "Using cPanel based system"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _cpanel_login; then
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
return 1
fi
if ! _get_root; then
_err "No matching root domain for $fulldomain found"
return 1
fi
_findentry "$fulldomain" "$txtvalue"
if [ -z "$_id" ]; then
_info "Entry doesn't exist, nothing to delete"
return 0
fi
_debug "Deleting record..."
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&domain=$_domain&line=$_id"
# removing entry
_debug "_result is: $_result"
if _successful_update; then return 0; fi
_err "Couldn't delete entry!"
return 1
}
#################### Private functions below ##################################
_checkcredentials() {
cPanel_Username="${cPanel_Username:-$(_readaccountconf_mutable cPanel_Username)}"
cPanel_Apitoken="${cPanel_Apitoken:-$(_readaccountconf_mutable cPanel_Apitoken)}"
cPanel_Hostname="${cPanel_Hostname:-$(_readaccountconf_mutable cPanel_Hostname)}"
if [ -z "$cPanel_Username" ] || [ -z "$cPanel_Apitoken" ] || [ -z "$cPanel_Hostname" ]; then
cPanel_Username=""
cPanel_Apitoken=""
cPanel_Hostname=""
_err "You haven't specified cPanel username, apitoken and hostname yet."
_err "Please add credentials and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable cPanel_Username "$cPanel_Username"
_saveaccountconf_mutable cPanel_Apitoken "$cPanel_Apitoken"
_saveaccountconf_mutable cPanel_Hostname "$cPanel_Hostname"
return 0
}
_cpanel_login() {
if ! _checkcredentials; then return 1; fi
if ! _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=CustInfo&cpanel_jsonapi_func=displaycontactinfo"; then
_err "cPanel login failed for user $cPanel_Username."
return 1
fi
return 0
}
_myget() {
#Adds auth header to request
export _H1="Authorization: cpanel $cPanel_Username:$cPanel_Apitoken"
_result=$(_get "$cPanel_Hostname/$1")
}
_get_root() {
_myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones'
_domains=$(echo "$_result" | _egrep_o '"[a-z0-9\.\-]*":\["; cPanel first' | cut -d':' -f1 | sed 's/"//g' | sed 's/{//g')
_debug "_result is: $_result"
_debug "_domains is: $_domains"
if [ -z "$_domains" ]; then
_err "Primary domain list not found!"
return 1
fi
for _domain in $_domains; do
_debug "Checking if $fulldomain ends with $_domain"
if (_endswith "$fulldomain" "$_domain"); then
_debug "Root domain: $_domain"
return 0
fi
done
return 1
}
_successful_update() {
if (echo "$_result" | _egrep_o 'data":\[[^]]*]' | grep -q '"newserial":null'); then return 1; fi
return 0
}
_findentry() {
_debug "In _findentry"
#returns id of dns entry, if it exists
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain"
_id=$(echo "$_result" | sed -e "s/},{/},\n{/g" | grep "$fulldomain" | grep "$txtvalue" | _egrep_o 'line":[0-9]+' | cut -d ':' -f 2)
_debug "_result is: $_result"
_debug "fulldomain. is $fulldomain."
_debug "txtvalue is $txtvalue"
_debug "_id is: $_id"
if [ -n "$_id" ]; then
_debug "Entry found with _id=$_id"
return 0
fi
return 1
}

159
dnsapi/dns_curanet.sh Normal file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env sh
#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management.
#Requires api credentials with scope: dns
#Author: Peter L. Hansen <peter@r12.dk>
#Version 1.0
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
CURANET_ACCESS_TOKEN=""
######## Public functions #####################
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_curanet_add() {
fulldomain=$1
txtvalue=$2
_info "Using curanet"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
if [ -z "$CURANET_AUTHCLIENTID" ] || [ -z "$CURANET_AUTHSECRET" ]; then
CURANET_AUTHCLIENTID=""
CURANET_AUTHSECRET=""
_err "You don't specify curanet api client and secret."
_err "Please create your auth info and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable CURANET_AUTHCLIENTID "$CURANET_AUTHCLIENTID"
_saveaccountconf_mutable CURANET_AUTHSECRET "$CURANET_AUTHSECRET"
if ! _get_token; then
_err "Unable to get token"
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
export _H1="Content-Type: application/json-patch+json"
export _H2="Accept: application/json"
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
data="{\"name\": \"$fulldomain\",\"type\": \"TXT\",\"ttl\": 60,\"priority\": 0,\"data\": \"$txtvalue\"}"
response="$(_post "$data" "$CURANET_REST_URL/${_domain}/Records" "" "")"
if _contains "$response" "$txtvalue"; then
_debug "TXT record added OK"
else
_err "Unable to add TXT record"
return 1
fi
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_curanet_rm() {
fulldomain=$1
txtvalue=$2
_info "Using curanet"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
if ! _get_token; then
_err "Unable to get token"
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug "Getting current record list to identify TXT to delete"
export _H1="Content-Type: application/json"
export _H2="Accept: application/json"
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
response="$(_get "$CURANET_REST_URL/${_domain}/Records" "" "")"
if ! _contains "$response" "$txtvalue"; then
_err "Unable to delete record (does not contain $txtvalue )"
return 1
fi
recordid=$(echo "$response" | _egrep_o "{\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" | _egrep_o "id\":[0-9]+" | cut -c 5-)
if [ -z "$recordid" ]; then
_err "Unable to get recordid"
_debug "regex {\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue"
_debug "response $response"
return 1
fi
_debug "Deleting recordID $recordid"
response="$(_post "" "$CURANET_REST_URL/${_domain}/Records/$recordid" "" "DELETE")"
return 0
}
#################### Private functions below ##################################
_get_token() {
response="$(_post "grant_type=client_credentials&client_id=$CURANET_AUTHCLIENTID&client_secret=$CURANET_AUTHSECRET&scope=dns" "$CURANET_AUTH_URL" "" "")"
if ! _contains "$response" "access_token"; then
_err "Unable get access token"
return 1
fi
CURANET_ACCESS_TOKEN=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]+" | cut -c 17-)
if [ -z "$CURANET_ACCESS_TOKEN" ]; then
_err "Unable to get token"
return 1
fi
return 0
}
#_acme-challenge.www.domain.com
#returns
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
export _H1="Content-Type: application/json"
export _H2="Accept: application/json"
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")"
if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then
_domain=$h
return 0
fi
i=$(_math "$i" + 1)
done
return 1
}

View File

@@ -1,185 +0,0 @@
#!/usr/bin/env sh
# CloudXNS Domain api
#
#CX_Key="1234"
#
#CX_Secret="sADDsdasdgdsf"
CX_Api="https://www.cloudxns.net/api2"
#REST_API
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_cx_add() {
fulldomain=$1
txtvalue=$2
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
CX_Key=""
CX_Secret=""
_err "You don't specify cloudxns.net api key or secret yet."
_err "Please create you key and try again."
return 1
fi
REST_API="$CX_Api"
#save the api key and email to the account conf file.
_saveaccountconf_mutable CX_Key "$CX_Key"
_saveaccountconf_mutable CX_Secret "$CX_Secret"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
add_record "$_domain" "$_sub_domain" "$txtvalue"
}
#fulldomain txtvalue
dns_cx_rm() {
fulldomain=$1
txtvalue=$2
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
REST_API="$CX_Api"
if _get_root "$fulldomain"; then
record_id=""
existing_records "$_domain" "$_sub_domain" "$txtvalue"
if [ "$record_id" ]; then
_rest DELETE "record/$record_id/$_domain_id" "{}"
_info "Deleted record ${fulldomain}"
fi
fi
}
#usage: root sub
#return if the sub record already exists.
#echos the existing records count.
# '0' means doesn't exist
existing_records() {
_debug "Getting txt records"
root=$1
sub=$2
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
return 1
fi
seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
_debug seg "$seg"
if [ -z "$seg" ]; then
return 0
fi
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
_debug record_id "$record_id"
return 0
fi
}
#add the txt record.
#usage: root sub txtvalue
add_record() {
root=$1
sub=$2
txtvalue=$3
fulldomain="$sub.$root"
_info "Adding record"
if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=2
p=1
if ! _rest GET "domain"; then
return 1
fi
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
seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
_debug seg "$seg"
_domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _sub_domain "$_sub_domain"
_domain="$h"
_debug _domain "$_domain"
return 0
fi
return 1
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
#Usage: method URI data
_rest() {
m=$1
ep="$2"
_debug ep "$ep"
url="$REST_API/$ep"
_debug url "$url"
cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
_debug cdate "$cdate"
data="$3"
_debug data "$data"
sec="$CX_Key$url$data$cdate$CX_Secret"
_debug sec "$sec"
hmac=$(printf "%s" "$sec" | _digest md5 hex)
_debug hmac "$hmac"
export _H1="API-KEY: $CX_Key"
export _H2="API-REQUEST-DATE: $cdate"
export _H3="API-HMAC: $hmac"
export _H4="Content-Type: application/json"
if [ "$data" ]; then
response="$(_post "$data" "$url" "" "$m")"
else
response="$(_get "$url")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
_contains "$response" '"code":1'
}

View File

@@ -44,7 +44,7 @@ dns_cyon_rm() {
_cyon_load_credentials() {
# Convert loaded password to/from base64 as needed.
if [ "${CY_Password_B64}" ]; then
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64)"
elif [ "${CY_Password}" ]; then
CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
fi

View File

@@ -12,7 +12,7 @@
# --
#
DDNSS_DNS_API="https://ip4.ddnss.de/upd.php"
DDNSS_DNS_API="https://ddnss.de/upd.php"
######## Public functions #####################
@@ -77,7 +77,7 @@ dns_ddnss_rm() {
# Now remove the TXT record from DDNS DNS
_info "Trying to remove TXT record"
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=1&txt=."; then
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=2"; then
if [ "$response" = "Updated 1 hostname." ]; then
_info "TXT record has been successfully removed from your DDNSS domain."
return 0

View File

@@ -20,21 +20,17 @@ dns_desec_add() {
_debug txtvalue "$txtvalue"
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
if [ -z "$DEDYN_TOKEN" ]; then
DEDYN_TOKEN=""
DEDYN_NAME=""
_err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "You did not specify DEDYN_TOKEN yet."
_err "Please create your key and try again."
_err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io"
return 1
fi
#save the api token and name to the account conf file.
#save the api token to the account conf file.
_saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN"
_saveaccountconf_mutable DEDYN_NAME "$DEDYN_NAME"
_debug "First detect the root zone"
if ! _get_root "$fulldomain" "$REST_API/"; then
@@ -47,7 +43,7 @@ dns_desec_add() {
# Get existing TXT record
_debug "Getting txt records"
txtvalues="\"\\\"$txtvalue\\\"\""
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
if [ "$_code" = "200" ]; then
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
@@ -61,9 +57,9 @@ dns_desec_add() {
fi
_debug txtvalues "$txtvalues"
_info "Adding record"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
if _desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"; then
if _desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
@@ -87,16 +83,13 @@ dns_desec_rm() {
_debug txtvalue "$txtvalue"
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
if [ -z "$DEDYN_TOKEN" ]; then
DEDYN_TOKEN=""
DEDYN_NAME=""
_err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "You did not specify DEDYN_TOKEN yet."
_err "Please create your key and try again."
_err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io"
return 1
fi
@@ -112,7 +105,7 @@ dns_desec_rm() {
# Get existing TXT record
_debug "Getting txt records"
txtvalues=""
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
if [ "$_code" = "200" ]; then
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
@@ -130,8 +123,8 @@ dns_desec_rm() {
_debug txtvalues "$txtvalues"
_info "Deleting record"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
_desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
_desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"
if [ "$_code" = "200" ]; then
_info "Deleted, OK"
return 0

View File

@@ -192,6 +192,7 @@ _get_base_domain() {
## get URL for the list of domains
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
DOMURL="https://api.digitalocean.com/v2/domains"
found=""
## while we dont have a matching domain we keep going
while [ -z "$found" ]; do
@@ -205,9 +206,7 @@ _get_base_domain() {
fi
_debug2 domain_list "$domain_list"
## for each shortening of our $fulldomain, check if it exists in the $domain_list
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
i=2
i=1
while [ $i -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")

87
dnsapi/dns_dnshome.sh Executable file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/env sh
# dnsHome.de API for acme.sh
#
# This Script adds the necessary TXT record to a Subdomain
#
# Author dnsHome.de (https://github.com/dnsHome-de)
#
# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819
#
# export DNSHOME_Subdomain=""
# export DNSHOME_SubdomainPassword=""
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_dnshome_add() {
txtvalue=$2
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
DNSHOME_Subdomain=""
DNSHOME_SubdomainPassword=""
_err "Please specify/export your dnsHome.de Subdomain and Password"
return 1
fi
#save the credentials to the account conf file.
_savedomainconf DNSHOME_Subdomain "$DNSHOME_Subdomain"
_savedomainconf DNSHOME_SubdomainPassword "$DNSHOME_SubdomainPassword"
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
_DNSHOME_rest POST "acme=add&txt=$txtvalue"
if ! echo "$response" | grep 'successfully' >/dev/null; then
_err "Error"
_err "$response"
return 1
fi
return 0
}
# Usage: txtvalue
# Used to remove the txt record after validation
dns_dnshome_rm() {
txtvalue=$2
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
DNSHOME_Subdomain=""
DNSHOME_SubdomainPassword=""
_err "Please specify/export your dnsHome.de Subdomain and Password"
return 1
fi
_DNSHOME_rest POST "acme=rm&txt=$txtvalue"
if ! echo "$response" | grep 'successfully' >/dev/null; then
_err "Error"
_err "$response"
return 1
fi
return 0
}
#################### Private functions below ##################################
_DNSHOME_rest() {
method=$1
data="$2"
_debug "$data"
_debug data "$data"
response="$(_post "$data" "$DNSHOME_Api" "" "$method")"
if [ "$?" != "0" ]; then
_err "error $data"
return 1
fi
_debug2 response "$response"
return 0
}

248
dnsapi/dns_dnsservices.sh Executable file
View File

@@ -0,0 +1,248 @@
#!/usr/bin/env sh
#This file name is "dns_dnsservices.sh"
#Script for Danish DNS registra and DNS hosting provider https://dns.services
#Author: Bjarke Bruun <bbruun@gmail.com>
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
# Global variable to connect to the DNS.Services API
DNSServices_API=https://dns.services/api
######## Public functions #####################
#Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dnsservices_add() {
fulldomain="$1"
txtvalue="$2"
_info "Using dns.services to create ACME DNS challenge"
_debug2 add_fulldomain "$fulldomain"
_debug2 add_txtvalue "$txtvalue"
# Read username/password from environment or .acme.sh/accounts.conf
DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
DnsServices_Username=""
DnsServices_Password=""
_err "You didn't specify dns.services api username and password yet."
_err "Set environment variables DnsServices_Username and DnsServices_Password"
return 1
fi
# Setup GET/POST/DELETE headers
_setup_headers
#save the credentials to the account conf file.
_saveaccountconf_mutable DnsServices_Username "$DnsServices_Username"
_saveaccountconf_mutable DnsServices_Password "$DnsServices_Password"
if ! _contains "$DnsServices_Username" "@"; then
_err "It seems that the username variable DnsServices_Username has not been set/left blank"
_err "or is not a valid email. Please correct and try again."
return 1
fi
if ! _get_root "${fulldomain}"; then
_err "Invalid domain ${fulldomain}"
return 1
fi
if ! createRecord "$fulldomain" "${txtvalue}"; then
_err "Error creating TXT record in domain $fulldomain in $rootZoneName"
return 1
fi
_debug2 challenge-created "Created $fulldomain"
return 0
}
#Usage: fulldomain txtvalue
#Description: Remove the txt record after validation.
dns_dnsservices_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue"
_debug rm_fulldomain "$fulldomain"
_debug rm_txtvalue "$txtvalue"
# Read username/password from environment or .acme.sh/accounts.conf
DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
DnsServices_Username=""
DnsServices_Password=""
_err "You didn't specify dns.services api username and password yet."
_err "Set environment variables DnsServices_Username and DnsServices_Password"
return 1
fi
# Setup GET/POST/DELETE headers
_setup_headers
if ! _get_root "${fulldomain}"; then
_err "Invalid domain ${fulldomain}"
return 1
fi
_debug2 rm_rootDomainInfo "found root domain $rootZoneName for $fulldomain"
if ! deleteRecord "${fulldomain}" "${txtvalue}"; then
_err "Error removing record: $fulldomain TXT ${txtvalue}"
return 1
fi
return 0
}
#################### Private functions below ##################################
_setup_headers() {
# Set up API Headers for _get() and _post()
# The <function>_add or <function>_rm must have been called before to work
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
_err "Could not setup BASIC authentication headers, they are missing"
return 1
fi
DnsServiceCredentials="$(printf "%s" "$DnsServices_Username:$DnsServices_Password" | _base64)"
export _H1="Authorization: Basic $DnsServiceCredentials"
export _H2="Content-Type: application/json"
# Just return if headers are set
return 0
}
_get_root() {
domain="$1"
_debug2 _get_root "Get the root domain of ${domain} for DNS API"
# Setup _get() and _post() headers
#_setup_headers
result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns")
result2="$(printf "%s\n" "$result" | tr '[' '\n' | grep '"name"')"
result3="$(printf "%s\n" "$result2" | tr '}' '\n' | grep '"name"' | sed "s,^\,,,g" | sed "s,$,},g")"
useResult=""
_debug2 _get_root "Got the following root domain(s) $result"
_debug2 _get_root "- JSON: $result"
if [ "$(printf "%s\n" "$result" | tr '}' '\n' | grep -c '"name"')" -gt "1" ]; then
checkMultiZones="true"
_debug2 _get_root "- multiple zones found"
else
checkMultiZones="false"
_debug2 _get_root "- single zone found"
fi
# Find/isolate the root zone to work with in createRecord() and deleteRecord()
rootZone=""
if [ "$checkMultiZones" = "true" ]; then
#rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
rootZone=$(for x in $(printf "%s\n" "${result3}" | tr ',' '\n' | grep name | cut -d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
if [ "$rootZone" != "" ]; then
_debug2 _rootZone "- root zone for $domain is $rootZone"
else
_err "Could not find root zone for $domain, is it correctly typed?"
return 1
fi
else
rootZone=$(echo "$result" | tr '}' '\n' | _egrep_o '"name":"[^"]*' | cut -d'"' -f4)
_debug2 _get_root "- only found 1 domain in API: $rootZone"
fi
if [ -z "$rootZone" ]; then
_err "Could not find root domain for $domain - is it correctly typed?"
return 1
fi
# Make sure we use the correct API zone data
useResult="$(printf "%s\n" "${result3}" tr ',' '\n' | grep "$rootZone")"
_debug2 _useResult "useResult=$useResult"
# Setup variables used by other functions to communicate with DNS.Services API
#zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g')
zoneInfo=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep '"name"' | cut -d'"' -f4)
rootZoneName="$rootZone"
subDomainName="$(printf "%s\n" "$domain" | sed "s,\.$rootZone,,g")"
subDomainNameClean="$(printf "%s\n" "$domain" | sed "s,_acme-challenge.,,g")"
rootZoneDomainID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep domain_id | cut -d'"' -f4)
rootZoneServiceID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep service_id | cut -d'"' -f4)
_debug2 _zoneInfo "Zone info from API : $zoneInfo"
_debug2 _get_root "Root zone name : $rootZoneName"
_debug2 _get_root "Root zone domain ID : $rootZoneDomainID"
_debug2 _get_root "Root zone service ID: $rootZoneServiceID"
_debug2 _get_root "Sub domain : $subDomainName"
_debug _get_root "Found valid root domain $rootZone for $subDomainNameClean"
return 0
}
createRecord() {
fulldomain="$1"
txtvalue="$2"
# Get root domain information - needed for DNS.Services API communication
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
_get_root "$fulldomain"
fi
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
_err "Something happend - could not get the API zone information"
return 1
fi
_debug2 createRecord "CNAME TXT value is: $txtvalue"
# Prepare data to send to API
data="{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\", \"ttl\":\"10\"}"
_debug2 createRecord "data to API: $data"
result=$(_post "$data" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records" "" "POST")
_debug2 createRecord "result from API: $result"
if [ "$(echo "$result" | _egrep_o "\"success\":true")" = "" ]; then
_err "Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName"
_err "$result"
return 1
fi
_info "Record \"$fulldomain TXT $txtvalue\" has been created"
return 0
}
deleteRecord() {
fulldomain="$1"
txtvalue="$2"
_log deleteRecord "Deleting $fulldomain TXT $txtvalue record"
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
_get_root "$fulldomain"
fi
result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")"
#recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")"
#recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')"
recordID="$(printf "%s\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id"' | cut -d'"' -f4)"
_debug2 _recordID "recordID used for deletion of record: $recordID"
if [ -z "$recordID" ]; then
_info "Record $fulldomain TXT $txtvalue not found or already deleted"
return 0
else
_debug2 deleteRecord "Found recordID=$recordID"
fi
_debug2 deleteRecord "DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
_log "curl DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
result="$(_H1="$_H1" _H2="$_H2" _post "" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" "" "DELETE")"
_debug2 deleteRecord "API Delete result \"$result\""
_log "curl API Delete result \"$result\""
# Return OK regardless
return 0
}

View File

@@ -89,7 +89,7 @@ add_record() {
_info "Adding record"
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=%E9%BB%98%E8%AE%A4"; then
return 1
fi

View File

@@ -53,7 +53,7 @@ dns_dpi_rm() {
return 1
fi
if ! _rest POST "Record.List" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
if ! _rest POST "Record.List" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
_err "Record.Lis error."
return 1
fi
@@ -63,19 +63,19 @@ dns_dpi_rm() {
return 0
fi
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \")
record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id."
return 1
fi
if ! _rest POST "Record.Remove" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
if ! _rest POST "Record.Remove" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
_err "Record.Remove error."
return 1
fi
_contains "$response" "Action completed successful"
_contains "$response" "Operation successful"
}
@@ -89,11 +89,11 @@ add_record() {
_info "Adding record"
if ! _rest POST "Record.Create" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
if ! _rest POST "Record.Create" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
return 1
fi
_contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
_contains "$response" "Operation successful" || _contains "$response" "Domain record already exists"
}
#################### Private functions below ##################################
@@ -113,11 +113,11 @@ _get_root() {
return 1
fi
if ! _rest POST "Domain.Info" "user_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
if ! _rest POST "Domain.Info" "login_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
return 1
fi
if _contains "$response" "Action completed successful"; then
if _contains "$response" "Operation successful"; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then

View File

@@ -12,7 +12,7 @@
DuckDNS_API="https://www.duckdns.org/update"
######## Public functions #####################
######## Public functions ######################
#Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_duckdns_add() {
@@ -96,7 +96,7 @@ dns_duckdns_rm() {
_duckdns_get_domain() {
# We'll extract the domain/username from full domain
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '^(_acme-challenge\.)?[a-z0-9-]*\.duckdns\.org' | sed 's/^\(_acme-challenge\.\)\?\([a-z0-9-]*\)\.duckdns\.org/\2/')"
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '^(_acme-challenge\.)?([a-z0-9-]+\.)+duckdns\.org' | sed -n 's/^\([^.]\{1,\}\.\)*\([a-z0-9-]\{1,\}\)\.duckdns\.org$/\2/p;')"
if [ -z "$_duckdns_domain" ]; then
_err "Error extracting the domain."
@@ -112,7 +112,7 @@ _duckdns_rest() {
param="$2"
_debug param "$param"
url="$DuckDNS_API?$param"
if [ "$DEBUG" -gt 0 ]; then
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ]; then
url="$url&verbose=true"
fi
_debug url "$url"
@@ -121,7 +121,7 @@ _duckdns_rest() {
if [ "$method" = "GET" ]; then
response="$(_get "$url")"
_debug2 response "$response"
if [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then
response="OK"
fi
else

View File

@@ -94,8 +94,8 @@ _get_domain() {
_your_hosts="$(echo "$_your_hosts" | awk '/\./ {print $1}')"
for l in $_your_hosts; do
#echo "host: $l"
if test "${_full_domain#*$l}" != "$_full_domain"; then
_record="${_full_domain%.$l}"
if test "${_full_domain#*"$l"}" != "$_full_domain"; then
_record=${_full_domain%."$l"}
_host=$l
_debug "The host is $_host and the record $_record"
return 0
@@ -143,7 +143,7 @@ _dns_dynv6_add_http() {
return 1
fi
_get_zone_name "$_zone_id"
record="${fulldomain%%.$_zone_name}"
record=${fulldomain%%."$_zone_name"}
_set_record TXT "$record" "$txtvalue"
if _contains "$response" "$txtvalue"; then
_info "Successfully added record"
@@ -161,7 +161,7 @@ _dns_dynv6_rm_http() {
return 1
fi
_get_zone_name "$_zone_id"
record="${fulldomain%%.$_zone_name}"
record=${fulldomain%%."$_zone_name"}
_get_record_id "$_zone_id" "$record" "$txtvalue"
_del_record "$_zone_id" "$_record_id"
if [ -z "$response" ]; then

470
dnsapi/dns_edgedns.sh Executable file
View File

@@ -0,0 +1,470 @@
#!/usr/bin/env sh
# Akamai Edge DNS v2 API
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
# Edge DNS Zones and Recordsets, e.g. DNS—Zone Record Management authorization
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
# Values to export:
# --EITHER--
# *** TBD. NOT IMPLEMENTED YET ***
# specify Edgegrid credentials file and section
# AKAMAI_EDGERC=<full file path>
# AKAMAI_EDGERC_SECTION="default"
## --OR--
# specify indiviual credentials
# export AKAMAI_HOST = <host>
# export AKAMAI_ACCESS_TOKEN = <access token>
# export AKAMAI_CLIENT_TOKEN = <client token>
# export AKAMAI_CLIENT_SECRET = <client secret>
ACME_EDGEDNS_VERSION="0.1.0"
######## Public functions #####################
# Usage: dns_edgedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
#
dns_edgedns_add() {
fulldomain=$1
txtvalue=$2
_debug "ENTERING DNS_EDGEDNS_ADD"
_debug2 "fulldomain" "$fulldomain"
_debug2 "txtvalue" "$txtvalue"
if ! _EDGEDNS_credentials; then
_err "$@"
return 1
fi
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug2 "Add: zone" "$zone"
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "$edge_endpoint" "$zone" "$fulldomain")
_debug3 "Add URL" "$acmeRecordURI"
# Get existing TXT record
_edge_result=$(_edgedns_rest GET "$acmeRecordURI")
_api_status="$?"
_debug3 "_edge_result" "$_edge_result"
if [ "$_api_status" -ne 0 ]; then
if [ "$curResult" = "FATAL" ]; then
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
fi
if [ "$_edge_result" != "404" ]; then
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
return 1
fi
fi
rdata="\"${txtvalue}\""
record_op="POST"
if [ "$_api_status" -eq 0 ]; then
# record already exists. Get existing record data and update
record_op="PUT"
rdlist="${_edge_result#*\"rdata\":[}"
rdlist="${rdlist%%]*}"
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
_debug3 "existing TXT found"
_debug3 "record data" "$rdlist"
# value already there?
if _contains "$rdlist" "$txtvalue"; then
return 0
fi
_txt_val=""
while [ "$_txt_val" != "$rdlist" ] && [ "${rdlist}" ]; do
_txt_val="${rdlist%%,*}"
rdlist="${rdlist#*,}"
rdata="${rdata},\"${_txt_val}\""
done
fi
# Add the txtvalue TXT Record
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
_debug3 "Add body '${body}'"
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
_api_status="$?"
if [ "$_api_status" -eq 0 ]; then
_log "$(printf "Text value %s added to recordset %s" "$txtvalue" "$fulldomain")"
return 0
else
_err "$(printf "error adding TXT record for validation. Error: %s" "$_edge_result")"
return 1
fi
}
# Usage: dns_edgedns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to delete txt record
#
dns_edgedns_rm() {
fulldomain=$1
txtvalue=$2
_debug "ENTERING DNS_EDGEDNS_RM"
_debug2 "fulldomain" "$fulldomain"
_debug2 "txtvalue" "$txtvalue"
if ! _EDGEDNS_credentials; then
_err "$@"
return 1
fi
if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
_err "Invalid domain"
return 1
fi
_debug2 "RM: zone" "${zone}"
acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "${edge_endpoint}" "$zone" "$fulldomain")
_debug3 "RM URL" "$acmeRecordURI"
# Get existing TXT record
_edge_result=$(_edgedns_rest GET "$acmeRecordURI")
_api_status="$?"
if [ "$_api_status" -ne 0 ]; then
if [ "$curResult" = "FATAL" ]; then
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
fi
if [ "$_edge_result" != "404" ]; then
_err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
return 1
fi
fi
_debug3 "_edge_result" "$_edge_result"
record_op="DELETE"
body=""
if [ "$_api_status" -eq 0 ]; then
# record already exists. Get existing record data and update
rdlist="${_edge_result#*\"rdata\":[}"
rdlist="${rdlist%%]*}"
rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
_debug3 "rdlist" "$rdlist"
if [ -n "$rdlist" ]; then
record_op="PUT"
comma=""
rdata=""
_txt_val=""
while [ "$_txt_val" != "$rdlist" ] && [ "$rdlist" ]; do
_txt_val="${rdlist%%,*}"
rdlist="${rdlist#*,}"
_debug3 "_txt_val" "$_txt_val"
_debug3 "txtvalue" "$txtvalue"
if ! _contains "$_txt_val" "$txtvalue"; then
rdata="${rdata}${comma}\"${_txt_val}\""
comma=","
fi
done
if [ -z "$rdata" ]; then
record_op="DELETE"
else
# Recreate the txtvalue TXT Record
body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
_debug3 "body" "$body"
fi
fi
fi
_edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
_api_status="$?"
if [ "$_api_status" -eq 0 ]; then
_log "$(printf "Text value %s removed from recordset %s" "$txtvalue" "$fulldomain")"
return 0
else
_err "$(printf "error removing TXT record for validation. Error: %s" "$_edge_result")"
return 1
fi
}
#################### Private functions below ##################################
_EDGEDNS_credentials() {
_debug "GettingEdge DNS credentials"
_log "$(printf "ACME DNSAPI Edge DNS version %s" ${ACME_EDGEDNS_VERSION})"
args_missing=0
AKAMAI_ACCESS_TOKEN="${AKAMAI_ACCESS_TOKEN:-$(_readaccountconf_mutable AKAMAI_ACCESS_TOKEN)}"
if [ -z "$AKAMAI_ACCESS_TOKEN" ]; then
AKAMAI_ACCESS_TOKEN=""
AKAMAI_CLIENT_TOKEN=""
AKAMAI_HOST=""
AKAMAI_CLIENT_SECRET=""
_err "AKAMAI_ACCESS_TOKEN is missing"
args_missing=1
fi
AKAMAI_CLIENT_TOKEN="${AKAMAI_CLIENT_TOKEN:-$(_readaccountconf_mutable AKAMAI_CLIENT_TOKEN)}"
if [ -z "$AKAMAI_CLIENT_TOKEN" ]; then
AKAMAI_ACCESS_TOKEN=""
AKAMAI_CLIENT_TOKEN=""
AKAMAI_HOST=""
AKAMAI_CLIENT_SECRET=""
_err "AKAMAI_CLIENT_TOKEN is missing"
args_missing=1
fi
AKAMAI_HOST="${AKAMAI_HOST:-$(_readaccountconf_mutable AKAMAI_HOST)}"
if [ -z "$AKAMAI_HOST" ]; then
AKAMAI_ACCESS_TOKEN=""
AKAMAI_CLIENT_TOKEN=""
AKAMAI_HOST=""
AKAMAI_CLIENT_SECRET=""
_err "AKAMAI_HOST is missing"
args_missing=1
fi
AKAMAI_CLIENT_SECRET="${AKAMAI_CLIENT_SECRET:-$(_readaccountconf_mutable AKAMAI_CLIENT_SECRET)}"
if [ -z "$AKAMAI_CLIENT_SECRET" ]; then
AKAMAI_ACCESS_TOKEN=""
AKAMAI_CLIENT_TOKEN=""
AKAMAI_HOST=""
AKAMAI_CLIENT_SECRET=""
_err "AKAMAI_CLIENT_SECRET is missing"
args_missing=1
fi
if [ "$args_missing" = 1 ]; then
_err "You have not properly specified the EdgeDNS Open Edgegrid API credentials. Please try again."
return 1
else
_saveaccountconf_mutable AKAMAI_ACCESS_TOKEN "$AKAMAI_ACCESS_TOKEN"
_saveaccountconf_mutable AKAMAI_CLIENT_TOKEN "$AKAMAI_CLIENT_TOKEN"
_saveaccountconf_mutable AKAMAI_HOST "$AKAMAI_HOST"
_saveaccountconf_mutable AKAMAI_CLIENT_SECRET "$AKAMAI_CLIENT_SECRET"
# Set whether curl should use secure or insecure mode
fi
export HTTPS_INSECURE=0 # All Edgegrid API calls are secure
edge_endpoint=$(printf "https://%s/config-dns/v2/zones" "$AKAMAI_HOST")
_debug3 "Edge API Endpoint:" "$edge_endpoint"
}
_EDGEDNS_getZoneInfo() {
_debug "Getting Zoneinfo"
zoneEnd=false
curZone=$1
while [ -n "$zoneEnd" ]; do
# we can strip the first part of the fulldomain, since its just the _acme-challenge string
curZone="${curZone#*.}"
# suffix . needed for zone -> domain.tld.
# create zone get url
get_zone_url=$(printf "%s/%s" "$edge_endpoint" "$curZone")
_debug3 "Zone Get: " "${get_zone_url}"
curResult=$(_edgedns_rest GET "$get_zone_url")
retVal=$?
if [ "$retVal" -ne 0 ]; then
if [ "$curResult" = "FATAL" ]; then
_err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
fi
if [ "$curResult" != "404" ]; then
_err "$(printf "Managed zone validation failed. Error response: %s" "$retVal")"
return 1
fi
fi
if _contains "$curResult" "\"zone\":"; then
_debug2 "Zone data" "${curResult}"
zone=$(echo "${curResult}" | _egrep_o "\"zone\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
_debug3 "Zone" "${zone}"
zoneEnd=""
return 0
fi
if [ "${curZone#*.}" != "$curZone" ]; then
_debug3 "$(printf "%s still contains a '.' - so we can check next higher level" "$curZone")"
else
zoneEnd=true
_err "Couldn't retrieve zone data."
return 1
fi
done
_err "Failed to retrieve zone data."
return 2
}
_edgedns_headers=""
_edgedns_rest() {
_debug "Handling API Request"
m=$1
# Assume endpoint is complete path, including query args if applicable
ep=$2
body_data=$3
_edgedns_content_type=""
_request_url_path="$ep"
_request_body="$body_data"
_request_method="$m"
_edgedns_headers=""
tab=""
_edgedns_headers="${_edgedns_headers}${tab}Host: ${AKAMAI_HOST}"
tab="\t"
# Set in acme.sh _post/_get
#_edgedns_headers="${_edgedns_headers}${tab}User-Agent:ACME DNSAPI Edge DNS version ${ACME_EDGEDNS_VERSION}"
_edgedns_headers="${_edgedns_headers}${tab}Accept: application/json,*/*"
if [ "$m" != "GET" ] && [ "$m" != "DELETE" ]; then
_edgedns_content_type="application/json"
_debug3 "_request_body" "$_request_body"
_body_len=$(echo "$_request_body" | tr -d "\n\r" | awk '{print length}')
_edgedns_headers="${_edgedns_headers}${tab}Content-Length: ${_body_len}"
fi
_edgedns_make_auth_header
_edgedns_headers="${_edgedns_headers}${tab}Authorization: ${_signed_auth_header}"
_secure_debug2 "Made Auth Header" "$_signed_auth_header"
hdr_indx=1
work_header="${_edgedns_headers}${tab}"
_debug3 "work_header" "$work_header"
while [ "$work_header" ]; do
entry="${work_header%%\\t*}"
work_header="${work_header#*\\t}"
export "$(printf "_H%s=%s" "$hdr_indx" "$entry")"
_debug2 "Request Header " "$entry"
hdr_indx=$((hdr_indx + 1))
done
# clear headers from previous request to avoid getting wrong http code on timeouts
: >"$HTTP_HEADER"
_debug2 "$ep"
if [ "$m" != "GET" ]; then
_debug3 "Method data" "$data"
# body url [needbase64] [POST|PUT|DELETE] [ContentType]
response=$(_post "$_request_body" "$ep" false "$m" "$_edgedns_content_type")
else
response=$(_get "$ep")
fi
_ret="$?"
if [ "$_ret" -ne 0 ]; then
_err "$(printf "acme.sh API function call failed. Error: %s" "$_ret")"
echo "FATAL"
return "$_ret"
fi
_debug2 "response" "${response}"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug2 "http response code" "$_code"
if [ "$_code" = "200" ] || [ "$_code" = "201" ]; then
# All good
response="$(echo "${response}" | _normalizeJson)"
echo "$response"
return 0
fi
if [ "$_code" = "204" ]; then
# Success, no body
echo "$_code"
return 0
fi
if [ "$_code" = "400" ]; then
_err "Bad request presented"
_log "$(printf "Headers: %s" "$_edgedns_headers")"
_log "$(printf "Method: %s" "$_request_method")"
_log "$(printf "URL: %s" "$ep")"
_log "$(printf "Data: %s" "$data")"
fi
if [ "$_code" = "403" ]; then
_err "access denied make sure your Edgegrid cedentials are correct."
fi
echo "$_code"
return 1
}
_edgedns_eg_timestamp() {
_debug "Generating signature Timestamp"
_debug3 "Retriving ntp time"
_timeheaders="$(_get "https://www.ntp.org" "onlyheader")"
_debug3 "_timeheaders" "$_timeheaders"
_ntpdate="$(echo "$_timeheaders" | grep -i "Date:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
_debug3 "_ntpdate" "$_ntpdate"
_ntpdate="$(echo "${_ntpdate}" | sed -e 's/^[[:space:]]*//')"
_debug3 "_NTPDATE" "$_ntpdate"
_ntptime="$(echo "${_ntpdate}" | _head_n 1 | cut -d " " -f 5 | tr -d "\r\n")"
_debug3 "_ntptime" "$_ntptime"
_eg_timestamp=$(date -u "+%Y%m%dT")
_eg_timestamp="$(printf "%s%s+0000" "$_eg_timestamp" "$_ntptime")"
_debug "_eg_timestamp" "$_eg_timestamp"
}
_edgedns_new_nonce() {
_debug "Generating Nonce"
_nonce=$(echo "EDGEDNS$(_time)" | _digest sha1 hex | cut -c 1-32)
_debug3 "_nonce" "$_nonce"
}
_edgedns_make_auth_header() {
_debug "Constructing Auth Header"
_edgedns_new_nonce
_edgedns_eg_timestamp
# "Unsigned authorization header: 'EG1-HMAC-SHA256 client_token=block;access_token=block;timestamp=20200806T14:16:33+0000;nonce=72cde72c-82d9-4721-9854-2ba057929d67;'"
_auth_header="$(printf "EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;" "$AKAMAI_CLIENT_TOKEN" "$AKAMAI_ACCESS_TOKEN" "$_eg_timestamp" "$_nonce")"
_secure_debug2 "Unsigned Auth Header: " "$_auth_header"
_edgedns_sign_request
_signed_auth_header="$(printf "%ssignature=%s" "$_auth_header" "$_signed_req")"
_secure_debug2 "Signed Auth Header: " "${_signed_auth_header}"
}
_edgedns_sign_request() {
_debug2 "Signing http request"
_edgedns_make_data_to_sign "$_auth_header"
_secure_debug2 "Returned signed data" "$_mdata"
_edgedns_make_signing_key "$_eg_timestamp"
_edgedns_base64_hmac_sha256 "$_mdata" "$_signing_key"
_signed_req="$_hmac_out"
_secure_debug2 "Signed Request" "$_signed_req"
}
_edgedns_make_signing_key() {
_debug2 "Creating sigining key"
ts=$1
_edgedns_base64_hmac_sha256 "$ts" "$AKAMAI_CLIENT_SECRET"
_signing_key="$_hmac_out"
_secure_debug2 "Signing Key" "$_signing_key"
}
_edgedns_make_data_to_sign() {
_debug2 "Processing data to sign"
hdr=$1
_secure_debug2 "hdr" "$hdr"
_edgedns_make_content_hash
path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')"
path=${path#*"$AKAMAI_HOST"}
_debug "hier path" "$path"
# dont expose headers to sign so use MT string
_mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")"
_secure_debug2 "Data to Sign" "$_mdata"
}
_edgedns_make_content_hash() {
_debug2 "Generating content hash"
_hash=""
_debug2 "Request method" "${_request_method}"
if [ "$_request_method" != "POST" ] || [ -z "$_request_body" ]; then
return 0
fi
_debug2 "Req body" "$_request_body"
_edgedns_base64_sha256 "$_request_body"
_hash="$_sha256_out"
_debug2 "Content hash" "$_hash"
}
_edgedns_base64_hmac_sha256() {
_debug2 "Generating hmac"
data=$1
key=$2
encoded_data="$(echo "$data" | iconv -t utf-8)"
encoded_key="$(echo "$key" | iconv -t utf-8)"
_secure_debug2 "encoded data" "$encoded_data"
_secure_debug2 "encoded key" "$encoded_key"
encoded_key_hex=$(printf "%s" "$encoded_key" | _hex_dump | tr -d ' ')
data_sig="$(echo "$encoded_data" | tr -d "\n\r" | _hmac sha256 "$encoded_key_hex" | _base64)"
_secure_debug2 "data_sig:" "$data_sig"
_hmac_out="$(echo "$data_sig" | tr -d "\n\r" | iconv -f utf-8)"
_secure_debug2 "hmac" "$_hmac_out"
}
_edgedns_base64_sha256() {
_debug2 "Creating sha256 digest"
trg=$1
_secure_debug2 "digest data" "$trg"
digest="$(echo "$trg" | tr -d "\n\r" | _digest "sha256")"
_sha256_out="$(echo "$digest" | tr -d "\n\r" | iconv -f utf-8)"
_secure_debug2 "digest decode" "$_sha256_out"
}
#_edgedns_parse_edgerc() {
# filepath=$1
# section=$2
#}

146
dnsapi/dns_fornex.sh Normal file
View File

@@ -0,0 +1,146 @@
#!/usr/bin/env sh
#Author: Timur Umarov <inbox@tumarov.com>
FORNEX_API_URL="https://fornex.com/api/dns/v0.1"
######## Public functions #####################
#Usage: dns_fornex_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_fornex_add() {
fulldomain=$1
txtvalue=$2
if ! _Fornex_API; then
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Unable to determine root domain"
return 1
else
_debug _domain "$_domain"
fi
_info "Adding record"
if _rest POST "$_domain/entry_set/add/" "host=$fulldomain&type=TXT&value=$txtvalue&apikey=$FORNEX_API_KEY"; then
_debug _response "$response"
if _contains "$response" '"ok": true' || _contains "$response" 'Такая запись уже существует.'; then
_info "Added, OK"
return 0
fi
fi
_err "Add txt record error."
return 1
}
#Usage: dns_fornex_rm _acme-challenge.www.domain.com
dns_fornex_rm() {
fulldomain=$1
txtvalue=$2
if ! _Fornex_API; then
return 1
fi
if ! _get_root "$fulldomain"; then
_err "Unable to determine root domain"
return 1
else
_debug _domain "$_domain"
fi
_debug "Getting txt records"
_rest GET "$_domain/entry_set.json?apikey=$FORNEX_API_KEY"
if ! _contains "$response" "$txtvalue"; then
_err "Txt record not found"
return 1
fi
_record_id="$(echo "$response" | _egrep_o "{[^{]*\"value\"*:*\"$txtvalue\"[^}]*}" | sed -n -e 's#.*"id": \([0-9]*\).*#\1#p')"
_debug "_record_id" "$_record_id"
if [ -z "$_record_id" ]; then
_err "can not find _record_id"
return 1
fi
if ! _rest POST "$_domain/entry_set/$_record_id/delete/" "apikey=$FORNEX_API_KEY"; then
_err "Delete record error."
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _rest GET "domain_list.json?q=$h&apikey=$FORNEX_API_KEY"; then
return 1
fi
if _contains "$response" "\"$h\"" >/dev/null; then
_domain=$h
return 0
else
_debug "$h not found"
fi
i=$(_math "$i" + 1)
done
return 1
}
_Fornex_API() {
FORNEX_API_KEY="${FORNEX_API_KEY:-$(_readaccountconf_mutable FORNEX_API_KEY)}"
if [ -z "$FORNEX_API_KEY" ]; then
FORNEX_API_KEY=""
_err "You didn't specify the Fornex API key yet."
_err "Please create your key and try again."
return 1
fi
_saveaccountconf_mutable FORNEX_API_KEY "$FORNEX_API_KEY"
}
#method method action data
_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Accept: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$FORNEX_API_URL/$ep" "" "$m")"
else
response="$(_get "$FORNEX_API_URL/$ep" | _normalizeJson)"
fi
_ret="$?"
if [ "$_ret" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
# Gandi LiveDNS v5 API
# http://doc.livedns.gandi.net/
# https://doc.livedns.gandi.net/
# currently under beta
#
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable

View File

@@ -39,7 +39,7 @@ dns_gcloud_rm() {
_dns_gcloud_start_tr || return $?
_dns_gcloud_get_rrdatas || return $?
echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
echo "$rrdatas" | grep -F -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
_dns_gcloud_execute_tr || return $?
_info "$fulldomain record added"
@@ -98,7 +98,7 @@ _dns_gcloud_remove_rrs() {
--ttl="$ttl" \
--type=TXT \
--zone="$managedZone" \
--transaction-file="$tr"; then
--transaction-file="$tr" --; then
_debug tr "$(cat "$tr")"
rm -r "$trd"
_err "_dns_gcloud_remove_rrs: failed to remove RRs"
@@ -113,7 +113,7 @@ _dns_gcloud_add_rrs() {
--ttl="$ttl" \
--type=TXT \
--zone="$managedZone" \
--transaction-file="$tr"; then
--transaction-file="$tr" --; then
_debug tr "$(cat "$tr")"
rm -r "$trd"
_err "_dns_gcloud_add_rrs: failed to add RRs"
@@ -163,5 +163,8 @@ _dns_gcloud_get_rrdatas() {
return 1
fi
ttl=$(echo "$rrdatas" | cut -f1)
rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/","/"\n"/g')
# starting with version 353.0.0 gcloud seems to
# separate records with a semicolon instead of commas
# see also https://cloud.google.com/sdk/docs/release-notes#35300_2021-08-17
rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/"[,;]"/"\n"/g')
}

187
dnsapi/dns_gcore.sh Executable file
View File

@@ -0,0 +1,187 @@
#!/usr/bin/env sh
#
#GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
#
GCORE_Api="https://api.gcorelabs.com/dns/v2"
GCORE_Doc="https://apidocs.gcore.com/dns"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gcore_add() {
fulldomain=$1
txtvalue=$2
GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
if [ -z "$GCORE_Key" ]; then
GCORE_Key=""
_err "You didn't specify a Gcore api key yet."
_err "You can get yours from here $GCORE_Doc"
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable GCORE_Key "$GCORE_Key"
_debug "First detect the zone name"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _zone_name "$_zone_name"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
payload=""
if echo "$response" | grep "record is not found" >/dev/null; then
_info "Record doesn't exists"
payload="{\"resource_records\":[{\"content\":[\"$txtvalue\"],\"enabled\":true}],\"ttl\":120}"
elif echo "$response" | grep "$txtvalue" >/dev/null; then
_info "Already exists, OK"
return 0
elif echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
_info "Record with mismatch txtvalue, try update it"
payload=$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/"meta":{}}]}/"meta":{}},{"content":['\""$txtvalue"\"'],"enabled":true}]}/')
fi
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
# we can not use updating anymore.
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
# _debug count "$count"
# if [ "$count" = "0" ]; then
_info "Adding record"
if _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
elif _contains "$response" "rrset is already exists"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_gcore_rm() {
fulldomain=$1
txtvalue=$2
GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _zone_name "$_zone_name"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
if echo "$response" | grep "record is not found" >/dev/null; then
_info "No such txt recrod"
return 0
fi
if ! echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
_err "Error: $response"
return 1
fi
if ! echo "$response" | tr -d " " | grep \""$txtvalue"\" >/dev/null; then
_info "No such txt recrod"
return 0
fi
count="$(echo "$response" | grep -o "content" | wc -l)"
if [ "$count" = "1" ]; then
if ! _gcore_rest DELETE "zones/$_zone_name/$fulldomain/TXT"; then
_err "Delete record error. $response"
return 1
fi
return 0
fi
payload="$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/{"id":[0-9]\+,"content":\["'"$txtvalue"'"\],"enabled":true,"meta":{}}//' | sed 's/\[,/\[/' | sed 's/,,/,/' | sed 's/,\]/\]/')"
if ! _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
_err "Delete record error. $response"
fi
}
#################### Private functions below ##################################
#_acme-challenge.sub.domain.com
#returns
# _sub_domain=_acme-challenge.sub or _acme-challenge
# _domain=domain.com
# _zone_name=domain.com or sub.domain.com
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _gcore_rest GET "zones/$h"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\""; then
_zone_name=$h
if [ "$_zone_name" ]; 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
return 1
}
_gcore_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
key_trimmed=$(echo "$GCORE_Key" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Authorization: APIKey $key_trimmed"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$GCORE_Api/$ep" "" "$m")"
else
response="$(_get "$GCORE_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env sh
#Godaddy domain api
# Get API key and secret from https://developer.godaddy.com/
#
#GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
# GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
# GD_Secret="asdfsdfsfsdfsdfdfsdf"
#
#GD_Secret="asdfsdfsfsdfsdfdfsdf"
# Ex.: acme.sh --issue --staging --dns dns_gd -d "*.s.example.com" -d "s.example.com"
GD_Api="https://api.godaddy.com/v1"
@@ -20,8 +22,8 @@ dns_gd_add() {
if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
GD_Key=""
GD_Secret=""
_err "You don't specify godaddy api key and secret yet."
_err "Please create you key and try again."
_err "You didn't specify godaddy api key and secret yet."
_err "Please create your key and try again."
return 1
fi
@@ -44,14 +46,15 @@ dns_gd_add() {
fi
if _contains "$response" "$txtvalue"; then
_info "The record is existing, skip"
_info "This record already exists, skipping"
return 0
fi
_add_data="{\"data\":\"$txtvalue\"}"
for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
_debug2 t "$t"
if [ "$t" ]; then
# ignore empty (previously removed) records, to prevent useless _acme-challenge TXT entries
if [ "$t" ] && [ "$t" != '""' ]; then
_add_data="$_add_data,{\"data\":$t}"
fi
done
@@ -59,13 +62,25 @@ dns_gd_add() {
_info "Adding record"
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
_info "Added, sleeping 10 seconds"
_sleep 10
#todo: check if the record takes effect
return 0
_debug "Checking updated records of '${fulldomain}'"
if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
_err "Validating TXT record for '${fulldomain}' with rest error [$?]." "$response"
return 1
fi
if ! _contains "$response" "$txtvalue"; then
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
return 1
fi
else
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
return 1
fi
_err "Add txt record error."
return 1
_sleep 10
_info "Added TXT record '${txtvalue}' for '${fulldomain}'."
return 0
}
#fulldomain
@@ -107,11 +122,20 @@ dns_gd_rm() {
fi
done
if [ -z "$_add_data" ]; then
_add_data="{\"data\":\"\"}"
# delete empty record
_debug "Delete last record for '${fulldomain}'"
if ! _gd_rest DELETE "domains/$_domain/records/TXT/$_sub_domain"; then
_err "Cannot delete empty TXT record for '$fulldomain'"
return 1
fi
else
# remove specific TXT value, keeping other entries
_debug2 _add_data "$_add_data"
if ! _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
_err "Cannot update TXT record for '$fulldomain'"
return 1
fi
fi
_debug2 _add_data "$_add_data"
_gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"
}
#################### Private functions below ##################################
@@ -156,15 +180,15 @@ _gd_rest() {
export _H1="Authorization: sso-key $GD_Key:$GD_Secret"
export _H2="Content-Type: application/json"
if [ "$data" ]; then
_debug data "$data"
if [ "$data" ] || [ "$m" = "DELETE" ]; then
_debug "data ($m): " "$data"
response="$(_post "$data" "$GD_Api/$ep" "" "$m")"
else
response="$(_get "$GD_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
_err "error on rest call ($m): $ep"
return 1
fi
_debug2 response "$response"

View File

@@ -1,177 +0,0 @@
#!/usr/bin/env sh
#Author: Herman Sletteng
#Report Bugs here: https://github.com/loial/acme.sh
#
#
# Note, gratisdns requires a login first, so the script needs to handle
# temporary cookies. Since acme.sh _get/_post currently don't directly support
# cookies, I've defined wrapper functions _myget/_mypost to set the headers
GDNSDK_API="https://admin.gratisdns.com"
######## Public functions #####################
#Usage: dns_gdnsdk_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gdnsdk_add() {
fulldomain=$1
txtvalue=$2
_info "Using gratisdns.dk"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _gratisdns_login; then
_err "Login failed!"
return 1
fi
#finding domain zone
if ! _get_domain; then
_err "No matching root domain for $fulldomain found"
return 1
fi
# adding entry
_info "Adding the entry"
_mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1"
if _successful_update; then return 0; fi
_err "Couldn't create entry!"
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_gdnsdk_rm() {
fulldomain=$1
txtvalue=$2
_info "Using gratisdns.dk"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _gratisdns_login; then
_err "Login failed!"
return 1
fi
if ! _get_domain; then
_err "No matching root domain for $fulldomain found"
return 1
fi
_findentry "$fulldomain" "$txtvalue"
if [ -z "$_id" ]; then
_info "Entry doesn't exist, nothing to delete"
return 0
fi
_debug "Deleting record..."
_mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id"
# removing entry
if _successful_update; then return 0; fi
_err "Couldn't delete entry!"
return 1
}
#################### Private functions below ##################################
_checkcredentials() {
GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}"
GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}"
if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then
GDNSDK_Username=""
GDNSDK_Password=""
_err "You haven't specified gratisdns.dk username and password yet."
_err "Please add credentials and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username"
_saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password"
return 0
}
_checkcookie() {
GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}"
if [ -z "$GDNSDK_Cookie" ]; then
_debug "No cached cookie found"
return 1
fi
_myget "action="
if (echo "$_result" | grep -q "logmeout"); then
_debug "Cached cookie still valid"
return 0
fi
_debug "Cached cookie no longer valid"
GDNSDK_Cookie=""
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
return 1
}
_gratisdns_login() {
if ! _checkcredentials; then return 1; fi
if _checkcookie; then
_debug "Already logged in"
return 0
fi
_debug "Logging into GratisDNS with user $GDNSDK_Username"
if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then
_err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post"
return 1
fi
GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)"
if [ -z "$GDNSDK_Cookie" ]; then
_err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file"
return 1
fi
export GDNSDK_Cookie
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
return 0
}
_myget() {
#Adds cookie to request
export _H1="Cookie: $GDNSDK_Cookie"
_result=$(_get "$GDNSDK_API?$1")
}
_mypost() {
#Adds cookie to request
export _H1="Cookie: $GDNSDK_Cookie"
_result=$(_post "$1" "$GDNSDK_API")
}
_get_domain() {
_myget 'action=dns_primarydns'
_domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:]._-]+' | sed 's/^.*"//')
if [ -z "$_domains" ]; then
_err "Primary domain list not found!"
return 1
fi
for _domain in $_domains; do
if (_endswith "$fulldomain" "$_domain"); then
_debug "Root domain: $_domain"
return 0
fi
done
return 1
}
_successful_update() {
if (echo "$_result" | grep -q 'table-success'); then return 0; fi
return 1
}
_findentry() {
#args $1: fulldomain, $2: txtvalue
#returns id of dns entry, if it exists
_myget "action=dns_primary_changeDNSsetup&user_domain=$_domain"
_debug3 "_result: $_result"
_tmp_result=$(echo "$_result" | tr -d '\n\r' | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*")
_debug _tmp_result "$_tmp_result"
if [ -z "${_tmp_result:-}" ]; then
_debug "The variable is _tmp_result is not supposed to be empty, there may be something wrong with the script"
fi
_id=$(echo "$_tmp_result" | sed 's/^.*=//')
if [ -n "$_id" ]; then
_debug "Entry found with _id=$_id"
return 0
fi
return 1
}

232
dnsapi/dns_geoscaling.sh Executable file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/env sh
########################################################################
# Geoscaling hook script for acme.sh
#
# Environment variables:
#
# - $GEOSCALING_Username (your Geoscaling username - this is usually NOT an amail address)
# - $GEOSCALING_Password (your Geoscaling password)
#-- dns_geoscaling_add() - Add TXT record --------------------------------------
# Usage: dns_geoscaling_add _acme-challenge.subdomain.domain.com "XyZ123..."
dns_geoscaling_add() {
full_domain=$1
txt_value=$2
_info "Using DNS-01 Geoscaling DNS2 hook"
GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}"
GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}"
if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then
GEOSCALING_Username=
GEOSCALING_Password=
_err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables."
return 1
fi
_saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}"
_saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}"
# Fills in the $zone_id and $zone_name
find_zone "${full_domain}" || return 1
_debug "Zone id '${zone_id}' will be used."
# We're logged in here
# we should add ${full_domain} minus the trailing ${zone_name}
prefix=$(echo "${full_domain}" | sed "s|\\.${zone_name}\$||")
body="id=${zone_id}&name=${prefix}&type=TXT&content=${txt_value}&ttl=300&prio=0"
do_post "$body" "https://www.geoscaling.com/dns2/ajax/add_record.php"
exit_code="$?"
if [ "${exit_code}" -eq 0 ]; then
_info "TXT record added successfully."
else
_err "Couldn't add the TXT record."
fi
do_logout
return "${exit_code}"
}
#-- dns_geoscaling_rm() - Remove TXT record ------------------------------------
# Usage: dns_geoscaling_rm _acme-challenge.subdomain.domain.com "XyZ123..."
dns_geoscaling_rm() {
full_domain=$1
txt_value=$2
_info "Cleaning up after DNS-01 Geoscaling DNS2 hook"
GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}"
GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}"
if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then
GEOSCALING_Username=
GEOSCALING_Password=
_err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables."
return 1
fi
_saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}"
_saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}"
# fills in the $zone_id
find_zone "${full_domain}" || return 1
_debug "Zone id '${zone_id}' will be used."
# Here we're logged in
# Find the record id to clean
# get the domain
response=$(do_get "https://www.geoscaling.com/dns2/index.php?module=domain&id=${zone_id}")
_debug2 "response" "$response"
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Basic Records</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')"
_debug2 table "${table}"
names=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|</td>||; s|.*>||')
ids=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|\.name">.*||; s|id="||')
types=$(echo "${table}" | _egrep_o 'id="[0-9]+\.type">[^<]*</td>' | sed 's|</td>||; s|.*>||')
values=$(echo "${table}" | _egrep_o 'id="[0-9]+\.content">[^<]*</td>' | sed 's|</td>||; s|.*>||')
_debug2 names "${names}"
_debug2 ids "${ids}"
_debug2 types "${types}"
_debug2 values "${values}"
# look for line whose name is ${full_domain}, whose type is TXT, and whose value is ${txt_value}
line_num="$(echo "${values}" | grep -F -n -- "${txt_value}" | _head_n 1 | cut -d ':' -f 1)"
_debug2 line_num "${line_num}"
found_id=
if [ -n "$line_num" ]; then
type=$(echo "${types}" | sed -n "${line_num}p")
name=$(echo "${names}" | sed -n "${line_num}p")
id=$(echo "${ids}" | sed -n "${line_num}p")
_debug2 type "$type"
_debug2 name "$name"
_debug2 id "$id"
_debug2 full_domain "$full_domain"
if [ "${type}" = "TXT" ] && [ "${name}" = "${full_domain}" ]; then
found_id=${id}
fi
fi
if [ "${found_id}" = "" ]; then
_err "Can not find record id."
return 0
fi
# Remove the record
body="id=${zone_id}&record_id=${found_id}"
response=$(do_post "$body" "https://www.geoscaling.com/dns2/ajax/delete_record.php")
exit_code="$?"
if [ "$exit_code" -eq 0 ]; then
_info "Record removed successfully."
else
_err "Could not clean (remove) up the record. Please go to Geoscaling administration interface and clean it by hand."
fi
do_logout
return "${exit_code}"
}
########################## PRIVATE FUNCTIONS ###########################
do_get() {
_url=$1
export _H1="Cookie: $geoscaling_phpsessid_cookie"
_get "${_url}"
}
do_post() {
_body=$1
_url=$2
export _H1="Cookie: $geoscaling_phpsessid_cookie"
_post "${_body}" "${_url}"
}
do_login() {
_info "Logging in..."
username_encoded="$(printf "%s" "${GEOSCALING_Username}" | _url_encode)"
password_encoded="$(printf "%s" "${GEOSCALING_Password}" | _url_encode)"
body="username=${username_encoded}&password=${password_encoded}"
response=$(_post "$body" "https://www.geoscaling.com/dns2/index.php?module=auth")
_debug2 response "${response}"
#retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | _egrep_o '[0-9]+$')
retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | cut -d ' ' -f 2)
if [ "$retcode" != "302" ]; then
_err "Geoscaling login failed for user ${GEOSCALING_Username}. Check ${HTTP_HEADER} file"
return 1
fi
geoscaling_phpsessid_cookie="$(grep -i '^set-cookie:' "${HTTP_HEADER}" | _egrep_o 'PHPSESSID=[^;]*;' | tr -d ';')"
return 0
}
do_logout() {
_info "Logging out."
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=auth")"
_debug2 response "$response"
return 0
}
find_zone() {
domain="$1"
# do login
do_login || return 1
# get zones
response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=domains")"
table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Your domains</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')"
_debug2 table "${table}"
zone_names="$(echo "${table}" | _egrep_o '<b>[^<]*</b>' | sed 's|<b>||;s|</b>||')"
_debug2 _matches "${zone_names}"
# Zone names and zone IDs are in same order
zone_ids=$(echo "${table}" | _egrep_o '<a href=.index\.php\?module=domain&id=[0-9]+. onclick="javascript:show_loader\(\);">' | sed 's|.*id=||;s|. .*||')
_debug2 "These are the zones on this Geoscaling account:"
_debug2 "zone_names" "${zone_names}"
_debug2 "And these are their respective IDs:"
_debug2 "zone_ids" "${zone_ids}"
if [ -z "${zone_names}" ] || [ -z "${zone_ids}" ]; then
_err "Can not get zone names or IDs."
return 1
fi
# Walk through all possible zone names
strip_counter=1
while true; do
attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-)
# All possible zone names have been tried
if [ -z "${attempted_zone}" ]; then
_err "No zone for domain '${domain}' found."
return 1
fi
_debug "Looking for zone '${attempted_zone}'"
line_num="$(echo "${zone_names}" | grep -n "^${attempted_zone}\$" | _head_n 1 | cut -d : -f 1)"
_debug2 line_num "${line_num}"
if [ "$line_num" ]; then
zone_id=$(echo "${zone_ids}" | sed -n "${line_num}p")
zone_name=$(echo "${zone_names}" | sed -n "${line_num}p")
if [ -z "${zone_id}" ]; then
_err "Can not find zone id."
return 1
fi
_debug "Found relevant zone '${attempted_zone}' with id '${zone_id}' - will be used for domain '${domain}'."
return 0
fi
_debug "Zone '${attempted_zone}' doesn't exist, let's try a less specific zone."
strip_counter=$(_math "${strip_counter}" + 1)
done
}
# vim: et:ts=2:sw=2:

173
dnsapi/dns_googledomains.sh Executable file
View File

@@ -0,0 +1,173 @@
#!/usr/bin/env sh
# Author: Alex Leigh <leigh at alexleigh dot me>
# Created: 2023-03-02
#GOOGLEDOMAINS_ACCESS_TOKEN="xxxx"
#GOOGLEDOMAINS_ZONE="xxxx"
GOOGLEDOMAINS_API="https://acmedns.googleapis.com/v1/acmeChallengeSets"
######## Public functions ########
#Usage: dns_googledomains_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_googledomains_add() {
fulldomain=$1
txtvalue=$2
_info "Invoking Google Domains ACME DNS API."
if ! _dns_googledomains_setup; then
return 1
fi
zone="$(_dns_googledomains_get_zone "$fulldomain")"
if [ -z "$zone" ]; then
_err "Could not find a Google Domains-managed zone containing the requested domain."
return 1
fi
_debug zone "$zone"
_debug txtvalue "$txtvalue"
_info "Adding TXT record for $fulldomain."
if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToAdd\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
if _contains "$response" "$txtvalue"; then
_info "TXT record added."
return 0
else
_err "Error adding TXT record."
return 1
fi
fi
_err "Error adding TXT record."
return 1
}
#Usage: dns_googledomains_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_googledomains_rm() {
fulldomain=$1
txtvalue=$2
_info "Invoking Google Domains ACME DNS API."
if ! _dns_googledomains_setup; then
return 1
fi
zone="$(_dns_googledomains_get_zone "$fulldomain")"
if [ -z "$zone" ]; then
_err "Could not find a Google Domains-managed domain based on request."
return 1
fi
_debug zone "$zone"
_debug txtvalue "$txtvalue"
_info "Removing TXT record for $fulldomain."
if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToRemove\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
if _contains "$response" "$txtvalue"; then
_err "Error removing TXT record."
return 1
else
_info "TXT record removed."
return 0
fi
fi
_err "Error removing TXT record."
return 1
}
######## Private functions ########
_dns_googledomains_setup() {
if [ -n "$GOOGLEDOMAINS_SETUP_COMPLETED" ]; then
return 0
fi
GOOGLEDOMAINS_ACCESS_TOKEN="${GOOGLEDOMAINS_ACCESS_TOKEN:-$(_readaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN)}"
GOOGLEDOMAINS_ZONE="${GOOGLEDOMAINS_ZONE:-$(_readaccountconf_mutable GOOGLEDOMAINS_ZONE)}"
if [ -z "$GOOGLEDOMAINS_ACCESS_TOKEN" ]; then
GOOGLEDOMAINS_ACCESS_TOKEN=""
_err "Google Domains access token was not specified."
_err "Please visit Google Domains Security settings to provision an ACME DNS API access token."
return 1
fi
if [ "$GOOGLEDOMAINS_ZONE" ]; then
_savedomainconf GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
_savedomainconf GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
else
_saveaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
_clearaccountconf_mutable GOOGLEDOMAINS_ZONE
_clearaccountconf GOOGLEDOMAINS_ZONE
fi
_debug GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
_debug GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
GOOGLEDOMAINS_SETUP_COMPLETED=1
return 0
}
_dns_googledomains_get_zone() {
domain=$1
# Use zone directly if provided
if [ "$GOOGLEDOMAINS_ZONE" ]; then
if ! _dns_googledomains_api "$GOOGLEDOMAINS_ZONE"; then
return 1
fi
echo "$GOOGLEDOMAINS_ZONE"
return 0
fi
i=2
while true; do
curr=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug curr "$curr"
if [ -z "$curr" ]; then
return 1
fi
if _dns_googledomains_api "$curr"; then
echo "$curr"
return 0
fi
i=$(_math "$i" + 1)
done
return 1
}
_dns_googledomains_api() {
zone=$1
apimethod=$2
data="$3"
if [ -z "$data" ]; then
response="$(_get "$GOOGLEDOMAINS_API/$zone$apimethod")"
else
_debug data "$data"
export _H1="Content-Type: application/json"
response="$(_post "$data" "$GOOGLEDOMAINS_API/$zone$apimethod")"
fi
_debug response "$response"
if [ "$?" != "0" ]; then
_err "Error"
return 1
fi
if _contains "$response" "\"error\": {"; then
return 1
fi
return 0
}

View File

@@ -85,7 +85,7 @@ dns_he_rm() {
_debug "The txt record is not found, just skip"
return 0
fi
_record_id="$(echo "$response" | tr -d "#" | sed "s/<tr/#<tr/g" | tr -d "\n" | tr "#" "\n" | grep "$_full_domain" | grep '"dns_tr"' | grep "$_txt_value" | cut -d '"' -f 4)"
_record_id="$(echo "$response" | tr -d "#" | sed "s/<tr/#<tr/g" | tr -d "\n" | tr "#" "\n" | grep "$_full_domain" | grep '"dns_tr"' | grep -- "$_txt_value" | cut -d '"' -f 4)"
_debug2 _record_id "$_record_id"
if [ -z "$_record_id" ]; then
_err "Can not find record id"

327
dnsapi/dns_huaweicloud.sh Normal file
View File

@@ -0,0 +1,327 @@
#!/usr/bin/env sh
# HUAWEICLOUD_Username
# HUAWEICLOUD_Password
# HUAWEICLOUD_DomainName
iam_api="https://iam.myhuaweicloud.com"
dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
#
# Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/zh-cn_topic_0132421999.html
#
# About "DomainName" parameters see: https://support.huaweicloud.com/api-iam/iam_01_0006.html
#
dns_huaweicloud_add() {
fulldomain=$1
txtvalue=$2
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
# Check information
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
_err "Not enough information provided to dns_huaweicloud!"
return 1
fi
unset token # Clear token
token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
if [ -z "${token}" ]; then # Check token
_err "dns_api(dns_huaweicloud): Error getting token."
return 1
fi
_secure_debug "Access token is:" "${token}"
unset zoneid
zoneid="$(_get_zoneid "${token}" "${fulldomain}")"
if [ -z "${zoneid}" ]; then
_err "dns_api(dns_huaweicloud): Error getting zone id."
return 1
fi
_debug "Zone ID is:" "${zoneid}"
_debug "Adding Record"
_add_record "${token}" "${fulldomain}" "${txtvalue}"
ret="$?"
if [ "${ret}" != "0" ]; then
_err "dns_api(dns_huaweicloud): Error adding record."
return 1
fi
# Do saving work if all succeeded
_saveaccountconf_mutable HUAWEICLOUD_Username "${HUAWEICLOUD_Username}"
_saveaccountconf_mutable HUAWEICLOUD_Password "${HUAWEICLOUD_Password}"
_saveaccountconf_mutable HUAWEICLOUD_DomainName "${HUAWEICLOUD_DomainName}"
return 0
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
#
# Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/dns_api_64005.html
#
dns_huaweicloud_rm() {
fulldomain=$1
txtvalue=$2
HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
# Check information
if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
_err "Not enough information provided to dns_huaweicloud!"
return 1
fi
unset token # Clear token
token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
if [ -z "${token}" ]; then # Check token
_err "dns_api(dns_huaweicloud): Error getting token."
return 1
fi
_secure_debug "Access token is:" "${token}"
unset zoneid
zoneid="$(_get_zoneid "${token}" "${fulldomain}")"
if [ -z "${zoneid}" ]; then
_err "dns_api(dns_huaweicloud): Error getting zone id."
return 1
fi
_debug "Zone ID is:" "${zoneid}"
record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
_recursive_rm_record "${token}" "${fulldomain}" "${zoneid}" "${record_id}"
ret="$?"
if [ "${ret}" != "0" ]; then
_err "dns_api(dns_huaweicloud): Error removing record."
return 1
fi
return 0
}
################### Private functions below ##################################
# _recursive_rm_record
# remove all records from the record set
#
# _token=$1
# _domain=$2
# _zoneid=$3
# _record_id=$4
#
# Returns 0 on success
_recursive_rm_record() {
_token=$1
_domain=$2
_zoneid=$3
_record_id=$4
# Most likely to have problems will huaweicloud side if more than 50 attempts but still cannot fully remove the record set
# Maybe can be removed manually in the dashboard
_retry_cnt=50
# Remove all records
# Therotically HuaweiCloud does not allow more than one record set
# But remove them recurringly to increase robusty
while [ "${_record_id}" != "0" ] && [ "${_retry_cnt}" != "0" ]; do
_debug "Removing Record"
_retry_cnt=$((_retry_cnt - 1))
_rm_record "${_token}" "${_zoneid}" "${_record_id}"
_record_id="$(_get_recordset_id "${_token}" "${_domain}" "${_zoneid}")"
_debug2 "Checking record exists: record_id=${_record_id}"
done
# Check if retry count is reached
if [ "${_retry_cnt}" = "0" ]; then
_debug "Failed to remove record after 50 attempts, please try removing it manually in the dashboard"
return 1
fi
return 0
}
# _get_zoneid
#
# _token=$1
# _domain_string=$2
#
# printf "%s" "${_zoneid}"
_get_zoneid() {
_token=$1
_domain_string=$2
export _H1="X-Auth-Token: ${_token}"
i=1
while true; do
h=$(printf "%s" "${_domain_string}" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_debug "$h"
response=$(_get "${dns_api}/v2/zones?name=${h}")
_debug2 "$response"
if _contains "${response}" '"id"'; then
zoneidlist=$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
zonenamelist=$(echo "${response}" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
_debug2 "Returned Zone ID(s):" "${zoneidlist}"
_debug2 "Returned Zone Name(s):" "${zonenamelist}"
zoneidnum=0
zoneidcount=$(echo "${zoneidlist}" | grep -c '^')
_debug "Returned Zone ID(s) Count:" "${zoneidcount}"
while [ "${zoneidnum}" -lt "${zoneidcount}" ]; do
zoneidnum=$(_math "$zoneidnum" + 1)
_zoneid=$(echo "${zoneidlist}" | sed -n "${zoneidnum}p")
zonename=$(echo "${zonenamelist}" | sed -n "${zoneidnum}p")
_debug "Check Zone Name" "${zonename}"
if [ "${zonename}" = "${h}." ]; then
_debug "Get Zone ID Success."
_debug "ZoneID:" "${_zoneid}"
printf "%s" "${_zoneid}"
return 0
fi
done
fi
i=$(_math "$i" + 1)
done
return 1
}
_get_recordset_id() {
_token=$1
_domain=$2
_zoneid=$3
export _H1="X-Auth-Token: ${_token}"
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}")
if _contains "${response}" '"id"'; then
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
printf "%s" "${_id}"
return 0
fi
printf "%s" "0"
return 1
}
_add_record() {
_token=$1
_domain=$2
_txtvalue=$3
# Get Existing Records
export _H1="X-Auth-Token: ${_token}"
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}")
_debug2 "${response}"
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')
_debug "${_exist_record}"
# Check if record exist
# Generate body data
if [ -z "${_exist_record}" ]; then
_post_body="{
\"name\": \"${_domain}.\",
\"description\": \"ACME Challenge\",
\"type\": \"TXT\",
\"ttl\": 1,
\"records\": [
\"\\\"${_txtvalue}\\\"\"
]
}"
else
_post_body="{
\"name\": \"${_domain}.\",
\"description\": \"ACME Challenge\",
\"type\": \"TXT\",
\"ttl\": 1,
\"records\": [
${_exist_record},\"\\\"${_txtvalue}\\\"\"
]
}"
fi
_record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
_debug "Record Set ID is:" "${_record_id}"
# Add brand new records with all old and new records
export _H2="Content-Type: application/json"
export _H1="X-Auth-Token: ${_token}"
_debug2 "${_post_body}"
if [ -z "${_exist_record}" ]; then
_post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
else
_post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets/${_record_id}" false "PUT" >/dev/null
fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$_code" != "202" ]; then
_err "dns_huaweicloud: http code ${_code}"
return 1
fi
return 0
}
# _rm_record $token $zoneid $recordid
# assume ${dns_api} exist
# no output
# return 0
_rm_record() {
_token=$1
_zone_id=$2
_record_id=$3
export _H2="Content-Type: application/json"
export _H1="X-Auth-Token: ${_token}"
_post "" "${dns_api}/v2/zones/${_zone_id}/recordsets/${_record_id}" false "DELETE" >/dev/null
return $?
}
_get_token() {
_username=$1
_password=$2
_domain_name=$3
_debug "Getting Token"
body="{
\"auth\": {
\"identity\": {
\"methods\": [
\"password\"
],
\"password\": {
\"user\": {
\"name\": \"${_username}\",
\"password\": \"${_password}\",
\"domain\": {
\"name\": \"${_domain_name}\"
}
}
}
},
\"scope\": {
\"project\": {
\"name\": \"ap-southeast-1\"
}
}
}
}"
export _H1="Content-Type: application/json;charset=utf8"
_post "${body}" "${iam_api}/v3/auth/tokens" >/dev/null
_code=$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")
_token=$(grep "^X-Subject-Token" "$HTTP_HEADER" | cut -d " " -f 2-)
_secure_debug "${_code}"
printf "%s" "${_token}"
return 0
}

View File

@@ -9,7 +9,6 @@ dns_infoblox_add() {
## Nothing to see here, just some housekeeping
fulldomain=$1
txtvalue=$2
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View"
_info "Using Infoblox API"
_debug fulldomain "$fulldomain"
@@ -19,12 +18,13 @@ dns_infoblox_add() {
if [ -z "$Infoblox_Creds" ] || [ -z "$Infoblox_Server" ]; then
Infoblox_Creds=""
Infoblox_Server=""
_err "You didn't specify the credentials, server or infoblox view yet (Infoblox_Creds, Infoblox_Server and Infoblox_View)."
_err "Please set them via EXPORT ([username:password], [ip or hostname]) and try again."
_err "You didn't specify the Infoblox credentials or server (Infoblox_Creds; Infoblox_Server)."
_err "Please set them via EXPORT Infoblox_Creds=username:password or EXPORT Infoblox_server=ip/hostname and try again."
return 1
fi
if [ -z "$Infoblox_View" ]; then
_info "No Infoblox_View set, using fallback value 'default'"
Infoblox_View="default"
fi
@@ -33,6 +33,9 @@ dns_infoblox_add() {
_saveaccountconf Infoblox_Server "$Infoblox_Server"
_saveaccountconf Infoblox_View "$Infoblox_View"
## URLencode Infoblox View to deal with e.g. spaces
Infoblox_ViewEncoded=$(printf "%b" "$Infoblox_View" | _url_encode)
## Base64 encode the credentials
Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64)
@@ -40,11 +43,14 @@ dns_infoblox_add() {
export _H1="Accept-Language:en-US"
export _H2="Authorization: Basic $Infoblox_CredsEncoded"
## Construct the request URL
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=${Infoblox_ViewEncoded}"
## Add the challenge record to the Infoblox grid member
result="$(_post "" "$baseurlnObject" "" "POST")"
## Let's see if we get something intelligible back from the unit
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
_info "Successfully created the txt record"
return 0
else
@@ -65,6 +71,9 @@ dns_infoblox_rm() {
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## URLencode Infoblox View to deal with e.g. spaces
Infoblox_ViewEncoded=$(printf "%b" "$Infoblox_View" | _url_encode)
## Base64 encode the credentials
Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)"
@@ -73,18 +82,18 @@ dns_infoblox_rm() {
export _H2="Authorization: Basic $Infoblox_CredsEncoded"
## Does the record exist? Let's check.
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty"
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=${Infoblox_ViewEncoded}&_return_type=xml-pretty"
result="$(_get "$baseurlnObject")"
## Let's see if we get something intelligible back from the grid
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
## Extract the object reference
objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")"
objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")"
objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef"
## Delete them! All the stale records!
rmResult="$(_post "" "$objRmUrl" "" "DELETE")"
## Let's see if that worked
if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
_info "Successfully deleted $objRef"
return 0
else

199
dnsapi/dns_infomaniak.sh Executable file
View File

@@ -0,0 +1,199 @@
#!/usr/bin/env sh
###############################################################################
# Infomaniak API integration
#
# To use this API you need visit the API dashboard of your account
# once logged into https://manager.infomaniak.com add /api/dashboard to the URL
#
# Please report bugs to
# https://github.com/acmesh-official/acme.sh/issues/3188
#
# Note: the URL looks like this:
# https://manager.infomaniak.com/v3/<account_id>/api/dashboard
# Then generate a token with the scope Domain
# this is given as an environment variable INFOMANIAK_API_TOKEN
###############################################################################
# base variables
DEFAULT_INFOMANIAK_API_URL="https://api.infomaniak.com"
DEFAULT_INFOMANIAK_TTL=300
######## Public functions #####################
#Usage: dns_infomaniak_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_infomaniak_add() {
INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}"
INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}"
INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}"
if [ -z "$INFOMANIAK_API_TOKEN" ]; then
INFOMANIAK_API_TOKEN=""
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN"
return 1
fi
if [ -z "$INFOMANIAK_API_URL" ]; then
INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL"
fi
if [ -z "$INFOMANIAK_TTL" ]; then
INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL"
fi
#save the token to the account conf file.
_saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN"
if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then
_saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL"
fi
if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then
_saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL"
fi
export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN"
export _H2="Content-Type: application/json"
fulldomain="$1"
txtvalue="$2"
_info "Infomaniak DNS API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
fqdn=${fulldomain#_acme-challenge.}
# guess which base domain to add record to
zone_and_id=$(_find_zone "$fqdn")
if [ -z "$zone_and_id" ]; then
_err "cannot find zone to modify"
return 1
fi
zone=${zone_and_id% *}
domain_id=${zone_and_id#* }
# extract first part of domain
key=${fulldomain%."$zone"}
_debug "zone:$zone id:$domain_id key:$key"
# payload
data="{\"type\": \"TXT\", \"source\": \"$key\", \"target\": \"$txtvalue\", \"ttl\": $INFOMANIAK_TTL}"
# API call
response=$(_post "$data" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record")
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
_info "Record added"
_debug "Response: $response"
return 0
fi
_err "could not create record"
_debug "Response: $response"
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_infomaniak_rm() {
INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}"
INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}"
INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}"
if [ -z "$INFOMANIAK_API_TOKEN" ]; then
INFOMANIAK_API_TOKEN=""
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN"
return 1
fi
if [ -z "$INFOMANIAK_API_URL" ]; then
INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL"
fi
if [ -z "$INFOMANIAK_TTL" ]; then
INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL"
fi
#save the token to the account conf file.
_saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN"
if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then
_saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL"
fi
if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then
_saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL"
fi
export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN"
export _H2="ContentType: application/json"
fulldomain=$1
txtvalue=$2
_info "Infomaniak DNS API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
fqdn=${fulldomain#_acme-challenge.}
# guess which base domain to add record to
zone_and_id=$(_find_zone "$fqdn")
if [ -z "$zone_and_id" ]; then
_err "cannot find zone to modify"
return 1
fi
zone=${zone_and_id% *}
domain_id=${zone_and_id#* }
# extract first part of domain
key=${fulldomain%."$zone"}
_debug "zone:$zone id:$domain_id key:$key"
# find previous record
# shellcheck disable=SC1004
record_id=$(_get "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record" | sed 's/.*"data":\[\(.*\)\]}/\1/; s/},{/}\
{/g' | sed -n 's/.*"id":"*\([0-9]*\)"*.*"source_idn":"'"$fulldomain"'".*"target_idn":"'"$txtvalue"'".*/\1/p')
if [ -z "$record_id" ]; then
_err "could not find record to delete"
return 1
fi
_debug "record_id: $record_id"
# API call
response=$(_post "" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record/$record_id" "" DELETE)
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
_info "Record deleted"
return 0
fi
_err "could not delete record"
return 1
}
#################### Private functions below ##################################
_get_domain_id() {
domain="$1"
# shellcheck disable=SC1004
_get "${INFOMANIAK_API_URL}/1/product?service_name=domain&customer_name=$domain" | sed 's/.*"data":\[{\(.*\)}\]}/\1/; s/,/\
/g' | sed -n 's/^"id":\(.*\)/\1/p'
}
_find_zone() {
zone="$1"
# find domain in list, removing . parts sequentialy
while _contains "$zone" '\.'; do
_debug "testing $zone"
id=$(_get_domain_id "$zone")
if [ -n "$id" ]; then
echo "$zone $id"
return
fi
zone=${zone#*.}
done
}

171
dnsapi/dns_ionos.sh Executable file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/env sh
# Supports IONOS DNS API v1.0.1
#
# Usage:
# Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh:
#
# $ export IONOS_PREFIX="..."
# $ export IONOS_SECRET="..."
#
# $ acme.sh --issue --dns dns_ionos ...
IONOS_API="https://api.hosting.ionos.com/dns"
IONOS_ROUTE_ZONES="/v1/zones"
IONOS_TXT_TTL=60 # minimum accepted by API
IONOS_TXT_PRIO=10
dns_ionos_add() {
fulldomain=$1
txtvalue=$2
if ! _ionos_init; then
return 1
fi
_body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]"
if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ "$_code" = "201" ]; then
_info "TXT record has been created successfully."
return 0
fi
return 1
}
dns_ionos_rm() {
fulldomain=$1
txtvalue=$2
if ! _ionos_init; then
return 1
fi
if ! _ionos_get_record "$fulldomain" "$_zone_id" "$txtvalue"; then
_err "Could not find _acme-challenge TXT record."
return 1
fi
if _ionos_rest DELETE "$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ "$_code" = "200" ]; then
_info "TXT record has been deleted successfully."
return 0
fi
return 1
}
_ionos_init() {
IONOS_PREFIX="${IONOS_PREFIX:-$(_readaccountconf_mutable IONOS_PREFIX)}"
IONOS_SECRET="${IONOS_SECRET:-$(_readaccountconf_mutable IONOS_SECRET)}"
if [ -z "$IONOS_PREFIX" ] || [ -z "$IONOS_SECRET" ]; then
_err "You didn't specify an IONOS api prefix and secret yet."
_err "Read https://beta.developer.hosting.ionos.de/docs/getstarted to learn how to get a prefix and secret."
_err ""
_err "Then set them before calling acme.sh:"
_err "\$ export IONOS_PREFIX=\"...\""
_err "\$ export IONOS_SECRET=\"...\""
_err "\$ acme.sh --issue -d ... --dns dns_ionos"
return 1
fi
_saveaccountconf_mutable IONOS_PREFIX "$IONOS_PREFIX"
_saveaccountconf_mutable IONOS_SECRET "$IONOS_SECRET"
if ! _get_root "$fulldomain"; then
_err "Cannot find this domain in your IONOS account."
return 1
fi
}
_get_root() {
domain=$1
i=1
p=1
if _ionos_rest GET "$IONOS_ROUTE_ZONES"; then
_response="$(echo "$_response" | tr -d "\n")"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
return 1
fi
_zone="$(echo "$_response" | _egrep_o "\"name\":\"$h\".*\}")"
if [ "$_zone" ]; then
_zone_id=$(printf "%s\n" "$_zone" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
if [ "$_zone_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
}
_ionos_get_record() {
fulldomain=$1
zone_id=$2
txtrecord=$3
if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
_response="$(echo "$_response" | tr -d "\n")"
_record="$(echo "$_response" | _egrep_o "\"name\":\"$fulldomain\"[^\}]*\"type\":\"TXT\"[^\}]*\"content\":\"\\\\\"$txtrecord\\\\\"\".*\}")"
if [ "$_record" ]; then
_record_id=$(printf "%s\n" "$_record" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
return 0
fi
fi
return 1
}
_ionos_rest() {
method="$1"
route="$2"
data="$3"
IONOS_API_KEY="$(printf "%s.%s" "$IONOS_PREFIX" "$IONOS_SECRET")"
export _H1="X-API-Key: $IONOS_API_KEY"
# clear headers
: >"$HTTP_HEADER"
if [ "$method" != "GET" ]; then
export _H2="Accept: application/json"
export _H3="Content-Type: application/json"
_response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
else
export _H2="Accept: */*"
export _H3=
_response="$(_get "$IONOS_API$route")"
fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$?" != "0" ]; then
_err "Error $route: $_response"
return 1
fi
_debug2 "_response" "$_response"
_debug2 "_code" "$_code"
return 0
}

157
dnsapi/dns_ipv64.sh Executable file
View File

@@ -0,0 +1,157 @@
#!/usr/bin/env sh
#Created by Roman Lumetsberger, to use ipv64.net's API to add/remove text records
#2022/11/29
# Pass credentials before "acme.sh --issue --dns dns_ipv64 ..."
# --
# export IPv64_Token="aaaaaaaaaaaaaaaaaaaaaaaaaa"
# --
#
IPv64_API="https://ipv64.net/api"
######## Public functions ######################
#Usage: dns_ipv64_add _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ipv64_add() {
fulldomain=$1
txtvalue=$2
IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
if [ -z "$IPv64_Token" ]; then
_err "You must export variable: IPv64_Token"
_err "The API Key for your IPv64 account is necessary."
_err "You can look it up in your IPv64 account."
return 1
fi
# Now save the credentials.
_saveaccountconf_mutable IPv64_Token "$IPv64_Token"
if ! _get_root "$fulldomain"; then
_err "invalid domain" "$fulldomain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# convert to lower case
_domain="$(echo "$_domain" | _lower_case)"
_sub_domain="$(echo "$_sub_domain" | _lower_case)"
# Now add the TXT record
_info "Trying to add TXT record"
if _ipv64_rest "POST" "add_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
_info "TXT record has been successfully added."
return 0
else
_err "Errors happened during adding the TXT record, response=$_response"
return 1
fi
}
#Usage: fulldomain txtvalue
#Usage: dns_ipv64_rm _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
#Remove the txt record after validation.
dns_ipv64_rm() {
fulldomain=$1
txtvalue=$2
IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
if [ -z "$IPv64_Token" ]; then
_err "You must export variable: IPv64_Token"
_err "The API Key for your IPv64 account is necessary."
_err "You can look it up in your IPv64 account."
return 1
fi
if ! _get_root "$fulldomain"; then
_err "invalid domain" "$fulldomain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# convert to lower case
_domain="$(echo "$_domain" | _lower_case)"
_sub_domain="$(echo "$_sub_domain" | _lower_case)"
# Now delete the TXT record
_info "Trying to delete TXT record"
if _ipv64_rest "DELETE" "del_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
_info "TXT record has been successfully deleted."
return 0
else
_err "Errors happened during deleting the TXT record, response=$_response"
return 1
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain="$1"
i=1
p=1
_ipv64_get "get_domains"
domain_data=$_response
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
#if _contains "$domain_data" "\""$h"\"\:"; then
if _contains "$domain_data" "\"""$h""\"\:"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
#send get request to api
# $1 has to set the api-function
_ipv64_get() {
url="$IPv64_API?$1"
export _H1="Authorization: Bearer $IPv64_Token"
_response=$(_get "$url")
_response="$(echo "$_response" | _normalizeJson)"
if _contains "$_response" "429 Too Many Requests"; then
_info "API throttled, sleeping to reset the limit"
_sleep 10
_response=$(_get "$url")
_response="$(echo "$_response" | _normalizeJson)"
fi
}
_ipv64_rest() {
url="$IPv64_API"
export _H1="Authorization: Bearer $IPv64_Token"
export _H2="Content-Type: application/x-www-form-urlencoded"
_response=$(_post "$2" "$url" "" "$1")
if _contains "$_response" "429 Too Many Requests"; then
_info "API throttled, sleeping to reset the limit"
_sleep 10
_response=$(_post "$2" "$url" "" "$1")
fi
if ! _contains "$_response" "\"info\":\"success\""; then
return 1
fi
_debug2 response "$_response"
return 0
}

View File

@@ -32,7 +32,11 @@ dns_ispconfig_rm() {
#################### Private functions below ##################################
_ISPC_credentials() {
if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
ISPC_User="${ISPC_User:-$(_readaccountconf_mutable ISPC_User)}"
ISPC_Password="${ISPC_Password:-$(_readaccountconf_mutable ISPC_Password)}"
ISPC_Api="${ISPC_Api:-$(_readaccountconf_mutable ISPC_Api)}"
ISPC_Api_Insecure="${ISPC_Api_Insecure:-$(_readaccountconf_mutable ISPC_Api_Insecure)}"
if [ -z "${ISPC_User}" ] || [ -z "${ISPC_Password}" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
ISPC_User=""
ISPC_Password=""
ISPC_Api=""
@@ -40,10 +44,10 @@ _ISPC_credentials() {
_err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
return 1
else
_saveaccountconf ISPC_User "${ISPC_User}"
_saveaccountconf ISPC_Password "${ISPC_Password}"
_saveaccountconf ISPC_Api "${ISPC_Api}"
_saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}"
_saveaccountconf_mutable ISPC_User "${ISPC_User}"
_saveaccountconf_mutable ISPC_Password "${ISPC_Password}"
_saveaccountconf_mutable ISPC_Api "${ISPC_Api}"
_saveaccountconf_mutable ISPC_Api_Insecure "${ISPC_Api_Insecure}"
# Set whether curl should use secure or insecure mode
export HTTPS_INSECURE="${ISPC_Api_Insecure}"
fi
@@ -75,7 +79,7 @@ _ISPC_getZoneInfo() {
# suffix . needed for zone -> domain.tld.
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")"
_debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?login'"
_debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?dns_zone_get'"
_debug "Result of _ISPC_getZoneInfo: '$curResult'"
if _contains "${curResult}" '"id":"'; then
zoneFound=true
@@ -110,18 +114,32 @@ _ISPC_getZoneInfo() {
;;
*) _info "Retrieved Zone ID" ;;
esac
client_id=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Client ID: '${client_id}'"
case "${client_id}" in
sys_userid=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "SYS User ID: '${sys_userid}'"
case "${sys_userid}" in
'' | *[!0-9]*)
_err "Client ID is not numeric."
_err "SYS User ID is not numeric."
return 1
;;
*) _info "Retrieved Client ID." ;;
*) _info "Retrieved SYS User ID." ;;
esac
zoneFound=""
zoneEnd=""
fi
# Need to get client_id as it is different from sys_userid
curData="{\"session_id\":\"${sessionID}\",\"sys_userid\":\"${sys_userid}\"}"
curResult="$(_post "${curData}" "${ISPC_Api}?client_get_id")"
_debug "Calling _ISPC_ClientGetID: '${curData}' '${ISPC_Api}?client_get_id'"
_debug "Result of _ISPC_ClientGetID: '$curResult'"
client_id=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2 | tr -d '{}')
_debug "Client ID: '${client_id}'"
case "${client_id}" in
'' | *[!0-9]*)
_err "Client ID is not numeric."
return 1
;;
*) _info "Retrieved Client ID." ;;
esac
}
_ISPC_addTxt() {

View File

@@ -5,51 +5,81 @@
# Environment variables:
#
# - $KAS_Login (Kasserver API login name)
# - $KAS_Authtype (Kasserver API auth type. Default: sha1)
# - $KAS_Authtype (Kasserver API auth type. Default: plain)
# - $KAS_Authdata (Kasserver API auth data.)
#
# Author: Martin Kammerlander, Phlegx Systems OG <martin.kammerlander@phlegx.com>
# Updated by: Marc-Oliver Lange <git@die-lang.es>
# Credits: Inspired by dns_he.sh. Thanks a lot man!
# Git repo: https://github.com/phlegx/acme.sh
# TODO: Better Error handling
# Last update: squared GmbH <github@squaredgmbh.de>
# Credits:
# - dns_he.sh. Thanks a lot man!
# - Martin Kammerlander, Phlegx Systems OG <martin.kammerlander@phlegx.com>
# - Marc-Oliver Lange <git@die-lang.es>
# - https://github.com/o1oo11oo/kasapi.sh
########################################################################
KAS_Api="https://kasapi.kasserver.com/dokumentation/formular.php"
KAS_Api_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl")"
KAS_Api="$(echo "$KAS_Api_GET" | tr -d ' ' | grep -i "<soap:addresslocation=" | sed "s/='/\n/g" | grep -i "http" | sed "s/'\/>//g")"
_info "[KAS] -> API URL $KAS_Api"
KAS_Auth_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasAuth.wsdl")"
KAS_Auth="$(echo "$KAS_Auth_GET" | tr -d ' ' | grep -i "<soap:addresslocation=" | sed "s/='/\n/g" | grep -i "http" | sed "s/'\/>//g")"
_info "[KAS] -> AUTH URL $KAS_Auth"
KAS_default_ratelimit=5 # TODO - Every response delivers a ratelimit (seconds) where KASAPI is blocking a request.
######## Public functions #####################
dns_kas_add() {
_fulldomain=$1
_txtvalue=$2
_info "Using DNS-01 All-inkl/Kasserver hook"
_info "Adding $_fulldomain DNS TXT entry on All-inkl/Kasserver"
_info "Check and Save Props"
_info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
_info "[KAS] -> Check and Save Props"
_check_and_save
_info "Checking Zone and Record_Name"
_info "[KAS] -> Adding $_fulldomain DNS TXT entry on all-inkl.com/Kasserver"
_info "[KAS] -> Retriving Credential Token"
_get_credential_token
_info "[KAS] -> Checking Zone and Record_Name"
_get_zone_and_record_name "$_fulldomain"
_info "Getting Record ID"
_info "[KAS] -> Checking for existing Record entries"
_get_record_id
_info "Creating TXT DNS record"
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&var1=record_name"
params="$params&wert1=$_record_name"
params="$params&var2=record_type"
params="$params&wert2=TXT"
params="$params&var3=record_data"
params="$params&wert3=$_txtvalue"
params="$params&var4=record_aux"
params="$params&wert4=0"
params="$params&kas_action=add_dns_settings"
params="$params&var5=zone_host"
params="$params&wert5=$_zone"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
# If there is a record_id, delete the entry
if [ -n "$_record_id" ]; then
_info "[KAS] -> Existing records found. Now deleting old entries"
for i in $_record_id; do
_delete_RecordByID "$i"
done
else
_info "[KAS] -> No record found."
fi
if ! _contains "$response" "TRUE"; then
_err "An unkown error occurred, please check manually."
_info "[KAS] -> Creating TXT DNS record"
action="add_dns_settings"
kasReqParam="\"record_name\":\"$_record_name\""
kasReqParam="$kasReqParam,\"record_type\":\"TXT\""
kasReqParam="$kasReqParam,\"record_data\":\"$_txtvalue\""
kasReqParam="$kasReqParam,\"record_aux\":\"0\""
kasReqParam="$kasReqParam,\"zone_host\":\"$_zone\""
response="$(_callAPI "$action" "$kasReqParam")"
_debug2 "[KAS] -> Response" "$response"
if [ -z "$response" ]; then
_info "[KAS] -> Response was empty, please check manually."
return 1
elif _contains "$response" "<SOAP-ENV:Fault>"; then
faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
case "${faultstring}" in
"record_already_exists")
_info "[KAS] -> The record already exists, which must not be a problem. Please check manually."
;;
*)
_err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
return 1
;;
esac
elif ! _contains "$response" "<item><key xsi:type=\"xsd:string\">ReturnString</key><value xsi:type=\"xsd:string\">TRUE</value></item>"; then
_err "[KAS] -> An unknown error occurred, please check manually."
return 1
fi
return 0
@@ -58,45 +88,62 @@ dns_kas_add() {
dns_kas_rm() {
_fulldomain=$1
_txtvalue=$2
_info "Using DNS-01 All-inkl/Kasserver hook"
_info "Cleaning up after All-inkl/Kasserver hook"
_info "Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
_info "Check and Save Props"
_info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
_info "[KAS] -> Check and Save Props"
_check_and_save
_info "Checking Zone and Record_Name"
_info "[KAS] -> Cleaning up after All-inkl/Kasserver hook"
_info "[KAS] -> Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
_info "[KAS] -> Retriving Credential Token"
_get_credential_token
_info "[KAS] -> Checking Zone and Record_Name"
_get_zone_and_record_name "$_fulldomain"
_info "Getting Record ID"
_info "[KAS] -> Getting Record ID"
_get_record_id
_info "[KAS] -> Removing entries with ID: $_record_id"
# If there is a record_id, delete the entry
if [ -n "$_record_id" ]; then
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=delete_dns_settings"
for i in $_record_id; do
params2="$params&var1=record_id"
params2="$params2&wert1=$i"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params2")"
_debug2 "response" "$response"
if ! _contains "$response" "TRUE"; then
_err "Either the txt record is not found or another error occurred, please check manually."
return 1
fi
_delete_RecordByID "$i"
done
else # Cannot delete or unkown error
_err "No record_id found that can be deleted. Please check manually."
return 1
_info "[KAS] -> No record_id found that can be deleted. Please check manually."
fi
return 0
}
########################## PRIVATE FUNCTIONS ###########################
# Delete Record ID
_delete_RecordByID() {
recId=$1
action="delete_dns_settings"
kasReqParam="\"record_id\":\"$recId\""
response="$(_callAPI "$action" "$kasReqParam")"
_debug2 "[KAS] -> Response" "$response"
if [ -z "$response" ]; then
_info "[KAS] -> Response was empty, please check manually."
return 1
elif _contains "$response" "<SOAP-ENV:Fault>"; then
faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
case "${faultstring}" in
"record_id_not_found")
_info "[KAS] -> The record was not found, which perhaps is not a problem. Please check manually."
;;
*)
_err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
return 1
;;
esac
elif ! _contains "$response" "<item><key xsi:type=\"xsd:string\">ReturnString</key><value xsi:type=\"xsd:string\">TRUE</value></item>"; then
_err "[KAS] -> An unknown error occurred, please check manually."
return 1
fi
}
# Checks for the ENV variables and saves them
_check_and_save() {
KAS_Login="${KAS_Login:-$(_readaccountconf_mutable KAS_Login)}"
@@ -107,7 +154,7 @@ _check_and_save() {
KAS_Login=
KAS_Authtype=
KAS_Authdata=
_err "No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
_err "[KAS] -> No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
return 1
fi
_saveaccountconf_mutable KAS_Login "$KAS_Login"
@@ -119,50 +166,116 @@ _check_and_save() {
# Gets back the base domain/zone and record name.
# See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
_get_zone_and_record_name() {
params="?kas_login=$KAS_Login"
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=get_domains"
action="get_domains"
response="$(_callAPI "$action")"
_debug2 "[KAS] -> Response" "$response"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
_zonen="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "domain_name" | tr '<' '\n' | grep "domain_name" | sed "s/domain_name>=>//g")"
_domain="$1"
_temp_domain="$(echo "$1" | sed 's/\.$//')"
_rootzone="$_domain"
for i in $_zonen; do
l1=${#_rootzone}
if [ -z "$response" ]; then
_info "[KAS] -> Response was empty, please check manually."
return 1
elif _contains "$response" "<SOAP-ENV:Fault>"; then
faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
_err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
return 1
fi
zonen="$(echo "$response" | sed 's/<item>/\n/g' | sed -r 's/(.*<key xsi:type="xsd:string">domain_name<\/key><value xsi:type="xsd:string">)(.*)(<\/value.*)/\2/' | sed '/^</d')"
domain="$1"
temp_domain="$(echo "$1" | sed 's/\.$//')"
rootzone="$domain"
for i in $zonen; do
l1=${#rootzone}
l2=${#i}
if _endswith "$_domain" "$i" && [ "$l1" -ge "$l2" ]; then
_rootzone="$i"
if _endswith "$domain" "$i" && [ "$l1" -ge "$l2" ]; then
rootzone="$i"
fi
done
_zone="${_rootzone}."
_temp_record_name="$(echo "$_temp_domain" | sed "s/$_rootzone//g")"
_record_name="$(echo "$_temp_record_name" | sed 's/\.$//')"
_debug2 "Zone:" "$_zone"
_debug2 "Domain:" "$_domain"
_debug2 "Record_Name:" "$_record_name"
_zone="${rootzone}."
temp_record_name="$(echo "$temp_domain" | sed "s/$rootzone//g")"
_record_name="$(echo "$temp_record_name" | sed 's/\.$//')"
_debug "[KAS] -> Zone:" "$_zone"
_debug "[KAS] -> Domain:" "$domain"
_debug "[KAS] -> Record_Name:" "$_record_name"
return 0
}
# Retrieve the DNS record ID
_get_record_id() {
params="?kas_login=$KAS_Login"
params="$params&kas_auth_type=$KAS_Authtype"
params="$params&kas_auth_data=$KAS_Authdata"
params="$params&kas_action=get_dns_settings"
params="$params&var1=zone_host"
params="$params&wert1=$_zone"
action="get_dns_settings"
kasReqParam="\"zone_host\":\"$_zone\""
response="$(_callAPI "$action" "$kasReqParam")"
_debug2 "[KAS] -> Response" "$response"
_debug2 "Wait for 10 seconds by default before calling KAS API."
_sleep 10
response="$(_get "$KAS_Api$params")"
_debug2 "response" "$response"
_record_id="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "=>$_record_name<" | grep '>TXT<' | tr '<' '\n' | grep record_id | sed "s/record_id>=>//g")"
_debug2 _record_id "$_record_id"
if [ -z "$response" ]; then
_info "[KAS] -> Response was empty, please check manually."
return 1
elif _contains "$response" "<SOAP-ENV:Fault>"; then
faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
_err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
return 1
fi
_record_id="$(echo "$response" | tr -d '\n\r' | sed "s/<item xsi:type=\"ns2:Map\">/\n/g" | grep -i "$_record_name" | grep -i ">TXT<" | sed "s/<item><key xsi:type=\"xsd:string\">record_id<\/key><value xsi:type=\"xsd:string\">/=>/g" | grep -i "$_txtvalue" | sed "s/<\/value><\/item>/\n/g" | grep "=>" | sed "s/=>//g")"
_debug "[KAS] -> Record Id: " "$_record_id"
return 0
}
# Retrieve credential token
_get_credential_token() {
baseParamAuth="\"kas_login\":\"$KAS_Login\""
baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"$KAS_Authtype\""
baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$KAS_Authdata\""
baseParamAuth="$baseParamAuth,\"session_lifetime\":600"
baseParamAuth="$baseParamAuth,\"session_update_lifetime\":\"Y\""
data='<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:xmethodsKasApiAuthentication" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:KasAuth><Params xsi:type="xsd:string">{'
data="$data$baseParamAuth}</Params></ns1:KasAuth></SOAP-ENV:Body></SOAP-ENV:Envelope>"
_debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
_sleep $KAS_default_ratelimit
contentType="text/xml"
export _H1="SOAPAction: urn:xmethodsKasApiAuthentication#KasAuth"
response="$(_post "$data" "$KAS_Auth" "" "POST" "$contentType")"
_debug2 "[KAS] -> Response" "$response"
if [ -z "$response" ]; then
_info "[KAS] -> Response was empty, please check manually."
return 1
elif _contains "$response" "<SOAP-ENV:Fault>"; then
faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
_err "[KAS] -> Could not retrieve login token or antoher error =>$faultstring<= occurred, please check manually."
return 1
fi
_credential_token="$(echo "$response" | tr '\n' ' ' | sed 's/.*return xsi:type="xsd:string">\(.*\)<\/return>/\1/' | sed 's/<\/ns1:KasAuthResponse\(.*\)Envelope>.*//')"
_debug "[KAS] -> Credential Token: " "$_credential_token"
return 0
}
_callAPI() {
kasaction=$1
kasReqParams=$2
baseParamAuth="\"kas_login\":\"$KAS_Login\""
baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"session\""
baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$_credential_token\""
data='<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:xmethodsKasApi" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:KasApi><Params xsi:type="xsd:string">{'
data="$data$baseParamAuth,\"kas_action\":\"$kasaction\""
if [ -n "$kasReqParams" ]; then
data="$data,\"KasRequestParams\":{$kasReqParams}"
fi
data="$data}</Params></ns1:KasApi></SOAP-ENV:Body></SOAP-ENV:Envelope>"
_debug2 "[KAS] -> Request" "$data"
_debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
_sleep $KAS_default_ratelimit
contentType="text/xml"
export _H1="SOAPAction: urn:xmethodsKasApi#KasApi"
response="$(_post "$data" "$KAS_Api" "" "POST" "$contentType")"
_debug2 "[KAS] -> Response" "$response"
echo "$response"
}

View File

@@ -2,7 +2,7 @@
############################################################
# KingHost API support #
# http://api.kinghost.net/doc/ #
# https://api.kinghost.net/doc/ #
# #
# Author: Felipe Keller Braz <felipebraz@kinghost.com.br> #
# Report Bugs here: https://github.com/kinghost/acme.sh #

View File

@@ -19,8 +19,9 @@ dns_knot_add() {
_info "Adding ${fulldomain}. 60 TXT \"${txtvalue}\""
knsupdate -y "${KNOT_KEY}" <<EOF
knsupdate <<EOF
server ${KNOT_SERVER}
key ${KNOT_KEY}
zone ${_domain}.
update add ${fulldomain}. 60 TXT "${txtvalue}"
send
@@ -49,8 +50,9 @@ dns_knot_rm() {
_info "Removing ${fulldomain}. TXT"
knsupdate -y "${KNOT_KEY}" <<EOF
knsupdate <<EOF
server ${KNOT_SERVER}
key ${KNOT_KEY}
zone ${_domain}.
update del ${fulldomain}. TXT
send

147
dnsapi/dns_la.sh Normal file
View File

@@ -0,0 +1,147 @@
#!/usr/bin/env sh
#LA_Id="test123"
#LA_Key="d1j2fdo4dee3948"
LA_Api="https://api.dns.la/api"
######## Public functions #####################
#Usage: dns_la_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_la_add() {
fulldomain=$1
txtvalue=$2
LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
if [ -z "$LA_Id" ] || [ -z "$LA_Key" ]; then
LA_Id=""
LA_Key=""
_err "You didn't specify a dnsla api id and key yet."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable LA_Id "$LA_Id"
_saveaccountconf_mutable LA_Key "$LA_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"
_info "Adding record"
if _la_rest "record.ashx?cmd=create&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue&recordline="; then
if _contains "$response" '"resultid":'; then
_info "Added, OK"
return 0
elif _contains "$response" '"code":532'; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_la_rm() {
fulldomain=$1
txtvalue=$2
LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_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"
_debug "Getting txt records"
if ! _la_rest "record.ashx?cmd=listn&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue"; then
_err "Error"
return 1
fi
if ! _contains "$response" '"recordid":'; then
_info "Don't need to remove."
return 0
fi
record_id=$(printf "%s" "$response" | grep '"recordid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _la_rest "record.ashx?cmd=remove&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&recordid=$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" '"code":300'
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _la_rest "domain.ashx?cmd=get&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domain=$h"; then
return 1
fi
if _contains "$response" '"domainid":'; then
_domain_id=$(printf "%s" "$response" | grep '"domainid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
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
return 1
}
#Usage: URI
_la_rest() {
url="$LA_Api/$1"
_debug "$url"
if ! response="$(_get "$url" | tr -d ' ' | tr "}" ",")"; then
_err "Error: $url"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -3,10 +3,10 @@
#Author: Rolph Haspers <r.haspers@global.leaseweb.com>
#Utilize leaseweb.com API to finish dns-01 verifications.
#Requires a Leaseweb API Key (export LSW_Key="Your Key")
#See http://developer.leaseweb.com for more information.
#See https://developer.leaseweb.com for more information.
######## Public functions #####################
LSW_API="https://api.leaseweb.com/hosting/v2/domains/"
LSW_API="https://api.leaseweb.com/hosting/v2/domains"
#Usage: dns_leaseweb_add _acme-challenge.www.domain.com
dns_leaseweb_add() {

View File

@@ -106,6 +106,7 @@ dns_linode_v4_rm() {
#################### Private functions below ##################################
_Linode_API() {
LINODE_V4_API_KEY="${LINODE_V4_API_KEY:-$(_readaccountconf_mutable LINODE_V4_API_KEY)}"
if [ -z "$LINODE_V4_API_KEY" ]; then
LINODE_V4_API_KEY=""
@@ -115,7 +116,7 @@ _Linode_API() {
return 1
fi
_saveaccountconf LINODE_V4_API_KEY "$LINODE_V4_API_KEY"
_saveaccountconf_mutable LINODE_V4_API_KEY "$LINODE_V4_API_KEY"
}
#################### Private functions below ##################################

View File

@@ -32,8 +32,12 @@ dns_loopia_add() {
_info "Adding record"
_loopia_add_sub_domain "$_domain" "$_sub_domain"
_loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"
if ! _loopia_add_sub_domain "$_domain" "$_sub_domain"; then
return 1
fi
if ! _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
return 1
fi
}
@@ -70,12 +74,13 @@ dns_loopia_rm() {
<value><string>%s</string></value>
</param>
</params>
</methodCall>' "$LOOPIA_User" "$LOOPIA_Password" "$_domain" "$_sub_domain")
</methodCall>' "$LOOPIA_User" "$Encoded_Password" "$_domain" "$_sub_domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "OK"; then
_err "Error could not get txt records"
err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
_err "Error could not get txt records: $err_response"
return 1
fi
}
@@ -101,6 +106,12 @@ _loopia_load_config() {
return 1
fi
if _contains "$LOOPIA_Password" "'" || _contains "$LOOPIA_Password" '"'; then
_err "Password contains a quotation mark or double quotation marks and this is not supported by dns_loopia.sh"
return 1
fi
Encoded_Password=$(_xml_encode "$LOOPIA_Password")
return 0
}
@@ -133,11 +144,12 @@ _loopia_get_records() {
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
</methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "<array>"; then
_err "Error"
err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
_err "Error: $err_response"
return 1
fi
return 0
@@ -162,7 +174,7 @@ _get_root() {
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password)
</methodCall>' "$LOOPIA_User" "$Encoded_Password")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
while true; do
@@ -206,32 +218,35 @@ _loopia_add_record() {
<value><string>%s</string></value>
</param>
<param>
<struct>
<member>
<name>type</name>
<value><string>TXT</string></value>
</member>
<member>
<name>priority</name>
<value><int>0</int></value>
</member>
<member>
<name>ttl</name>
<value><int>300</int></value>
</member>
<member>
<name>rdata</name>
<value><string>%s</string></value>
</member>
</struct>
<value>
<struct>
<member>
<name>type</name>
<value><string>TXT</string></value>
</member>
<member>
<name>priority</name>
<value><int>0</int></value>
</member>
<member>
<name>ttl</name>
<value><int>300</int></value>
</member>
<member>
<name>rdata</name>
<value><string>%s</string></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain" "$txtval")
</methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain" "$txtval")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "OK"; then
_err "Error"
err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
_err "Error: $err_response"
return 1
fi
return 0
@@ -255,7 +270,7 @@ _sub_domain_exists() {
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain")
</methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
@@ -290,13 +305,22 @@ _loopia_add_sub_domain() {
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
</methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "OK"; then
_err "Error"
err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
_err "Error: $err_response"
return 1
fi
return 0
}
_xml_encode() {
encoded_string=$1
encoded_string=$(echo "$encoded_string" | sed 's/&/\&amp;/')
encoded_string=$(echo "$encoded_string" | sed 's/</\&lt;/')
encoded_string=$(echo "$encoded_string" | sed 's/>/\&gt;/')
printf "%s" "$encoded_string"
}

View File

@@ -163,6 +163,7 @@ _retrieve_miab_env() {
_saveaccountconf_mutable MIAB_Username "$MIAB_Username"
_saveaccountconf_mutable MIAB_Password "$MIAB_Password"
_saveaccountconf_mutable MIAB_Server "$MIAB_Server"
return 0
}
#Useage: _miab_rest "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" "custom/_acme-challenge.www.domain.com/txt "POST"

View File

@@ -74,7 +74,7 @@ dns_mydevil_rm() {
validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$"
for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do
_info "Removing record $id from domain $domain"
devil dns del "$domain" "$id" || _err "Could not remove DNS record."
echo "y" | devil dns del "$domain" "$id" || _err "Could not remove DNS record."
done
}
@@ -87,7 +87,9 @@ mydevil_get_domain() {
domain=""
for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do
_debug "Checking domain: $domain"
if _endswith "$fulldomain" "$domain"; then
_debug "Fulldomain '$fulldomain' matches '$domain'"
printf -- "%s" "$domain"
return 0
fi

View File

@@ -150,7 +150,7 @@ _get_root() {
_mydnsjp_retrieve_domain() {
_debug "Login to MyDNS.JP"
response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")"
response="$(_post "MENU=100&masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/members/")"
cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
# If cookies is not empty then logon successful
@@ -159,22 +159,8 @@ _mydnsjp_retrieve_domain() {
return 1
fi
_debug "Retrieve DOMAIN INFO page"
export _H1="Cookie:${cookie}"
response="$(_get "$MYDNSJP_API/?MENU=300")"
if [ "$?" != "0" ]; then
_err "Fail to retrieve DOMAIN INFO."
return 1
fi
_root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/')
# Logout
response="$(_get "$MYDNSJP_API/?MENU=090")"
_debug _root_domain "$_root_domain"
if [ -z "$_root_domain" ]; then

261
dnsapi/dns_mythic_beasts.sh Executable file
View File

@@ -0,0 +1,261 @@
#!/usr/bin/env sh
# Mythic Beasts is a long-standing UK service provider using standards-based OAuth2 authentication
# To test: ./acme.sh --dns dns_mythic_beasts --test --debug 1 --output-insecure --issue --domain domain.com
# Cannot retest once cert is issued
# OAuth2 tokens only valid for 300 seconds so we do not store
# NOTE: This will remove all TXT records matching the fulldomain, not just the added ones (_acme-challenge.www.domain.com)
# Test OAuth2 credentials
#MB_AK="aaaaaaaaaaaaaaaa"
#MB_AS="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
# URLs
MB_API='https://api.mythic-beasts.com/dns/v2/zones'
MB_AUTH='https://auth.mythic-beasts.com/login'
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_mythic_beasts_add() {
fulldomain=$1
txtvalue=$2
_info "MYTHIC BEASTS Adding record $fulldomain = $txtvalue"
if ! _initAuth; then
return 1
fi
if ! _get_root "$fulldomain"; then
return 1
fi
# method path body_data
if _mb_rest POST "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then
if _contains "$response" "1 records added"; then
_info "Added, verifying..."
# Max 120 seconds to publish
for i in $(seq 1 6); do
# Retry on error
if ! _mb_rest GET "$_domain/records/$_sub_domain/TXT?verify"; then
_sleep 20
else
_info "Record published!"
return 0
fi
done
else
_err "\n$response"
fi
fi
_err "Add txt record error."
return 1
}
#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_mythic_beasts_rm() {
fulldomain=$1
txtvalue=$2
_info "MYTHIC BEASTS Removing record $fulldomain = $txtvalue"
if ! _initAuth; then
return 1
fi
if ! _get_root "$fulldomain"; then
return 1
fi
# method path body_data
if _mb_rest DELETE "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then
_info "Record removed"
return 0
fi
_err "Remove txt record error."
return 1
}
#################### Private functions below ##################################
#Possible formats:
# _acme-challenge.www.example.com
# _acme-challenge.example.com
# _acme-challenge.example.co.uk
# _acme-challenge.www.example.co.uk
# _acme-challenge.sub1.sub2.www.example.co.uk
# sub1.sub2.example.co.uk
# example.com
# example.co.uk
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
p=1
_debug "Detect the root zone"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
_err "Domain exhausted"
return 1
fi
# Use the status errors to find the domain, continue on 403 Access denied
# method path body_data
_mb_rest GET "$h/records"
ret="$?"
if [ "$ret" -eq 0 ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
return 0
elif [ "$ret" -eq 1 ]; then
return 1
fi
p=$i
i=$(_math "$i" + 1)
if [ "$i" -gt 50 ]; then
break
fi
done
_err "Domain too long"
return 1
}
_initAuth() {
MB_AK="${MB_AK:-$(_readaccountconf_mutable MB_AK)}"
MB_AS="${MB_AS:-$(_readaccountconf_mutable MB_AS)}"
if [ -z "$MB_AK" ] || [ -z "$MB_AS" ]; then
MB_AK=""
MB_AS=""
_err "Please specify an OAuth2 Key & Secret"
return 1
fi
_saveaccountconf_mutable MB_AK "$MB_AK"
_saveaccountconf_mutable MB_AS "$MB_AS"
if ! _oauth2; then
return 1
fi
_info "Checking authentication"
_secure_debug access_token "$MB_TK"
_sleep 1
# GET a list of zones
# method path body_data
if ! _mb_rest GET ""; then
_err "The token is invalid"
return 1
fi
_info "Token OK"
return 0
}
# Github appears to use an outbound proxy for requests which means subsequent requests may not have the same
# source IP. The standard Mythic Beasts OAuth2 tokens are tied to an IP, meaning github test requests fail
# authentication. This is a work around using an undocumented MB API to obtain a token not tied to an
# IP just for the github tests.
_oauth2() {
if [ "$GITHUB_ACTIONS" = "true" ]; then
_oauth2_github
else
_oauth2_std
fi
return $?
}
_oauth2_std() {
# HTTP Basic Authentication
_H1="Authorization: Basic $(echo "$MB_AK:$MB_AS" | _base64)"
_H2="Accepts: application/json"
export _H1 _H2
body="grant_type=client_credentials"
_info "Getting OAuth2 token..."
# body url [needbase64] [POST|PUT|DELETE] [ContentType]
response="$(_post "$body" "$MB_AUTH" "" "POST" "application/x-www-form-urlencoded")"
if _contains "$response" "\"token_type\":\"bearer\""; then
MB_TK="$(echo "$response" | _egrep_o "access_token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
if [ -z "$MB_TK" ]; then
_err "Unable to get access_token"
_err "\n$response"
return 1
fi
else
_err "OAuth2 token_type not Bearer"
_err "\n$response"
return 1
fi
_debug2 response "$response"
return 0
}
_oauth2_github() {
_H1="Accepts: application/json"
export _H1
body="{\"login\":{\"handle\":\"$MB_AK\",\"pass\":\"$MB_AS\",\"floating\":1}}"
_info "Getting Floating token..."
# body url [needbase64] [POST|PUT|DELETE] [ContentType]
response="$(_post "$body" "$MB_AUTH" "" "POST" "application/json")"
MB_TK="$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
if [ -z "$MB_TK" ]; then
_err "Unable to get token"
_err "\n$response"
return 1
fi
_debug2 response "$response"
return 0
}
# method path body_data
_mb_rest() {
# URL encoded body for single API operations
m="$1"
ep="$2"
data="$3"
if [ -z "$ep" ]; then
_mb_url="$MB_API"
else
_mb_url="$MB_API/$ep"
fi
_H1="Authorization: Bearer $MB_TK"
_H2="Accepts: application/json"
export _H1 _H2
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
# body url [needbase64] [POST|PUT|DELETE] [ContentType]
response="$(_post "data=$data" "$_mb_url" "" "$m" "application/x-www-form-urlencoded")"
else
response="$(_get "$_mb_url")"
fi
if [ "$?" != "0" ]; then
_err "Request error"
return 1
fi
header="$(cat "$HTTP_HEADER")"
status="$(echo "$header" | _egrep_o "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n")"
code="$(echo "$status" | _egrep_o "^[0-9]*")"
if [ "$code" -ge 400 ] || _contains "$response" "\"error\"" || _contains "$response" "invalid_client"; then
_err "error $status"
_err "\n$response"
_debug "\n$header"
return 2
fi
_debug2 response "$response"
return 0
}

View File

@@ -82,7 +82,7 @@ _get_root() {
_debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
# The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
# user is not the owner, but still has administrative rights, we must query the getHosts api directly.
# See this comment and the official namecheap response: http://disq.us/p/1q6v9x9
# See this comment and the official namecheap response: https://disq.us/p/1q6v9x9
if ! _get_root_by_getHosts "$fulldomain"; then
return 1
fi
@@ -157,7 +157,7 @@ _namecheap_set_publicip() {
if [ -z "$NAMECHEAP_SOURCEIP" ]; then
_err "No Source IP specified for Namecheap API."
_err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
_err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
return 1
else
_saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
@@ -175,7 +175,7 @@ _namecheap_set_publicip() {
_publicip=$(_get "$addr")
else
_err "No Source IP specified for Namecheap API."
_err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
_err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
return 1
fi
fi
@@ -208,7 +208,7 @@ _namecheap_parse_host() {
_hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
_hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
_hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
_hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2)
_hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2 | _xml_decode)
_hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
_hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
@@ -259,7 +259,7 @@ _set_namecheap_TXT() {
_debug hosts "$hosts"
if [ -z "$hosts" ]; then
_error "Hosts not found"
_err "Hosts not found"
return 1
fi
@@ -313,7 +313,7 @@ _del_namecheap_TXT() {
_debug hosts "$hosts"
if [ -z "$hosts" ]; then
_error "Hosts not found"
_err "Hosts not found"
return 1
fi
@@ -405,3 +405,7 @@ _namecheap_set_tld_sld() {
done
}
_xml_decode() {
sed 's/&quot;/"/g'
}

View File

@@ -110,7 +110,7 @@ _get_root() {
return 1
fi
if _contains "$response" "<domain>$host"; then
if _contains "$response" ">$host</domain>"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$host"
return 0

59
dnsapi/dns_nanelo.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env sh
# Official DNS API for Nanelo.com
# Provide the required API Key like this:
# NANELO_TOKEN="FmD408PdqT1E269gUK57"
NANELO_API="https://api.nanelo.com/v1/"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_nanelo_add() {
fulldomain=$1
txtvalue=$2
NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
if [ -z "$NANELO_TOKEN" ]; then
NANELO_TOKEN=""
_err "You didn't configure a Nanelo API Key yet."
_err "Please set NANELO_TOKEN and try again."
_err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
return 1
fi
_saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
_info "Adding TXT record to ${fulldomain}"
response="$(_get "$NANELO_API$NANELO_TOKEN/dns/addrecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not create resource record, please check the logs"
_err "${response}"
return 1
}
dns_nanelo_rm() {
fulldomain=$1
txtvalue=$2
NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
if [ -z "$NANELO_TOKEN" ]; then
NANELO_TOKEN=""
_err "You didn't configure a Nanelo API Key yet."
_err "Please set NANELO_TOKEN and try again."
_err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
return 1
fi
_saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
_info "Deleting resource record $fulldomain"
response="$(_get "$NANELO_API$NANELO_TOKEN/dns/deleterecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not delete resource record, please check the logs"
_err "${response}"
return 1
}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env sh
#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghjk"
#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghj"
NederHost_Api="https://api.nederhost.nl/dns/v1"
@@ -112,12 +112,8 @@ _nederhost_rest() {
export _H1="Authorization: Bearer $NederHost_Key"
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
else
response="$(_get "$NederHost_Api/$ep")"
fi
_debug data "$data"
response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"

View File

@@ -119,16 +119,16 @@ login() {
tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4)
_debug "$tmp"
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
if [ "$(_getfield "$tmp" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$tmp"
return 1
fi
}
logout() {
tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
_debug "$tmp"
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
if [ "$(_getfield "$tmp" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$tmp"
return 1
fi
}

View File

@@ -18,15 +18,15 @@ dns_netlify_add() {
NETLIFY_ACCESS_TOKEN=""
_err "Please specify your Netlify Access Token and try again."
return 1
else
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
fi
_info "Using Netlify"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
if ! _get_root "$fulldomain" "$accesstoken"; then
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
@@ -62,9 +62,9 @@ dns_netlify_rm() {
_debug txtdomain "$txtdomain"
_debug txt "$txt"
_saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
if ! _get_root "$txtdomain" "$accesstoken"; then
if ! _get_root "$txtdomain"; then
_err "invalid domain"
return 1
fi
@@ -114,7 +114,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h" | cut -d , -f 1 | tr -d \")
_domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h\"" | cut -d , -f 1 | tr -d \")
if [ "$_domain_id" ]; then
if [ "$i" = 1 ]; then
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias

View File

@@ -51,7 +51,7 @@ dns_nsd_rm() {
Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}"
Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}"
sed -i "/$fulldomain. $ttlvalue IN TXT \"$txtvalue\"/d" "$Nsd_ZoneFile"
_sed_i "/$fulldomain. $ttlvalue IN TXT \"$txtvalue\"/d" "$Nsd_ZoneFile"
_info "Removed TXT record for $fulldomain"
_debug "Running $Nsd_Command"
if eval "$Nsd_Command"; then

325
dnsapi/dns_oci.sh Normal file
View File

@@ -0,0 +1,325 @@
#!/usr/bin/env sh
#
# Acme.sh DNS API plugin for Oracle Cloud Infrastructure
# Copyright (c) 2021, Oracle and/or its affiliates
#
# The plugin will automatically use the default profile from an OCI SDK and CLI
# configuration file, if it exists.
#
# Alternatively, set the following environment variables:
# - OCI_CLI_TENANCY : OCID of tenancy that contains the target DNS zone
# - OCI_CLI_USER : OCID of user with permission to add/remove records from zones
# - OCI_CLI_REGION : Should point to the tenancy home region
#
# One of the following two variables is required:
# - OCI_CLI_KEY_FILE: Path to private API signing key file in PEM format; or
# - OCI_CLI_KEY : The private API signing key in PEM format
#
# NOTE: using an encrypted private key that needs a passphrase is not supported.
#
dns_oci_add() {
_fqdn="$1"
_rdata="$2"
if _get_oci_zone; then
_add_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"ttl\": 30,\"operation\":\"ADD\"}]}"
response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_add_record_body")
if [ "$response" ]; then
_info "Success: added TXT record for ${_sub_domain}.${_domain}."
else
_err "Error: failed to add TXT record for ${_sub_domain}.${_domain}."
_err "Check that the user has permission to add records to this zone."
return 1
fi
else
return 1
fi
}
dns_oci_rm() {
_fqdn="$1"
_rdata="$2"
if _get_oci_zone; then
_remove_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"operation\":\"REMOVE\"}]}"
response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_remove_record_body")
if [ "$response" ]; then
_info "Success: removed TXT record for ${_sub_domain}.${_domain}."
else
_err "Error: failed to remove TXT record for ${_sub_domain}.${_domain}."
_err "Check that the user has permission to remove records from this zone."
return 1
fi
else
return 1
fi
}
#################### Private functions below ##################################
_get_oci_zone() {
if ! _oci_config; then
return 1
fi
if ! _get_zone "$_fqdn"; then
_err "Error: DNS Zone not found for $_fqdn in $OCI_CLI_TENANCY"
return 1
fi
return 0
}
_oci_config() {
_DEFAULT_OCI_CLI_CONFIG_FILE="$HOME/.oci/config"
OCI_CLI_CONFIG_FILE="${OCI_CLI_CONFIG_FILE:-$(_readaccountconf_mutable OCI_CLI_CONFIG_FILE)}"
if [ -z "$OCI_CLI_CONFIG_FILE" ]; then
OCI_CLI_CONFIG_FILE="$_DEFAULT_OCI_CLI_CONFIG_FILE"
fi
if [ "$_DEFAULT_OCI_CLI_CONFIG_FILE" != "$OCI_CLI_CONFIG_FILE" ]; then
_saveaccountconf_mutable OCI_CLI_CONFIG_FILE "$OCI_CLI_CONFIG_FILE"
else
_clearaccountconf_mutable OCI_CLI_CONFIG_FILE
fi
_DEFAULT_OCI_CLI_PROFILE="DEFAULT"
OCI_CLI_PROFILE="${OCI_CLI_PROFILE:-$(_readaccountconf_mutable OCI_CLI_PROFILE)}"
if [ "$_DEFAULT_OCI_CLI_PROFILE" != "$OCI_CLI_PROFILE" ]; then
_saveaccountconf_mutable OCI_CLI_PROFILE "$OCI_CLI_PROFILE"
else
OCI_CLI_PROFILE="$_DEFAULT_OCI_CLI_PROFILE"
_clearaccountconf_mutable OCI_CLI_PROFILE
fi
OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readaccountconf_mutable OCI_CLI_TENANCY)}"
if [ "$OCI_CLI_TENANCY" ]; then
_saveaccountconf_mutable OCI_CLI_TENANCY "$OCI_CLI_TENANCY"
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
_debug "Reading OCI_CLI_TENANCY value from: $OCI_CLI_CONFIG_FILE"
OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readini "$OCI_CLI_CONFIG_FILE" tenancy "$OCI_CLI_PROFILE")}"
fi
if [ -z "$OCI_CLI_TENANCY" ]; then
_err "Error: unable to read OCI_CLI_TENANCY from config file or environment variable."
return 1
fi
OCI_CLI_USER="${OCI_CLI_USER:-$(_readaccountconf_mutable OCI_CLI_USER)}"
if [ "$OCI_CLI_USER" ]; then
_saveaccountconf_mutable OCI_CLI_USER "$OCI_CLI_USER"
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
_debug "Reading OCI_CLI_USER value from: $OCI_CLI_CONFIG_FILE"
OCI_CLI_USER="${OCI_CLI_USER:-$(_readini "$OCI_CLI_CONFIG_FILE" user "$OCI_CLI_PROFILE")}"
fi
if [ -z "$OCI_CLI_USER" ]; then
_err "Error: unable to read OCI_CLI_USER from config file or environment variable."
return 1
fi
OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readaccountconf_mutable OCI_CLI_REGION)}"
if [ "$OCI_CLI_REGION" ]; then
_saveaccountconf_mutable OCI_CLI_REGION "$OCI_CLI_REGION"
elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
_debug "Reading OCI_CLI_REGION value from: $OCI_CLI_CONFIG_FILE"
OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readini "$OCI_CLI_CONFIG_FILE" region "$OCI_CLI_PROFILE")}"
fi
if [ -z "$OCI_CLI_REGION" ]; then
_err "Error: unable to read OCI_CLI_REGION from config file or environment variable."
return 1
fi
OCI_CLI_KEY="${OCI_CLI_KEY:-$(_readaccountconf_mutable OCI_CLI_KEY)}"
if [ -z "$OCI_CLI_KEY" ]; then
_clearaccountconf_mutable OCI_CLI_KEY
OCI_CLI_KEY_FILE="${OCI_CLI_KEY_FILE:-$(_readini "$OCI_CLI_CONFIG_FILE" key_file "$OCI_CLI_PROFILE")}"
if [ "$OCI_CLI_KEY_FILE" ] && [ -f "$OCI_CLI_KEY_FILE" ]; then
_debug "Reading OCI_CLI_KEY value from: $OCI_CLI_KEY_FILE"
OCI_CLI_KEY=$(_base64 <"$OCI_CLI_KEY_FILE")
_saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
fi
else
_saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
fi
if [ -z "$OCI_CLI_KEY_FILE" ] && [ -z "$OCI_CLI_KEY" ]; then
_err "Error: unable to find key file path in OCI config file or OCI_CLI_KEY_FILE."
_err "Error: unable to load private API signing key from OCI_CLI_KEY."
return 1
fi
if [ "$(printf "%s\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then
OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64)
fi
return 0
}
# _get_zone(): retrieves the Zone name and OCID
#
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_ociid=ocid1.dns-zone.oc1..
_get_zone() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
# not valid
return 1
fi
_domain_id=$(_signed_request "GET" "/20180115/zones/$h" "" "id")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
#Usage: privatekey
#Output MD5 fingerprint
_fingerprint() {
pkey="$1"
if [ -z "$pkey" ]; then
_usage "Usage: _fingerprint privkey"
return 1
fi
printf "%s" "$pkey" | ${ACME_OPENSSL_BIN:-openssl} rsa -pubout -outform DER 2>/dev/null | ${ACME_OPENSSL_BIN:-openssl} md5 -c | cut -d = -f 2 | tr -d ' '
}
_signed_request() {
_sig_method="$1"
_sig_target="$2"
_sig_body="$3"
_return_field="$4"
_key_fingerprint=$(_fingerprint "$OCI_CLI_KEY")
_sig_host="dns.$OCI_CLI_REGION.oraclecloud.com"
_sig_keyId="$OCI_CLI_TENANCY/$OCI_CLI_USER/$_key_fingerprint"
_sig_alg="rsa-sha256"
_sig_version="1"
_sig_now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")"
_request_method=$(printf %s "$_sig_method" | _lower_case)
_curl_method=$(printf %s "$_sig_method" | _upper_case)
_request_target="(request-target): $_request_method $_sig_target"
_date_header="date: $_sig_now"
_host_header="host: $_sig_host"
_string_to_sign="$_request_target\n$_date_header\n$_host_header"
_sig_headers="(request-target) date host"
if [ "$_sig_body" ]; then
_secure_debug3 _sig_body "$_sig_body"
_sig_body_sha256="x-content-sha256: $(printf %s "$_sig_body" | _digest sha256)"
_sig_body_type="content-type: application/json"
_sig_body_length="content-length: ${#_sig_body}"
_string_to_sign="$_string_to_sign\n$_sig_body_sha256\n$_sig_body_type\n$_sig_body_length"
_sig_headers="$_sig_headers x-content-sha256 content-type content-length"
fi
_tmp_file=$(_mktemp)
if [ -f "$_tmp_file" ]; then
printf '%s' "$OCI_CLI_KEY" >"$_tmp_file"
_signature=$(printf '%b' "$_string_to_sign" | _sign "$_tmp_file" sha256 | tr -d '\r\n')
rm -f "$_tmp_file"
fi
_signed_header="Authorization: Signature version=\"$_sig_version\",keyId=\"$_sig_keyId\",algorithm=\"$_sig_alg\",headers=\"$_sig_headers\",signature=\"$_signature\""
_secure_debug3 _signed_header "$_signed_header"
if [ "$_curl_method" = "GET" ]; then
export _H1="$_date_header"
export _H2="$_signed_header"
_response="$(_get "https://${_sig_host}${_sig_target}")"
elif [ "$_curl_method" = "PATCH" ]; then
export _H1="$_date_header"
# shellcheck disable=SC2090
export _H2="$_sig_body_sha256"
export _H3="$_sig_body_type"
export _H4="$_sig_body_length"
export _H5="$_signed_header"
_response="$(_post "$_sig_body" "https://${_sig_host}${_sig_target}" "" "PATCH")"
else
_err "Unable to process method: $_curl_method."
fi
_ret="$?"
if [ "$_return_field" ]; then
_response="$(echo "$_response" | sed 's/\\\"//g'))"
_return=$(echo "${_response}" | _egrep_o "\"$_return_field\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
else
_return="$_response"
fi
printf "%s" "$_return"
return $_ret
}
# file key [section]
_readini() {
_file="$1"
_key="$2"
_section="${3:-DEFAULT}"
_start_n=$(grep -n '\['"$_section"']' "$_file" | cut -d : -f 1)
_debug3 _start_n "$_start_n"
if [ -z "$_start_n" ]; then
_err "Can not find section: $_section"
return 1
fi
_start_nn=$(_math "$_start_n" + 1)
_debug3 "_start_nn" "$_start_nn"
_left="$(sed -n "${_start_nn},99999p" "$_file")"
_debug3 _left "$_left"
_end="$(echo "$_left" | grep -n "^\[" | _head_n 1)"
_debug3 "_end" "$_end"
if [ "$_end" ]; then
_end_n=$(echo "$_end" | cut -d : -f 1)
_debug3 "_end_n" "$_end_n"
_seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
else
_seg_n="$_left"
fi
_debug3 "_seg_n" "$_seg_n"
_lineini="$(echo "$_seg_n" | grep "^ *$_key *= *")"
_inivalue="$(printf "%b" "$(eval "echo $_lineini | sed \"s/^ *${_key} *= *//g\"")")"
_debug2 _inivalue "$_inivalue"
echo "$_inivalue"
}

View File

@@ -1,22 +1,9 @@
#!/usr/bin/env sh
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
# one.com ui wrapper for acme.sh
# Author: github: @diseq
# Created: 2019-02-17
# Fixed by: @der-berni
# Modified: 2020-04-07
#
# Use ONECOM_KeepCnameProxy to keep the CNAME DNS record
# export ONECOM_KeepCnameProxy="1"
#
# export ONECOM_User="username"
# export ONECOM_Password="password"
#
# Usage:
# acme.sh --issue --dns dns_one -d example.com
#
# only single domain supported atm
dns_one_add() {
fulldomain=$1
@@ -36,27 +23,9 @@ dns_one_add() {
subdomain="${_sub_domain}"
maindomain=${_domain}
useProxy=0
if [ "${_sub_domain}" = "_acme-challenge" ]; then
subdomain="proxy${_sub_domain}"
useProxy=1
fi
_debug subdomain "$subdomain"
_debug maindomain "$maindomain"
if [ $useProxy -eq 1 ]; then
#Check if the CNAME exists
_dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
if [ -z "$id" ]; then
_info "$(__red "Add CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
_dns_one_addrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
_info "Not valid yet, let's wait 1 hour to take effect."
_sleep 3600
fi
fi
#Check if the TXT exists
_dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
if [ -n "$id" ]; then
@@ -92,26 +61,8 @@ dns_one_rm() {
subdomain="${_sub_domain}"
maindomain=${_domain}
useProxy=0
if [ "${_sub_domain}" = "_acme-challenge" ]; then
subdomain="proxy${_sub_domain}"
useProxy=1
fi
_debug subdomain "$subdomain"
_debug maindomain "$maindomain"
if [ $useProxy -eq 1 ]; then
if [ "$ONECOM_KeepCnameProxy" = "1" ]; then
_info "$(__red "Keeping CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
else
#Check if the CNAME exists
_dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
if [ -n "$id" ]; then
_info "$(__red "Removing CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
_dns_one_delrecord "$id"
fi
fi
fi
#Check if the TXT exists
_dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
@@ -136,7 +87,7 @@ dns_one_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)
@@ -163,8 +114,6 @@ _get_root() {
_dns_one_login() {
# get credentials
ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-$(_readaccountconf_mutable ONECOM_KeepCnameProxy)}"
ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-0}"
ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}"
ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}"
if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then
@@ -176,7 +125,6 @@ _dns_one_login() {
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable ONECOM_KeepCnameProxy "$ONECOM_KeepCnameProxy"
_saveaccountconf_mutable ONECOM_User "$ONECOM_User"
_saveaccountconf_mutable ONECOM_Password "$ONECOM_Password"

View File

@@ -57,16 +57,16 @@ _dns_openstack_create_recordset() {
if [ -z "$_recordset_id" ]; then
_info "Creating a new recordset"
if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record "$txtvalue" "$_zone_id" "$fulldomain."); then
if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
_err "No recordset ID found after create"
return 1
fi
else
_info "Updating existing recordset"
# Build new list of --record <rec> args for update
_record_args="--record $txtvalue"
# Build new list of --record=<rec> args for update
_record_args="--record=$txtvalue"
for _rec in $_records; do
_record_args="$_record_args --record $_rec"
_record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
@@ -107,13 +107,13 @@ _dns_openstack_delete_recordset() {
fi
else
_info "Found existing records, updating recordset"
# Build new list of --record <rec> args for update
# Build new list of --record=<rec> args for update
_record_args=""
for _rec in $_records; do
if [ "$_rec" = "$txtvalue" ]; then
continue
fi
_record_args="$_record_args --record $_rec"
_record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then

View File

@@ -137,7 +137,7 @@ _get_root() {
domain=$1
i=2
p=1
if _opns_rest "GET" "/domain/get"; then
if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
_domain_response="$response"
else
return 1
@@ -150,8 +150,7 @@ _get_root() {
return 1
fi
_debug h "$h"
id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\"(,\"allownotifyslave\":{\"\":{[^}]*}},|,)\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-$p)

View File

@@ -14,6 +14,9 @@
#'ovh-eu'
OVH_EU='https://eu.api.ovh.com/1.0'
#'ovh-us'
OVH_US='https://api.us.ovhcloud.com/1.0'
#'ovh-ca':
OVH_CA='https://ca.api.ovh.com/1.0'
@@ -29,9 +32,6 @@ SYS_EU='https://eu.api.soyoustart.com/1.0'
#'soyoustart-ca'
SYS_CA='https://ca.api.soyoustart.com/1.0'
#'runabove-ca'
RAV_CA='https://api.runabove.com/1.0'
wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
@@ -45,6 +45,10 @@ _ovh_get_api() {
printf "%s" $OVH_EU
return
;;
ovh-us | ovhus)
printf "%s" $OVH_US
return
;;
ovh-ca | ovhca)
printf "%s" $OVH_CA
return
@@ -65,14 +69,15 @@ _ovh_get_api() {
printf "%s" $SYS_CA
return
;;
runabove-ca | runaboveca)
printf "%s" $RAV_CA
# raw API url starts with https://
https*)
printf "%s" "$1"
return
;;
*)
_err "Unknown parameter : $1"
_err "Unknown endpoint : $1"
return 1
;;
esac
@@ -92,7 +97,7 @@ _initAuth() {
if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then
_info "It seems that your ovh key is changed, let's clear consumer key first."
_clearaccountconf OVH_CK
_clearaccountconf_mutable OVH_CK
fi
_saveaccountconf_mutable OVH_AK "$OVH_AK"
_saveaccountconf_mutable OVH_AS "$OVH_AS"
@@ -118,13 +123,14 @@ _initAuth() {
#return and wait for retry.
return 1
fi
_saveaccountconf_mutable OVH_CK "$OVH_CK"
_info "Checking authentication"
if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL" || _contains "$response" "NOT_CREDENTIAL"; then
_err "The consumer key is invalid: $OVH_CK"
_err "Please retry to create a new one."
_clearaccountconf OVH_CK
_clearaccountconf_mutable OVH_CK
return 1
fi
_info "Consumer key is ok."
@@ -198,6 +204,8 @@ dns_ovh_rm() {
if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then
return 1
fi
_ovh_rest POST "domain/zone/$_domain/refresh"
_debug "Refresh:$response"
return 0
fi
done
@@ -233,8 +241,7 @@ _ovh_authentication() {
_secure_debug consumerKey "$consumerKey"
OVH_CK="$consumerKey"
_saveaccountconf OVH_CK "$OVH_CK"
_saveaccountconf_mutable OVH_CK "$OVH_CK"
_info "Please open this link to do authentication: $(__green "$validationUrl")"
_info "Here is a guide for you: $(__green "$wiki")"
@@ -261,7 +268,9 @@ _get_root() {
return 1
fi
if ! _contains "$response" "This service does not exist" >/dev/null && ! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
if ! _contains "$response" "This service does not exist" >/dev/null &&
! _contains "$response" "This call has not been granted" >/dev/null &&
! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0

View File

@@ -103,7 +103,7 @@ set_record() {
_build_record_string "$oldchallenge"
done
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}"; then
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then
_err "Set txt record error."
return 1
fi
@@ -126,7 +126,7 @@ rm_record() {
if _contains "$_existing_challenges" "$txtvalue"; then
#Delete all challenges (PowerDNS API does not allow to delete content)
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}" "application/json"; then
_err "Delete txt record error."
return 1
fi
@@ -140,7 +140,7 @@ rm_record() {
fi
done
#Recreate the existing challenges
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}"; then
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then
_err "Set txt record error."
return 1
fi
@@ -175,13 +175,13 @@ _get_root() {
i=1
if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then
_zones_response="$response"
_zones_response=$(echo "$response" | _normalizeJson)
fi
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if _contains "$_zones_response" "\"name\": \"$h.\""; then
if _contains "$_zones_response" "\"name\":\"$h.\""; then
_domain="$h."
if [ -z "$h" ]; then
_domain="=2E"
@@ -203,12 +203,13 @@ _pdns_rest() {
method=$1
ep=$2
data=$3
ct=$4
export _H1="X-API-Key: $PDNS_Token"
if [ ! "$method" = "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$PDNS_Url$ep" "" "$method")"
response="$(_post "$data" "$PDNS_Url$ep" "" "$method" "$ct")"
else
response="$(_get "$PDNS_Url$ep")"
fi

View File

@@ -41,7 +41,7 @@ pleskxml_init_checks_done=0
NEWLINE='\
'
pleskxml_tplt_get_domains="<packet><customer><get-domain-list><filter/></get-domain-list></customer></packet>"
pleskxml_tplt_get_domains="<packet><webspace><get><filter/><dataset><gen_info/></dataset></get></webspace></packet>"
# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
# Also used to test credentials and URI.
# No params.
@@ -145,22 +145,25 @@ dns_pleskxml_rm() {
)"
if [ -z "$reclist" ]; then
_err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
_err "No TXT records found for root domain $fulldomain (Plesk domain ID ${root_domain_id}). Exiting."
return 1
fi
_debug "Got list of DNS TXT records for root domain '$root_domain_name':"
_debug "Got list of DNS TXT records for root Plesk domain ID ${root_domain_id} of root domain $fulldomain:"
_debug "$reclist"
# Extracting the id of the TXT record for the full domain (NOT case-sensitive) and corresponding value
recid="$(
_value "$reclist" |
grep "<host>${fulldomain}.</host>" |
grep -i "<host>${fulldomain}.</host>" |
grep "<value>${txtvalue}</value>" |
sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/'
)"
_debug "Got id from line: $recid"
if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
_err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
_err "DNS records for root domain '${fulldomain}.' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
_err "Cannot delete TXT record. Exiting."
return 1
fi
@@ -251,9 +254,12 @@ _call_api() {
# Detect any <status> that isn't "ok". None of the used calls should fail if the API is working correctly.
# Also detect if there simply aren't any status lines (null result?) and report that, as well.
# Remove <data></data> structure from result string, since it might contain <status> values that are related to the status of the domain and not to the API request
statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>[^<]*</status> *$')"
statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>ok</status> *$')"
statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | sed '/<data>/,/<\/data>/d' | grep -c '^ *<status>[^<]*</status> *$')"
statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | sed '/<data>/,/<\/data>/d' | grep -c '^ *<status>ok</status> *$')"
_debug "statuslines_count_total=$statuslines_count_total."
_debug "statuslines_count_okay=$statuslines_count_okay."
if [ -z "$statuslines_count_total" ]; then
@@ -375,7 +381,7 @@ _pleskxml_get_root_domain() {
# Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
# We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' '<type>domain</type>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
_debug 'Domains managed by Plesk server are (ignore the hacked output):'
_debug "$output"

157
dnsapi/dns_porkbun.sh Normal file
View File

@@ -0,0 +1,157 @@
#!/usr/bin/env sh
#
#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
PORKBUN_Api="https://porkbun.com/api/json/v3"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_porkbun_add() {
fulldomain=$1
txtvalue=$2
PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
if [ -z "$PORKBUN_API_KEY" ] || [ -z "$PORKBUN_SECRET_API_KEY" ]; then
PORKBUN_API_KEY=''
PORKBUN_SECRET_API_KEY=''
_err "You didn't specify a Porkbun api key and secret api key yet."
_err "You can get yours from here https://porkbun.com/account/api."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable PORKBUN_API_KEY "$PORKBUN_API_KEY"
_saveaccountconf_mutable PORKBUN_SECRET_API_KEY "$PORKBUN_SECRET_API_KEY"
_debug 'First detect the root zone'
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
# we can not use updating anymore.
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
# _debug count "$count"
# if [ "$count" = "0" ]; then
_info "Adding record"
if _porkbun_rest POST "dns/create/$_domain" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if _contains "$response" '\"status\":"SUCCESS"'; then
_info "Added, OK"
return 0
elif _contains "$response" "The record already exists"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error. ($response)"
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_porkbun_rm() {
fulldomain=$1
txtvalue=$2
PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
_debug 'First detect the root zone'
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(echo "$response" | tr '{' '\n' | grep -- "$txtvalue" | cut -d, -f1 | cut -d: -f2 | tr -d \")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _porkbun_rest POST "dns/delete/$_domain/$record_id"; then
_err "Delete record error."
return 1
fi
echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if _porkbun_rest POST "dns/retrieve/$h"; then
if _contains "$response" "\"status\":\"SUCCESS\""; then
_domain=$h
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
return 0
else
_debug "Go to next level of $_domain"
fi
else
_debug "Go to next level of $_domain"
fi
i=$(_math "$i" + 1)
done
return 1
}
_porkbun_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"')
secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"')
test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1),"
data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}"
export _H1="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$PORKBUN_Api/$ep" "" "$m")"
else
response="$(_get "$PORKBUN_Api/$ep")"
fi
_sleep 3 # prevent rate limit
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

156
dnsapi/dns_rackcorp.sh Normal file
View File

@@ -0,0 +1,156 @@
#!/usr/bin/env sh
# Provider: RackCorp (www.rackcorp.com)
# Author: Stephen Dendtler (sdendtler@rackcorp.com)
# Report Bugs here: https://github.com/senjoo/acme.sh
# Alternate email contact: support@rackcorp.com
#
# You'll need an API key (Portal: ADMINISTRATION -> API)
# Set the environment variables as below:
#
# export RACKCORP_APIUUID="UUIDHERE"
# export RACKCORP_APISECRET="SECRETHERE"
#
RACKCORP_API_ENDPOINT="https://api.rackcorp.net/api/rest/v2.4/json.php"
######## Public functions #####################
dns_rackcorp_add() {
fulldomain="$1"
txtvalue="$2"
_debug fulldomain="$fulldomain"
_debug txtvalue="$txtvalue"
if ! _rackcorp_validate; then
return 1
fi
_debug "Searching for root zone"
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _lookup "$_lookup"
_debug _domain "$_domain"
_info "Creating TXT record."
if ! _rackcorp_api dns.record.create "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\",\"ttl\":300"; then
return 1
fi
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_rackcorp_rm() {
fulldomain=$1
txtvalue=$2
_debug fulldomain="$fulldomain"
_debug txtvalue="$txtvalue"
if ! _rackcorp_validate; then
return 1
fi
_debug "Searching for root zone"
if ! _get_root "$fulldomain"; then
return 1
fi
_debug _lookup "$_lookup"
_debug _domain "$_domain"
_info "Creating TXT record."
if ! _rackcorp_api dns.record.delete "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\""; then
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.domain.com
#returns
# _lookup=_acme-challenge
# _domain=domain.com
_get_root() {
domain=$1
i=1
p=1
if ! _rackcorp_api dns.domain.getall "\"name\":\"$domain\""; then
return 1
fi
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug searchhost "$h"
if [ -z "$h" ]; then
_err "Could not find domain for record $domain in RackCorp using the provided credentials"
#not valid
return 1
fi
_rackcorp_api dns.domain.getall "\"exactName\":\"$h\""
if _contains "$response" "\"matches\":1"; then
if _contains "$response" "\"name\":\"$h\""; then
_lookup=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_rackcorp_validate() {
RACKCORP_APIUUID="${RACKCORP_APIUUID:-$(_readaccountconf_mutable RACKCORP_APIUUID)}"
if [ -z "$RACKCORP_APIUUID" ]; then
RACKCORP_APIUUID=""
_err "You require a RackCorp API UUID (export RACKCORP_APIUUID=\"<api uuid>\")"
_err "Please login to the portal and create an API key and try again."
return 1
fi
_saveaccountconf_mutable RACKCORP_APIUUID "$RACKCORP_APIUUID"
RACKCORP_APISECRET="${RACKCORP_APISECRET:-$(_readaccountconf_mutable RACKCORP_APISECRET)}"
if [ -z "$RACKCORP_APISECRET" ]; then
RACKCORP_APISECRET=""
_err "You require a RackCorp API secret (export RACKCORP_APISECRET=\"<api secret>\")"
_err "Please login to the portal and create an API key and try again."
return 1
fi
_saveaccountconf_mutable RACKCORP_APISECRET "$RACKCORP_APISECRET"
return 0
}
_rackcorp_api() {
_rackcorpcmd=$1
_rackcorpinputdata=$2
_debug cmd "$_rackcorpcmd $_rackcorpinputdata"
export _H1="Accept: application/json"
response="$(_post "{\"APIUUID\":\"$RACKCORP_APIUUID\",\"APISECRET\":\"$RACKCORP_APISECRET\",\"cmd\":\"$_rackcorpcmd\",$_rackcorpinputdata}" "$RACKCORP_API_ENDPOINT" "" "POST")"
if [ "$?" != "0" ]; then
_err "error $response"
return 1
fi
_debug2 response "$response"
if _contains "$response" "\"code\":\"OK\""; then
_debug code "OK"
else
_debug code "FAILED"
response=""
return 1
fi
return 0
}

View File

@@ -7,6 +7,7 @@
RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
# 20210923 - RS changed the fields in the API response; fix sed
# 20190213 - The name & id fields swapped in the API response; fix sed
# 20190101 - Duplicating file for new pull request to dev branch
# Original - tcocca:rackspace_dnsapi https://github.com/acmesh-official/acme.sh/pull/1297
@@ -79,8 +80,8 @@ _get_root_zone() {
_debug2 response "$response"
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
# Response looks like:
# {"ttl":300,"accountId":12345,"id":1111111,"name":"example.com","emailAddress": ...<and so on>
_domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\([^,]*\),\"name\":\"$h\",.*/\1/p")
# {"id":"12345","accountId":"1111111","name": "example.com","ttl":3600,"emailAddress": ... <and so on>
_domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\"\([^,]*\)\",\"accountId\":\"[0-9]*\",\"name\":\"$h\",.*/\1/p")
_debug2 domain_id "$_domain_id"
if [ -n "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)

115
dnsapi/dns_rage4.sh Executable file
View File

@@ -0,0 +1,115 @@
#!/usr/bin/env sh
#
#RAGE4_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#RAGE4_USERNAME="xxxx@sss.com"
RAGE4_Api="https://rage4.com/rapi/"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_rage4_add() {
fulldomain=$1
txtvalue=$2
unquotedtxtvalue=$(echo "$txtvalue" | tr -d \")
RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
if [ -z "$RAGE4_USERNAME" ] || [ -z "$RAGE4_TOKEN" ]; then
RAGE4_USERNAME=""
RAGE4_TOKEN=""
_err "You didn't specify a Rage4 api token and username yet."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable RAGE4_USERNAME "$RAGE4_USERNAME"
_saveaccountconf_mutable RAGE4_TOKEN "$RAGE4_TOKEN"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_rage4_rest "createrecord/?id=$_domain_id&name=$fulldomain&content=$unquotedtxtvalue&type=TXT&active=true&ttl=1"
return 0
}
#fulldomain txtvalue
dns_rage4_rm() {
fulldomain=$1
txtvalue=$2
RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug "Getting txt records"
_rage4_rest "getrecords/?id=${_domain_id}"
_record_id=$(echo "$response" | sed -rn 's/.*"id":([[:digit:]]+)[^\}]*'"$txtvalue"'.*/\1/p')
_rage4_rest "deleterecord/?id=${_record_id}"
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
if ! _rage4_rest "getdomains"; then
return 1
fi
_debug _get_root_domain "$domain"
for line in $(echo "$response" | tr '}' '\n'); do
__domain=$(echo "$line" | sed -rn 's/.*"name":"([^"]*)",.*/\1/p')
__domain_id=$(echo "$line" | sed -rn 's/.*"id":([^,]*),.*/\1/p')
if [ "$domain" != "${domain%"$__domain"*}" ]; then
_domain_id="$__domain_id"
break
fi
done
if [ -z "$_domain_id" ]; then
return 1
fi
return 0
}
_rage4_rest() {
ep="$1"
_debug "$ep"
username_trimmed=$(echo "$RAGE4_USERNAME" | tr -d '"')
token_trimmed=$(echo "$RAGE4_TOKEN" | tr -d '"')
auth=$(printf '%s:%s' "$username_trimmed" "$token_trimmed" | _base64)
export _H1="Content-Type: application/json"
export _H2="Authorization: Basic $auth"
response="$(_get "$RAGE4_Api$ep")"
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -92,9 +92,10 @@ _get_root() {
domains_list=$(echo "${response}" | grep dname | sed -r "s/.*dname=\"([^\"]+)\".*/\\1/g")
for ITEM in ${domains_list}; do
IDN_ITEM=${ITEM}
case "${domain}" in
*${ITEM}*)
_domain=${ITEM}
*${IDN_ITEM}*)
_domain="$(_idn "${ITEM}")"
_debug _domain "${_domain}"
return 0
;;

176
dnsapi/dns_scaleway.sh Executable file
View File

@@ -0,0 +1,176 @@
#!/usr/bin/env sh
# Scaleway API
# https://developers.scaleway.com/en/products/domain/dns/api/
#
# Requires Scaleway API token set in SCALEWAY_API_TOKEN
######## Public functions #####################
SCALEWAY_API="https://api.scaleway.com/domain/v2beta1"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_scaleway_add() {
fulldomain=$1
txtvalue=$2
if ! _scaleway_check_config; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
_scaleway_create_TXT_record "$_domain" "$_sub_domain" "$txtvalue"
if _contains "$response" "records"; then
return 0
else
_err error "$response"
return 1
fi
_info "Record added."
return 0
}
dns_scaleway_rm() {
fulldomain=$1
txtvalue=$2
if ! _scaleway_check_config; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Deleting record"
_scaleway_delete_TXT_record "$_domain" "$_sub_domain" "$txtvalue"
if _contains "$response" "records"; then
return 0
else
_err error "$response"
return 1
fi
_info "Record deleted."
return 0
}
#################### Private functions below ##################################
_scaleway_check_config() {
SCALEWAY_API_TOKEN="${SCALEWAY_API_TOKEN:-$(_readaccountconf_mutable SCALEWAY_API_TOKEN)}"
if [ -z "$SCALEWAY_API_TOKEN" ]; then
_err "No API key specified for Scaleway API."
_err "Create your key and export it as SCALEWAY_API_TOKEN"
return 1
fi
if ! _scaleway_rest GET "dns-zones"; then
_err "Invalid API key specified for Scaleway API."
return 1
fi
_saveaccountconf_mutable SCALEWAY_API_TOKEN "$SCALEWAY_API_TOKEN"
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_scaleway_rest GET "dns-zones/$h/records"
if ! _contains "$response" "subdomain not found" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Unable to retrive DNS zone matching this domain"
return 1
}
# this function add a TXT record
_scaleway_create_TXT_record() {
txt_zone=$1
txt_name=$2
txt_value=$3
_scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"add\":{\"records\":[{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\",\"ttl\":60}]}}]}"
if _contains "$response" "records"; then
return 0
else
_err "error1 $response"
return 1
fi
}
# this function delete a TXT record based on name and content
_scaleway_delete_TXT_record() {
txt_zone=$1
txt_name=$2
txt_value=$3
_scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"delete\":{\"id_fields\":{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\"}}}]}"
if _contains "$response" "records"; then
return 0
else
_err "error2 $response"
return 1
fi
}
_scaleway_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_scaleway_url="$SCALEWAY_API/$ep"
_debug2 _scaleway_url "$_scaleway_url"
export _H1="x-auth-token: $SCALEWAY_API_TOKEN"
export _H2="Accept: application/json"
export _H3="Content-Type: application/json"
if [ "$data" ] || [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$_scaleway_url" "" "$m")"
else
response="$(_get "$_scaleway_url")"
fi
if [ "$?" != "0" ] || _contains "$response" "denied_authentication" || _contains "$response" "Method not allowed" || _contains "$response" "json parse error: unexpected EOF"; then
_err "error $response"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@@ -76,7 +76,7 @@ dns_selectel_rm() {
return 1
fi
_record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")"
_record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
_debug2 "_record_seg" "$_record_seg"
if [ -z "$_record_seg" ]; then
_err "can not find _record_seg"
@@ -120,7 +120,7 @@ _get_root() {
return 1
fi
if _contains "$response" "\"name\": \"$h\","; then
if _contains "$response" "\"name\" *: *\"$h\","; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
_debug "Getting domain id for $h"

94
dnsapi/dns_selfhost.sh Normal file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env sh
#
# Author: Marvin Edeler
# Report Bugs here: https://github.com/Marvo2011/acme.sh/issues/1
# Last Edit: 17.02.2022
dns_selfhost_add() {
fulldomain=$1
txt=$2
_info "Calling acme-dns on selfhost"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txt"
SELFHOSTDNS_UPDATE_URL="https://selfhost.de/cgi-bin/api.pl"
# Get values, but don't save until we successfully validated
SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
SELFHOSTDNS_PASSWORD="${SELFHOSTDNS_PASSWORD:-$(_readaccountconf_mutable SELFHOSTDNS_PASSWORD)}"
# These values are domain dependent, so read them from there
SELFHOSTDNS_MAP="${SELFHOSTDNS_MAP:-$(_readdomainconf SELFHOSTDNS_MAP)}"
# Selfhost api can't dynamically add TXT record,
# so we have to store the last used RID of the domain to support a second RID for wildcard domains
# (format: 'fulldomainA:lastRid fulldomainB:lastRid ...')
SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(_readdomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL)
if [ -z "${SELFHOSTDNS_USERNAME:-}" ] || [ -z "${SELFHOSTDNS_PASSWORD:-}" ]; then
_err "SELFHOSTDNS_USERNAME and SELFHOSTDNS_PASSWORD must be set"
return 1
fi
# get the domain entry from SELFHOSTDNS_MAP
# only match full domains (at the beginning of the string or with a leading whitespace),
# e.g. don't match mytest.example.com or sub.test.example.com for test.example.com
# if the domain is defined multiple times only the last occurance will be matched
mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain)(:[[:digit:]]+)([:]?[[:digit:]]*)(.*)/\2\3\4/p")
_debug2 mapEntry "$mapEntry"
if test -z "$mapEntry"; then
_err "SELFHOSTDNS_MAP must contain the fulldomain incl. prefix and at least one RID"
return 1
fi
# get the RIDs from the map entry
rid1=$(echo "$mapEntry" | cut -d: -f2)
rid2=$(echo "$mapEntry" | cut -d: -f3)
# read last used rid domain
lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain:[[:digit:]]+)(.*)/\2/p")
_debug2 lastUsedRidForDomainEntry "$lastUsedRidForDomainEntry"
lastUsedRidForDomain=$(echo "$lastUsedRidForDomainEntry" | cut -d: -f2)
rid="$rid1"
if [ "$lastUsedRidForDomain" = "$rid" ] && ! test -z "$rid2"; then
rid="$rid2"
fi
_info "Trying to add $txt on selfhost for rid: $rid"
data="?username=$SELFHOSTDNS_USERNAME&password=$SELFHOSTDNS_PASSWORD&rid=$rid&content=$txt"
response="$(_get "$SELFHOSTDNS_UPDATE_URL$data")"
if ! echo "$response" | grep "200 OK" >/dev/null; then
_err "Invalid response of acme-dns for selfhost"
return 1
fi
# write last used rid domain
newLastUsedRidForDomainEntry="$fulldomain:$rid"
if ! test -z "$lastUsedRidForDomainEntry"; then
# replace last used rid entry for domain
SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/$lastUsedRidForDomainEntry/$newLastUsedRidForDomainEntry/p")
else
# add last used rid entry for domain
if test -z "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"; then
SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$newLastUsedRidForDomainEntry"
else
SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$SELFHOSTDNS_MAP_LAST_USED_INTERNAL $newLastUsedRidForDomainEntry"
fi
fi
# Now that we know the values are good, save them
_saveaccountconf_mutable SELFHOSTDNS_USERNAME "$SELFHOSTDNS_USERNAME"
_saveaccountconf_mutable SELFHOSTDNS_PASSWORD "$SELFHOSTDNS_PASSWORD"
# These values are domain dependent, so store them there
_savedomainconf SELFHOSTDNS_MAP "$SELFHOSTDNS_MAP"
_savedomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"
}
dns_selfhost_rm() {
fulldomain=$1
txt=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txt"
_info "Creating and removing of records is not supported by selfhost API, will not delete anything."
}

View File

@@ -49,16 +49,42 @@ dns_servercow_add() {
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
if printf -- "%s" "$response" | grep "ok" >/dev/null; then
_info "Added, OK"
return 0
else
_err "add txt record error."
return 1
# check whether a txt record already exists for the subdomain
if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then
_info "A txt record with the same name already exists."
# trim the string on the left
txtvalue_old=${response#*{\"name\":\""$_sub_domain"\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
# trim the string on the right
txtvalue_old=${txtvalue_old%%\"*}
_debug txtvalue_old "$txtvalue_old"
_info "Add the new txtvalue to the existing txt record."
if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":[\"$txtvalue\",\"$txtvalue_old\"],\"ttl\":20}"; then
if printf -- "%s" "$response" | grep "ok" >/dev/null; then
_info "Added additional txtvalue, OK"
return 0
else
_err "add txt record error."
return 1
fi
fi
_err "add txt record error."
return 1
else
_info "There is no txt record with the name yet."
if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
if printf -- "%s" "$response" | grep "ok" >/dev/null; then
_info "Added, OK"
return 0
else
_err "add txt record error."
return 1
fi
fi
_err "add txt record error."
return 1
fi
_err "add txt record error."
return 1
}

269
dnsapi/dns_simply.sh Normal file
View File

@@ -0,0 +1,269 @@
#!/usr/bin/env sh
# API-integration for Simply.com (https://www.simply.com)
#SIMPLY_AccountName="accountname"
#SIMPLY_ApiKey="apikey"
#
#SIMPLY_Api="https://api.simply.com/2/"
SIMPLY_Api_Default="https://api.simply.com/2"
#This is used for determining success of REST call
SIMPLY_SUCCESS_CODE='"status":200'
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_simply_add() {
fulldomain=$1
txtvalue=$2
if ! _simply_load_config; then
return 1
fi
_simply_save_config
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
if ! _simply_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
_err "Could not add DNS record"
return 1
fi
return 0
}
dns_simply_rm() {
fulldomain=$1
txtvalue=$2
if ! _simply_load_config; then
return 1
fi
_simply_save_config
_debug "Find the DNS zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug txtvalue "$txtvalue"
_info "Getting all existing records"
if ! _simply_get_all_records "$_domain"; then
_err "invalid domain"
return 1
fi
records=$(echo "$response" | tr '{' "\n" | grep 'record_id\|type\|data\|\name' | sed 's/\"record_id/;\"record_id/' | tr "\n" ' ' | tr -d ' ' | tr ';' ' ')
nr_of_deleted_records=0
_info "Fetching txt record"
for record in $records; do
_debug record "$record"
record_data=$(echo "$record" | sed -n "s/.*\"data\":\"\([^\"]*\)\".*/\1/p")
record_type=$(echo "$record" | sed -n "s/.*\"type\":\"\([^\"]*\)\".*/\1/p")
_debug2 record_data "$record_data"
_debug2 record_type "$record_type"
if [ "$record_data" = "$txtvalue" ] && [ "$record_type" = "TXT" ]; then
record_id=$(echo "$record" | cut -d "," -f 1 | grep "record_id" | cut -d ":" -f 2)
_info "Deleting record $record"
_debug2 record_id "$record_id"
if [ "$record_id" -gt 0 ]; then
if ! _simply_delete_record "$_domain" "$_sub_domain" "$record_id"; then
_err "Record with id $record_id could not be deleted"
return 1
fi
nr_of_deleted_records=1
break
else
_err "Fetching record_id could not be done, this should not happen, exiting function. Failing record is $record"
break
fi
fi
done
if [ "$nr_of_deleted_records" -eq 0 ]; then
_err "No record deleted, the DNS record needs to be removed manually."
else
_info "Deleted $nr_of_deleted_records record"
fi
return 0
}
#################### Private functions below ##################################
_simply_load_config() {
SIMPLY_Api="${SIMPLY_Api:-$(_readaccountconf_mutable SIMPLY_Api)}"
SIMPLY_AccountName="${SIMPLY_AccountName:-$(_readaccountconf_mutable SIMPLY_AccountName)}"
SIMPLY_ApiKey="${SIMPLY_ApiKey:-$(_readaccountconf_mutable SIMPLY_ApiKey)}"
if [ -z "$SIMPLY_Api" ]; then
SIMPLY_Api="$SIMPLY_Api_Default"
fi
if [ -z "$SIMPLY_AccountName" ] || [ -z "$SIMPLY_ApiKey" ]; then
SIMPLY_AccountName=""
SIMPLY_ApiKey=""
_err "A valid Simply API account and apikey not provided."
_err "Please provide a valid API user and try again."
return 1
fi
return 0
}
_simply_save_config() {
if [ "$SIMPLY_Api" != "$SIMPLY_Api_Default" ]; then
_saveaccountconf_mutable SIMPLY_Api "$SIMPLY_Api"
fi
_saveaccountconf_mutable SIMPLY_AccountName "$SIMPLY_AccountName"
_saveaccountconf_mutable SIMPLY_ApiKey "$SIMPLY_ApiKey"
}
_simply_get_all_records() {
domain=$1
if ! _simply_rest GET "my/products/$domain/dns/records/"; then
return 1
fi
return 0
}
_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
if ! _simply_rest GET "my/products/$h/dns/"; then
return 1
fi
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
_debug "$h not found"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
_simply_add_record() {
domain=$1
sub_domain=$2
txtval=$3
data="{\"name\": \"$sub_domain\", \"type\":\"TXT\", \"data\": \"$txtval\", \"priority\":0, \"ttl\": 3600}"
if ! _simply_rest POST "my/products/$domain/dns/records/" "$data"; then
_err "Adding record not successfull!"
return 1
fi
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
_err "Call to API not sucessfull, see below message for more details"
_err "$response"
return 1
fi
return 0
}
_simply_delete_record() {
domain=$1
sub_domain=$2
record_id=$3
_debug record_id "Delete record with id $record_id"
if ! _simply_rest DELETE "my/products/$domain/dns/records/$record_id/"; then
_err "Deleting record not successfull!"
return 1
fi
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
_err "Call to API not sucessfull, see below message for more details"
_err "$response"
return 1
fi
return 0
}
_simply_rest() {
m=$1
ep="$2"
data="$3"
_debug2 data "$data"
_debug2 ep "$ep"
_debug2 m "$m"
basicauth=$(printf "%s:%s" "$SIMPLY_AccountName" "$SIMPLY_ApiKey" | _base64)
if [ "$basicauth" ]; then
export _H1="Authorization: Basic $basicauth"
fi
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
response="$(_post "$data" "$SIMPLY_Api/$ep" "" "$m")"
else
response="$(_get "$SIMPLY_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
if _contains "$response" "Invalid account authorization"; then
_err "It seems that your api key or accountnumber is not correct."
return 1
fi
return 0
}

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env sh
TRANSIP_Api_Url="https://api.transip.nl/v6"
TRANSIP_Token_Read_Only="false"
TRANSIP_Token_Global_Key="false"
TRANSIP_Token_Expiration="30 minutes"
# You can't reuse a label token, so we leave this empty normally
TRANSIP_Token_Label=""
@@ -96,7 +95,11 @@ _transip_get_token() {
nonce=$(echo "TRANSIP$(_time)" | _digest sha1 hex | cut -c 1-32)
_debug nonce "$nonce"
data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key}\"}"
# make IP whitelisting configurable
TRANSIP_Token_Global_Key="${TRANSIP_Token_Global_Key:-$(_readaccountconf_mutable TRANSIP_Token_Global_Key)}"
_saveaccountconf_mutable TRANSIP_Token_Global_Key "$TRANSIP_Token_Global_Key"
data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key:-false}\"}"
_debug data "$data"
#_signature=$(printf "%s" "$data" | openssl dgst -sha512 -sign "$TRANSIP_Key_File" | _base64)
@@ -139,6 +142,18 @@ _transip_setup() {
_saveaccountconf_mutable TRANSIP_Username "$TRANSIP_Username"
_saveaccountconf_mutable TRANSIP_Key_File "$TRANSIP_Key_File"
# download key file if it's an URL
if _startswith "$TRANSIP_Key_File" "http"; then
_debug "download transip key file"
TRANSIP_Key_URL=$TRANSIP_Key_File
TRANSIP_Key_File="$(_mktemp)"
chmod 600 "$TRANSIP_Key_File"
if ! _get "$TRANSIP_Key_URL" >"$TRANSIP_Key_File"; then
_err "Error getting key file from : $TRANSIP_Key_URL"
return 1
fi
fi
if [ -f "$TRANSIP_Key_File" ]; then
if ! grep "BEGIN PRIVATE KEY" "$TRANSIP_Key_File" >/dev/null 2>&1; then
_err "Key file doesn't seem to be a valid key: ${TRANSIP_Key_File}"
@@ -156,6 +171,12 @@ _transip_setup() {
fi
fi
if [ -n "${TRANSIP_Key_URL}" ]; then
_debug "delete transip key file"
rm "${TRANSIP_Key_File}"
TRANSIP_Key_File=$TRANSIP_Key_URL
fi
_get_root "$fulldomain" || return 1
return 0

160
dnsapi/dns_udr.sh Normal file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env sh
# united-domains Reselling (https://www.ud-reselling.com/) DNS API
# Author: Andreas Scherer (https://github.com/andischerer)
# Created: 2021-02-01
#
# Set the environment variables as below:
#
# export UDR_USER="your_username_goes_here"
# export UDR_PASS="some_password_goes_here"
#
UDR_API="https://api.domainreselling.de/api/call.cgi"
UDR_TTL="30"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
dns_udr_add() {
fulldomain=$1
txtvalue=$2
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
UDR_USER=""
UDR_PASS=""
_err "You didn't specify an UD-Reselling username and password yet"
return 1
fi
# save the username and password to the account conf file.
_saveaccountconf_mutable UDR_USER "$UDR_USER"
_saveaccountconf_mutable UDR_PASS "$UDR_PASS"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _dnszone "${_dnszone}"
_debug "Getting txt records"
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
return 1
fi
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
_debug resource_record "${rr}"
if _contains "$response" "$rr" >/dev/null; then
_err "Error, it would appear that this record already exists. Please review existing TXT records for this domain."
return 1
fi
_info "Adding record"
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&addrr0=${rr}"; then
_err "Adding the record did not succeed, please verify/check."
return 1
fi
_info "Added, OK"
return 0
}
dns_udr_rm() {
fulldomain=$1
txtvalue=$2
UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
UDR_USER=""
UDR_PASS=""
_err "You didn't specify an UD-Reselling username and password yet"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _dnszone "${_dnszone}"
_debug "Getting txt records"
if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
return 1
fi
rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
_debug resource_record "${rr}"
if _contains "$response" "$rr" >/dev/null; then
if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&delrr0=${rr}"; then
_err "Deleting the record did not succeed, please verify/check."
return 1
fi
_info "Removed, OK"
return 0
else
_info "Text record is not present, will not delete anything."
return 0
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
if ! _udr_rest "QueryDNSZoneList" ""; then
return 1
fi
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}." >/dev/null; then
_dnszone=$(echo "$response" | _egrep_o "${h}")
if [ "$_dnszone" ]; then
return 0
fi
return 1
fi
i=$(_math "$i" + 1)
done
return 1
}
_udr_rest() {
if [ -n "$2" ]; then
data="command=$1&$2"
else
data="command=$1"
fi
_debug data "${data}"
response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")"
_code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
_description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
_debug response_code "$_code"
_debug response_description "$_description"
if [ ! "$_code" = "200" ]; then
_err "DNS-API-Error: $_description"
return 1
fi
return 0
}

View File

@@ -5,7 +5,8 @@
#
# ULTRA_PWD="some_password_goes_here"
ULTRA_API="https://restapi.ultradns.com/v2/"
ULTRA_API="https://api.ultradns.com/v3/"
ULTRA_AUTH_API="https://api.ultradns.com/v2/"
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
dns_ultra_add() {
@@ -121,7 +122,7 @@ _get_root() {
return 1
fi
if _contains "${response}" "${h}." >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "${h}")
_domain_id=$(echo "$response" | _egrep_o "${h}" | head -1)
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="${h}"
@@ -142,23 +143,25 @@ _ultra_rest() {
ep="$2"
data="$3"
_debug "$ep"
_debug TOKEN "${AUTH_TOKEN}"
if [ -z "$AUTH_TOKEN" ]; then
_ultra_login
fi
_debug TOKEN "$AUTH_TOKEN"
_ultra_login
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer ${AUTH_TOKEN}"
export _H2="Authorization: Bearer $AUTH_TOKEN"
if [ "$m" != "GET" ]; then
_debug data "${data}"
response="$(_post "${data}" "${ULTRA_API}"/"${ep}" "" "${m}")"
_debug data "$data"
response="$(_post "$data" "$ULTRA_API$ep" "" "$m")"
else
response="$(_get "$ULTRA_API/$ep")"
response="$(_get "$ULTRA_API$ep")"
fi
}
_ultra_login() {
export _H1=""
export _H2=""
AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_AUTH_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
export AUTH_TOKEN
}

158
dnsapi/dns_veesp.sh Normal file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/env sh
# bug reports to stepan@plyask.in
#
# export VEESP_User="username"
# export VEESP_Password="password"
VEESP_Api="https://secure.veesp.com/api"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_veesp_add() {
fulldomain=$1
txtvalue=$2
VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}"
VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}"
VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64)
if [ -z "$VEESP_Password" ] || [ -z "$VEESP_User" ]; then
VEESP_Password=""
VEESP_User=""
_err "You don't specify veesp api key and email yet."
_err "Please create you key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable VEESP_Password "$VEESP_Password"
_saveaccountconf_mutable VEESP_User "$VEESP_User"
_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"
_info "Adding record"
if VEESP_rest POST "service/$_service_id/dns/$_domain_id/records" "{\"name\":\"$fulldomain\",\"ttl\":1,\"priority\":0,\"type\":\"TXT\",\"content\":\"$txtvalue\"}"; then
if _contains "$response" "\"success\":true"; then
_info "Added"
#todo: check if the record takes effect
return 0
else
_err "Add txt record error."
return 1
fi
fi
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_veesp_rm() {
fulldomain=$1
txtvalue=$2
VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}"
VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}"
VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64)
_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"
VEESP_rest GET "service/$_service_id/dns/$_domain_id"
count=$(printf "%s\n" "$response" | _egrep_o "\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "{\"id\":[^}]*\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | cut -d\" -f4)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! VEESP_rest DELETE "service/$_service_id/dns/$_domain_id/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" "\"success\":true"
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=2
p=1
if ! VEESP_rest GET "dns"; then
return 1
fi
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" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"domain_id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1 | cut -d '"' -f 2)
_debug _domain_id "$_domain_id"
_service_id=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$h\",\"service_id\":[^}]*" | cut -d : -f 3 | cut -d '"' -f 2)
_debug _service_id "$_service_id"
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
return 1
}
VEESP_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Accept: application/json"
export _H2="Authorization: Basic $VEESP_auth"
if [ "$m" != "GET" ]; then
_debug data "$data"
export _H3="Content-Type: application/json"
response="$(_post "$data" "$VEESP_Api/$ep" "" "$m")"
else
response="$(_get "$VEESP_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

142
dnsapi/dns_vercel.sh Normal file
View File

@@ -0,0 +1,142 @@
#!/usr/bin/env sh
# Vercel DNS API
#
# This is your API token which can be acquired on the account page.
# https://vercel.com/account/tokens
#
# VERCEL_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
VERCEL_API="https://api.vercel.com"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_vercel_add() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
VERCEL_TOKEN="${VERCEL_TOKEN:-$(_readaccountconf_mutable VERCEL_TOKEN)}"
if [ -z "$VERCEL_TOKEN" ]; then
VERCEL_TOKEN=""
_err "You have not set the Vercel API token yet."
_err "Please visit https://vercel.com/account/tokens to generate it."
return 1
fi
_saveaccountconf_mutable VERCEL_TOKEN "$VERCEL_TOKEN"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
if _vercel_rest POST "v2/domains/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"; then
if printf -- "%s" "$response" | grep "\"uid\":\"" >/dev/null; then
_info "Added"
return 0
else
_err "Unexpected response while adding text record."
return 1
fi
fi
_err "Add txt record error."
}
dns_vercel_rm() {
fulldomain=$1
txtvalue=$2
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_vercel_rest GET "v2/domains/$_domain/records"
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
_record_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"slug\":\"[^,]*\",\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\",\"value\":\"$txtvalue\"" | cut -d: -f2 | cut -d, -f1 | tr -d '"')
if [ "$_record_id" ]; then
echo "$_record_id" | while read -r item; do
if _vercel_rest DELETE "v2/domains/$_domain/records/$item"; then
_info "removed record" "$item"
return 0
else
_err "failed to remove record" "$item"
return 1
fi
done
fi
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain="$1"
ep="$2"
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _vercel_rest GET "v4/domains/$h"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_vercel_rest() {
m="$1"
ep="$2"
data="$3"
path="$VERCEL_API/$ep"
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $VERCEL_TOKEN"
if [ "$m" != "GET" ]; then
_secure_debug2 data "$data"
response="$(_post "$data" "$path" "" "$m")"
else
response="$(_get "$path")"
fi
_ret="$?"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
_secure_debug2 response "$response"
if [ "$_ret" != "0" ]; then
_err "error $ep"
return 1
fi
response="$(printf "%s" "$response" | _normalizeJson)"
return 0
}

View File

@@ -3,10 +3,10 @@
#
#VULTR_API_KEY=000011112222333344445555666677778888
VULTR_Api="https://api.vultr.com/v1"
VULTR_Api="https://api.vultr.com/v2"
######## Public functions #####################
#
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_vultr_add() {
fulldomain=$1
@@ -31,14 +31,14 @@ dns_vultr_add() {
_debug _domain "$_domain"
_debug 'Getting txt records'
_vultr_rest GET "dns/records?domain=$_domain"
_vultr_rest GET "domains/$_domain/records"
if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
_err 'Error'
return 1
fi
if ! _vultr_rest POST 'dns/create_record' "domain=$_domain&name=$_sub_domain&data=\"$txtvalue\"&type=TXT"; then
if ! _vultr_rest POST "domains/$_domain/records" "{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"type\":\"TXT\"}"; then
_err "$response"
return 1
fi
@@ -71,14 +71,14 @@ dns_vultr_rm() {
_debug _domain "$_domain"
_debug 'Getting txt records'
_vultr_rest GET "dns/records?domain=$_domain"
_vultr_rest GET "domains/$_domain/records"
if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
_err 'Error'
return 1
fi
_record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep "$txtvalue" | tr ',' '\n' | grep -i 'RECORDID' | cut -d : -f 2)"
_record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'id' | cut -d : -f 2 | tr -d '"')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."
@@ -87,7 +87,7 @@ dns_vultr_rm() {
return 0
fi
if ! _vultr_rest POST 'dns/delete_record' "domain=$_domain&RECORDID=$_record_id"; then
if ! _vultr_rest DELETE "domains/$_domain/records/$_record_id"; then
_err "$response"
return 1
fi
@@ -112,11 +112,11 @@ _get_root() {
return 1
fi
if ! _vultr_rest GET "dns/list"; then
if ! _vultr_rest GET "domains"; then
return 1
fi
if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then
if printf "%s\n" "$response" | grep -E '^\{.*\}' >/dev/null; then
if _contains "$response" "\"domain\":\"$_domain\""; then
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
return 0
@@ -139,10 +139,10 @@ _vultr_rest() {
data="$3"
_debug "$ep"
api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"')
api_key_trimmed=$(echo "$VULTR_API_KEY" | tr -d '"')
export _H1="Api-Key: $api_key_trimmed"
export _H2='Content-Type: application/x-www-form-urlencoded'
export _H1="Authorization: Bearer $api_key_trimmed"
export _H2='Content-Type: application/json'
if [ "$m" != "GET" ]; then
_debug data "$data"

207
dnsapi/dns_websupport.sh Normal file
View File

@@ -0,0 +1,207 @@
#!/usr/bin/env sh
# Acme.sh DNS API wrapper for websupport.sk
#
# Original author: trgo.sk (https://github.com/trgosk)
# Tweaks by: akulumbeg (https://github.com/akulumbeg)
# Report Bugs here: https://github.com/akulumbeg/acme.sh
# Requirements: API Key and Secret from https://admin.websupport.sk/en/auth/apiKey
#
# WS_ApiKey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# (called "Identifier" in the WS Admin)
#
# WS_ApiSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# (called "Secret key" in the WS Admin)
WS_Api="https://rest.websupport.sk"
######## Public functions #####################
dns_websupport_add() {
fulldomain=$1
txtvalue=$2
WS_ApiKey="${WS_ApiKey:-$(_readaccountconf_mutable WS_ApiKey)}"
WS_ApiSecret="${WS_ApiSecret:-$(_readaccountconf_mutable WS_ApiSecret)}"
if [ "$WS_ApiKey" ] && [ "$WS_ApiSecret" ]; then
_saveaccountconf_mutable WS_ApiKey "$WS_ApiKey"
_saveaccountconf_mutable WS_ApiSecret "$WS_ApiSecret"
else
WS_ApiKey=""
WS_ApiSecret=""
_err "You did not specify the API Key and/or API Secret"
_err "You can get the API login credentials from https://admin.websupport.sk/en/auth/apiKey"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
# we can not use updating anymore.
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
# _debug count "$count"
# if [ "$count" = "0" ]; then
_info "Adding record"
if _ws_rest POST "/v1/user/self/zone/$_domain/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
elif _contains "$response" "The record already exists"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
dns_websupport_rm() {
fulldomain=$1
txtvalue=$2
_debug2 fulldomain "$fulldomain"
_debug2 txtvalue "$txtvalue"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_ws_rest GET "/v1/user/self/zone/$_domain/record"
if [ "$(printf "%s" "$response" | tr -d " " | grep -c \"items\")" -lt "1" ]; then
_err "Error: $response"
return 1
fi
record_line="$(_get_from_array "$response" "$txtvalue")"
_debug record_line "$record_line"
if [ -z "$record_line" ]; then
_info "Don't need to remove."
else
record_id=$(echo "$record_line" | _egrep_o "\"id\": *[^,]*" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _ws_rest DELETE "/v1/user/self/zone/$_domain/record/$record_id"; then
_err "Delete record error."
return 1
fi
if [ "$(printf "%s" "$response" | tr -d " " | grep -c \"success\")" -lt "1" ]; then
return 1
else
return 0
fi
fi
}
#################### Private Functions ##################################
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _ws_rest GET "/v1/user/self/zone"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *[^,]*" | _head_n 1 | cut -d : -f 2 | tr -d \" | 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
return 1
}
_ws_rest() {
me=$1
pa="$2"
da="$3"
_debug2 api_key "$WS_ApiKey"
_debug2 api_secret "$WS_ApiSecret"
timestamp=$(_time)
datez="$(_utc_date | sed "s/ /T/" | sed "s/$/+0000/")"
canonical_request="${me} ${pa} ${timestamp}"
signature_hash=$(printf "%s" "$canonical_request" | _hmac sha1 "$(printf "%s" "$WS_ApiSecret" | _hex_dump | tr -d " ")" hex)
basicauth="$(printf "%s:%s" "$WS_ApiKey" "$signature_hash" | _base64)"
_debug2 method "$me"
_debug2 path "$pa"
_debug2 data "$da"
_debug2 timestamp "$timestamp"
_debug2 datez "$datez"
_debug2 canonical_request "$canonical_request"
_debug2 signature_hash "$signature_hash"
_debug2 basicauth "$basicauth"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
export _H3="Authorization: Basic ${basicauth}"
export _H4="Date: ${datez}"
_debug2 H1 "$_H1"
_debug2 H2 "$_H2"
_debug2 H3 "$_H3"
_debug2 H4 "$_H4"
if [ "$me" != "GET" ]; then
_debug2 "${me} $WS_Api${pa}"
_debug data "$da"
response="$(_post "$da" "${WS_Api}${pa}" "" "$me")"
else
_debug2 "GET $WS_Api${pa}"
response="$(_get "$WS_Api${pa}")"
fi
_debug2 response "$response"
return "$?"
}
_get_from_array() {
va="$1"
fi="$2"
for i in $(echo "$va" | sed "s/{/ /g"); do
if _contains "$i" "$fi"; then
echo "$i"
break
fi
done
}

220
dnsapi/dns_world4you.sh Normal file
View File

@@ -0,0 +1,220 @@
#!/usr/bin/env sh
# World4You - www.world4you.com
# Lorenz Stechauner, 2020 - https://www.github.com/NerLOR
WORLD4YOU_API="https://my.world4you.com/en"
PAKETNR=''
TLD=''
RECORD=''
################ Public functions ################
# Usage: dns_world4you_add <fqdn> <value>
dns_world4you_add() {
fqdn=$(echo "$1" | _lower_case)
value="$2"
_info "Using world4you to add record"
_debug fulldomain "$fqdn"
_debug txtvalue "$value"
_login
if [ "$?" != 0 ]; then
return 1
fi
export _H1="Cookie: W4YSESSID=$sessid"
form=$(_get "$WORLD4YOU_API/")
_get_paketnr "$fqdn" "$form"
paketnr="$PAKETNR"
if [ -z "$paketnr" ]; then
_err "Unable to parse paketnr"
return 3
fi
_debug paketnr "$paketnr"
export _H1="Cookie: W4YSESSID=$sessid"
form=$(_get "$WORLD4YOU_API/$paketnr/dns")
formiddp=$(echo "$form" | grep 'AddDnsRecordForm\[uniqueFormIdDP\]' | sed 's/^.*name="AddDnsRecordForm\[uniqueFormIdDP\]" value="\([^"]*\)".*$/\1/')
form_token=$(echo "$form" | grep 'AddDnsRecordForm\[_token\]' | sed 's/^.*name="AddDnsRecordForm\[_token\]" value="\([^"]*\)".*$/\1/')
if [ -z "$formiddp" ]; then
_err "Unable to parse form"
return 3
fi
_resethttp
export ACME_HTTP_NO_REDIRECTS=1
body="AddDnsRecordForm[name]=$RECORD&AddDnsRecordForm[dnsType][type]=TXT&AddDnsRecordForm[value]=$value&AddDnsRecordForm[uniqueFormIdDP]=$formiddp&AddDnsRecordForm[_token]=$form_token"
_info "Adding record..."
ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns" '' POST 'application/x-www-form-urlencoded')
_resethttp
if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
res=$(_get "$WORLD4YOU_API/$paketnr/dns")
if _contains "$res" "successfully"; then
return 0
else
msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to add record: Unknown error"
echo "$ret" >'error-01.html'
echo "$res" >'error-02.html'
_err "View error-01.html and error-02.html for debugging"
else
_err "Unable to add record: my.world4you.com: $msg"
fi
return 1
fi
else
msg=$(echo "$ret" | grep '"form-error-message"' | sed 's/^.*<div class="form-error-message">\([^<]*\)<\/div>.*$/\1/')
_err "Unable to add record: my.world4you.com: $msg"
return 1
fi
}
# Usage: dns_world4you_rm <fqdn> <value>
dns_world4you_rm() {
fqdn=$(echo "$1" | _lower_case)
value="$2"
_info "Using world4you to remove record"
_debug fulldomain "$fqdn"
_debug txtvalue "$value"
_login
if [ "$?" != 0 ]; then
return 1
fi
export _H1="Cookie: W4YSESSID=$sessid"
form=$(_get "$WORLD4YOU_API/")
_get_paketnr "$fqdn" "$form"
paketnr="$PAKETNR"
if [ -z "$paketnr" ]; then
_err "Unable to parse paketnr"
return 3
fi
_debug paketnr "$paketnr"
form=$(_get "$WORLD4YOU_API/$paketnr/dns")
formiddp=$(echo "$form" | grep 'DeleteDnsRecordForm\[uniqueFormIdDP\]' | sed 's/^.*name="DeleteDnsRecordForm\[uniqueFormIdDP\]" value="\([^"]*\)".*$/\1/')
form_token=$(echo "$form" | grep 'DeleteDnsRecordForm\[_token\]' | sed 's/^.*name="DeleteDnsRecordForm\[_token\]" value="\([^"]*\)".*$/\1/')
if [ -z "$formiddp" ]; then
_err "Unable to parse form"
return 3
fi
recordid=$(printf "TXT:%s.:\"%s\"" "$fqdn" "$value" | _base64)
_debug recordid "$recordid"
_resethttp
export ACME_HTTP_NO_REDIRECTS=1
body="DeleteDnsRecordForm[recordId]=$recordid&DeleteDnsRecordForm[uniqueFormIdDP]=$formiddp&DeleteDnsRecordForm[_token]=$form_token"
_info "Removing record..."
ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns/record/delete" '' POST 'application/x-www-form-urlencoded')
_resethttp
if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
res=$(_get "$WORLD4YOU_API/$paketnr/dns")
if _contains "$res" "successfully"; then
return 0
else
msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to remove record: Unknown error"
echo "$ret" >'error-01.html'
echo "$res" >'error-02.html'
_err "View error-01.html and error-02.html for debugging"
else
_err "Unable to remove record: my.world4you.com: $msg"
fi
return 1
fi
else
msg=$(echo "$ret" | grep "form-error-message" | sed 's/^.*<div class="form-error-message">\([^<]*\)<\/div>.*$/\1/')
_err "Unable to remove record: my.world4you.com: $msg"
return 1
fi
}
################ Private functions ################
# Usage: _login
_login() {
WORLD4YOU_USERNAME="${WORLD4YOU_USERNAME:-$(_readaccountconf_mutable WORLD4YOU_USERNAME)}"
WORLD4YOU_PASSWORD="${WORLD4YOU_PASSWORD:-$(_readaccountconf_mutable WORLD4YOU_PASSWORD)}"
if [ -z "$WORLD4YOU_USERNAME" ] || [ -z "$WORLD4YOU_PASSWORD" ]; then
WORLD4YOU_USERNAME=""
WORLD4YOU_PASSWORD=""
_err "You didn't specify world4you username and password yet."
_err "Usage: export WORLD4YOU_USERNAME=<name>"
_err "Usage: export WORLD4YOU_PASSWORD=<password>"
return 1
fi
_saveaccountconf_mutable WORLD4YOU_USERNAME "$WORLD4YOU_USERNAME"
_saveaccountconf_mutable WORLD4YOU_PASSWORD "$WORLD4YOU_PASSWORD"
_resethttp
export ACME_HTTP_NO_REDIRECTS=1
page=$(_get "$WORLD4YOU_API/login")
_resethttp
if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
_info "Already logged in"
_parse_sessid
return 0
fi
_info "Logging in..."
username="$WORLD4YOU_USERNAME"
password="$WORLD4YOU_PASSWORD"
csrf_token=$(echo "$page" | grep '_csrf_token' | sed 's/^.*<input[^>]*value=\"\([^"]*\)\".*$/\1/')
_parse_sessid
export _H1="Cookie: W4YSESSID=$sessid"
export _H2="X-Requested-With: XMLHttpRequest"
body="_username=$username&_password=$password&_csrf_token=$csrf_token"
ret=$(_post "$body" "$WORLD4YOU_API/login" '' POST 'application/x-www-form-urlencoded')
unset _H2
_debug ret "$ret"
if _contains "$ret" "\"success\":true"; then
_info "Successfully logged in"
_parse_sessid
else
msg=$(echo "$ret" | sed 's/^.*"message":"\([^\"]*\)".*$/\1/')
_err "Unable to log in: my.world4you.com: $msg"
return 1
fi
}
# Usage: _get_paketnr <fqdn> <form>
_get_paketnr() {
fqdn="$1"
form="$2"
domains=$(echo "$form" | grep '<ul class="nav header-paket-list">' | sed 's/<li/\n<li/g' | sed 's/<[^>]*>/ /g' | sed 's/^.*>\([^>]*\)$/\1/')
domain=''
for domain in $domains; do
if _contains "$fqdn" "$domain\$"; then
break
fi
domain=''
done
if [ -z "$domain" ]; then
return 1
fi
TLD="$domain"
_debug domain "$domain"
RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))")
PAKETNR=$(echo "$domains" | grep "$domain" | sed 's/^[^,]*, *\([0-9]*\).*$/\1/')
return 0
}
# Usage: _parse_sessid
_parse_sessid() {
sessid=$(grep 'W4YSESSID' <"$HTTP_HEADER" | _tail_n 1 | sed 's/^.*W4YSESSID=\([^;]*\);.*$/\1/')
}

264
dnsapi/dns_yc.sh Normal file
View File

@@ -0,0 +1,264 @@
#!/usr/bin/env sh
#YC_Zone_ID="" # DNS Zone ID
#YC_Folder_ID="" # YC Folder ID
#YC_SA_ID="" # Service Account ID
#YC_SA_Key_ID="" # Service Account IAM Key ID
#YC_SA_Key_File_Path="/path/to/private.key" # Path to private.key use instead of YC_SA_Key_File_PEM_b64
#YC_SA_Key_File_PEM_b64="" # Base64 content of private.key use instead of YC_SA_Key_File_Path
YC_Api="https://dns.api.cloud.yandex.net/dns/v1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_yc_add() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
if [ "$YC_SA_Key_File_PEM_b64" ]; then
echo "$YC_SA_Key_File_PEM_b64" | _dbase64 >private.key
YC_SA_Key_File="private.key"
_savedomainconf YC_SA_Key_File_PEM_b64 "$YC_SA_Key_File_PEM_b64"
else
YC_SA_Key_File="$YC_SA_Key_File_Path"
_savedomainconf YC_SA_Key_File_Path "$YC_SA_Key_File_Path"
fi
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
if [ -f "$YC_SA_Key_File" ]; then
if _isRSA "$YC_SA_Key_File" >/dev/null 2>&1; then
if [ "$YC_Zone_ID" ]; then
_savedomainconf YC_Zone_ID "$YC_Zone_ID"
_savedomainconf YC_SA_ID "$YC_SA_ID"
_savedomainconf YC_SA_Key_ID "$YC_SA_Key_ID"
elif [ "$YC_Folder_ID" ]; then
_savedomainconf YC_Folder_ID "$YC_Folder_ID"
_saveaccountconf_mutable YC_SA_ID "$YC_SA_ID"
_saveaccountconf_mutable YC_SA_Key_ID "$YC_SA_Key_ID"
_clearaccountconf_mutable YC_Zone_ID
_clearaccountconf YC_Zone_ID
else
_err "You didn't specify a Yandex Cloud Zone ID or Folder ID yet."
return 1
fi
else
_err "YC_SA_Key_File not a RSA file(_isRSA function return false)."
return 1
fi
else
_err "YC_SA_Key_File not found in path $YC_SA_Key_File."
return 1
fi
else
_clearaccountconf YC_Zone_ID
_clearaccountconf YC_Folder_ID
_clearaccountconf YC_SA_ID
_clearaccountconf YC_SA_Key_ID
_clearaccountconf YC_SA_Key_File_PEM_b64
_clearaccountconf YC_SA_Key_File_Path
_err "You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
if ! _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
_err "Error: $response"
return 1
fi
_info "Adding record"
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
if _contains "$response" "\"done\": true"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_yc_rm() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
_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"
if _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
exists_txtvalue=$(echo "$response" | _normalizeJson | _egrep_o "\"data\".*\][^,]*" | _egrep_o "[^:]*$")
_debug exists_txtvalue "$exists_txtvalue"
else
_err "Error: $response"
return 1
fi
if _yc_rest POST "zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
if _contains "$response" "\"done\": true"; then
_info "Delete, OK"
return 0
else
_err "Delete record error."
return 1
fi
fi
_err "Delete record error."
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
# Use Zone ID directly if provided
if [ "$YC_Zone_ID" ]; then
if ! _yc_rest GET "zones/$YC_Zone_ID"; then
return 1
else
if echo "$response" | tr -d " " | _egrep_o "\"id\":\"$YC_Zone_ID\"" >/dev/null; then
_domain=$(echo "$response" | _egrep_o "\"zone\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
if [ "$_domain" ]; then
_cutlength=$((${#domain} - ${#_domain}))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
_domain_id=$YC_Zone_ID
return 0
else
return 1
fi
else
return 1
fi
fi
fi
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 [ "$YC_Folder_ID" ]; then
if ! _yc_rest GET "zones?folderId=$YC_Folder_ID"; then
return 1
fi
else
echo "You didn't specify a Yandex Cloud Folder ID."
return 1
fi
if _contains "$response" "\"zone\": \"$h\""; then
_domain_id=$(echo "$response" | _normalizeJson | _egrep_o "[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o "\"id\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')
_debug _domain_id "$_domain_id"
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
return 1
}
_yc_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
if [ ! "$YC_Token" ]; then
_debug "Login"
_yc_login
else
_debug "Token already exists. Skip Login."
fi
token_trimmed=$(echo "$YC_Token" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $token_trimmed"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$YC_Api/$ep" "" "$m")"
else
response="$(_get "$YC_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_yc_login() {
header=$(echo "{\"typ\":\"JWT\",\"alg\":\"PS256\",\"kid\":\"$YC_SA_Key_ID\"}" | _normalizeJson | _base64 | _url_replace)
_debug header "$header"
_current_timestamp=$(_time)
_expire_timestamp=$(_math "$_current_timestamp" + 1200) # 20 minutes
payload=$(echo "{\"iss\":\"$YC_SA_ID\",\"aud\":\"https://iam.api.cloud.yandex.net/iam/v1/tokens\",\"iat\":$_current_timestamp,\"exp\":$_expire_timestamp}" | _normalizeJson | _base64 | _url_replace)
_debug payload "$payload"
#signature=$(printf "%s.%s" "$header" "$payload" | ${ACME_OPENSSL_BIN:-openssl} dgst -sign "$YC_SA_Key_File -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _base64 | _url_replace )
_signature=$(printf "%s.%s" "$header" "$payload" | _sign "$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace)
_debug2 _signature "$_signature"
rm -rf "$YC_SA_Key_File"
_jwt=$(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
_debug2 _jwt "$_jwt"
export _H1="Content-Type: application/json"
_iam_response="$(_post "$_jwt" "https://iam.api.cloud.yandex.net/iam/v1/tokens" "" "POST")"
_debug3 _iam_response "$(echo "$_iam_response" | _normalizeJson)"
YC_Token="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')"
_debug3 YC_Token
return 0
}