@ -8,9 +8,9 @@ Options:
EXOSCALE_SECRET_KEY API Secret key
'
EXOSCALE_API = https://api.exoscale.com/dns/v1
EXOSCALE_API = "https://api-ch-gva-2.exoscale.com/v2"
######## Public functions ############# ########
######## Public functions ########
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
@ -18,159 +18,197 @@ dns_exoscale_add() {
fulldomain = $1
txtvalue = $2
if ! _checkAuth; then
_debug "Using Exoscale DNS v2 API"
_debug fulldomain " $fulldomain "
_debug txtvalue " $txtvalue "
if ! _check_auth; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root " $fulldomain " ; then
_err "invalid domain "
root_domain_id = $( _get_root_domain_id " $fulldomain " )
if [ -z " $root_domain_id " ] ; then
_err " Unable to determine root domain ID for $fulldomain "
return 1
fi
_debug root_domain_id " $root_domain_id "
_debug _sub_domain " $_sub_domain "
_debug _domain " $_domain "
# Always get the subdomain part first
sub_domain = $( _get_sub_domain " $fulldomain " " $root_domain_id " )
_debug sub_domain " $sub_domain "
_info "Adding record"
if _exoscale_rest POST " domains/ $_domain_id /records " " {\"record\":{\"name\":\" $_sub_domain \",\"record_type\":\"TXT\",\"content\":\" $txtvalue \",\"ttl\":120}} " " $_domain_token " ; then
if _contains " $response " " $txtvalue " ; then
_info "Added, OK"
return 0
fi
# Build the record name properly
if [ -z " $sub_domain " ] ; then
record_name = "_acme-challenge"
else
record_name = " _acme-challenge. $sub_domain "
fi
_err "Add txt record error."
return 1
payload = $( printf '{"name":"%s","type":"TXT","content":"%s","ttl":120}' " $record_name " " $txtvalue " )
_debug payload " $payload "
response = $( _exoscale_rest POST " /dns-domain/ ${ root_domain_id } /record " " $payload " )
if _contains " $response " "\"id\"" ; then
_info "TXT record added successfully."
return 0
else
_err " Error adding TXT record: $response "
return 1
fi
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_exoscale_rm( ) {
fulldomain = $1
txtvalue = $2
if ! _checkAuth; then
_debug "Using Exoscale DNS v2 API for removal"
_debug fulldomain " $fulldomain "
if ! _check_auth; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root " $fulldomain " ; then
_err "invalid domain "
root_domain_id = $( _get_root_domain_id " $fulldomain " )
if [ -z " $root_domain_id " ] ; then
_err " Unable to determine root domain ID for $fulldomain "
return 1
fi
_debug _sub_domain " $_sub_domain "
_debug _domain " $_domain "
_debug "Getting txt records"
_exoscale_rest GET " domains/ ${ _domain_id } /records?type=TXT&name= $_sub_domain " "" " $_domain_token "
if _contains " $response " " \"name\":\" $_sub_domain \" " >/dev/null; then
_record_id = $( echo " $response " | tr '{' "\n" | grep " \"content\":\" $txtvalue \" " | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \" )
record_name = "_acme-challenge"
sub_domain = $( _get_sub_domain " $fulldomain " " $root_domain_id " )
if [ -n " $sub_domain " ] ; then
record_name = " _acme-challenge. $sub_domain "
fi
if [ -z " $_record_id " ] ; then
_err "Can not get record id to remove."
record_id = $( _find_record_id " $root_domain_id " " $record_name " )
if [ -z " $record_id " ] ; then
_err "TXT record not found for deletion."
return 1
fi
_debug " Deleting record $_record_id "
if ! _exoscale_rest DELETE " domains/ $_domain_id /records/ $_record_id " "" " $_domain_token " ; then
_err "Delete record error."
response = $( _exoscale_rest DELETE " /dns-domain/ $root_domain_id /record/ $record_id " )
if _contains " $response " "\"state\":\"success\"" ; then
_info "TXT record deleted successfully."
return 0
else
_err " Error deleting TXT record: $response "
return 1
fi
return 0
}
#################### Private functions below ########################## ########
######## Private helpers ########
_checkA uth( ) {
_check_a uth( ) {
EXOSCALE_API_KEY = " ${ EXOSCALE_API_KEY :- $( _readaccountconf_mutable EXOSCALE_API_KEY) } "
EXOSCALE_SECRET_KEY = " ${ EXOSCALE_SECRET_KEY :- $( _readaccountconf_mutable EXOSCALE_SECRET_KEY) } "
if [ -z " $EXOSCALE_API_KEY " ] || [ -z " $EXOSCALE_SECRET_KEY " ] ; then
EXOSCALE_API_KEY = ""
EXOSCALE_SECRET_KEY = ""
_err "You don't specify Exoscale application key and application secret yet."
_err "Please create you key and try again."
_err "EXOSCALE_API_KEY and EXOSCALE_SECRET_KEY must be set."
return 1
fi
_saveaccountconf_mutable EXOSCALE_API_KEY " $EXOSCALE_API_KEY "
_saveaccountconf_mutable EXOSCALE_SECRET_KEY " $EXOSCALE_SECRET_KEY "
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
# _domain_token=sdjkglgdfewsdfg
_get_root( ) {
if ! _exoscale_rest GET "domains" ; then
return 1
fi
_get_root_domain_id( ) {
domain = $1
i = 2
p = 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 _contains " $response " " \"name\":\" $h \" " >/dev/null; then
_domain_id = $( echo " $response " | tr '{' "\n" | grep " \"name\":\" $h \" " | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \" )
_domain_token = $( echo " $response " | tr '{' "\n" | grep " \"name\":\" $h \" " | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" )
if [ " $_domain_token " ] && [ " $_domain_id " ] ; then
_sub_domain = $( printf "%s" " $domain " | cut -d . -f 1-" $p " )
_domain = $h
return 0
candidate = $( printf "%s" " $domain " | cut -d . -f " ${ i } -100 " )
[ -z " $candidate " ] && return 1
_debug " Trying root domain candidate: $candidate "
domains = $( _exoscale_rest GET "/dns-domain" )
# Extract from dns-domains array
result = $( echo " $domains " | _egrep_o '"dns-domains":\[.*\]' | _egrep_o '\{"id":"[^"]*","created-at":"[^"]*","unicode-name":"[^"]*"\}' | while read -r item; do
name = $( echo " $item " | _egrep_o '"unicode-name":"[^"]*"' | cut -d'"' -f4)
id = $( echo " $item " | _egrep_o '"id":"[^"]*"' | cut -d'"' -f4)
if [ " $name " = " $candidate " ] ; then
echo " $id "
break
fi
return 1
done )
if [ -n " $result " ] ; then
echo " $result "
return 0
fi
p = $i
i = $( _math " $i " + 1)
done
return 1
}
# returns response
_get_sub_domain( ) {
fulldomain = $1
root_id = $2
root_info = $( _exoscale_rest GET " /dns-domain/ $root_id " )
_debug root_info " $root_info "
root_name = $( echo " $root_info " | _egrep_o "\"unicode-name\":\"[^\"]*\"" | cut -d\" -f4)
sub = ${ fulldomain %%. " $root_name " }
if [ " $sub " = "_acme-challenge" ] ; then
echo ""
else
# Remove _acme-challenge. prefix to get the actual subdomain
echo " ${ sub #_acme-challenge. } "
fi
}
_find_record_id( ) {
root_id = $1
name = $2
records = $( _exoscale_rest GET " /dns-domain/ $root_id /record " )
# Convert search name to lowercase for case-insensitive matching
name_lower = $( echo " $name " | tr '[:upper:]' '[:lower:]' )
echo " $records " | _egrep_o '\{[^}]*"name":"[^"]*"[^}]*\}' | while read -r record; do
record_name = $( echo " $record " | _egrep_o '"name":"[^"]*"' | cut -d'"' -f4)
record_name_lower = $( echo " $record_name " | tr '[:upper:]' '[:lower:]' )
if [ " $record_name_lower " = " $name_lower " ] ; then
echo " $record " | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d'"' -f4
break
fi
done
}
_exoscale_sign( ) {
k = $1
shift
hex_key = $( printf %b " $k " | _hex_dump | tr -d ' ' )
printf %s " $@ " | _hmac sha256 " $hex_key "
}
_exoscale_rest( ) {
method = $1
path = " $2 "
data = " $3 "
token = " $4 "
request_url = " $EXOSCALE_API / $path "
_debug " $path "
path = $2
data = $3
export _H1 = "Accept: application/json"
url = " ${ EXOSCALE_API } ${ path } "
expiration = $( _math " $( date +%s) " + 300) # 5m from now
if [ " $token " ] ; then
export _H2 = " X-DNS-Domain-Token: $token "
else
export _H2 = " X-DNS-Token: $EXOSCALE_API_KEY : $EXOSCALE_SECRET_KEY "
fi
# Build the message with the actual body or empty line
message = $( printf "%s %s\n%s\n\n\n%s" " $method " " /v2 $path " " $data " " $expiration " )
signature = $( _exoscale_sign " $EXOSCALE_SECRET_KEY " " $message " | _base64)
auth = " EXO2-HMAC-SHA256 credential= ${ EXOSCALE_API_KEY } ,expires= ${ expiration } ,signature= ${ signature } "
_debug " API request: $method $url "
_debug " Signed message: [ $message ] "
_debug " Authorization header: [ $auth ] "
export _H1 = "Accept: application/json"
export _H2 = " Authorization: ${ auth } "
if [ " $data " ] || [ " $method " = "DELETE" ] ; then
export _H3 = "Content-Type: application/json"
_debug data " $data "
response = " $( _post " $data " " $request_url " "" " $method " ) "
response = " $( _post " $data " " $url " "" " $method " ) "
else
response = " $( _get " $request_url " "" "" " $method " ) "
response = " $( _get " $url " "" "" " $method " ) "
fi
if [ " $? " != "0" ] ; then
_err " error $request_url "
# shellcheck disable=SC2181
if [ " $? " -ne 0 ] ; then
_err " error $url "
return 1
fi
_debug2 response " $response "
echo " $response "
return 0
}