@@ -8,169 +8,207 @@ Options:
88 EXOSCALE_SECRET_KEY API Secret key
99'
1010
11- EXOSCALE_API=https://api.exoscale.com/dns/v1
11+ EXOSCALE_API=" https://api-ch-gva-2 .exoscale.com/v2 "
1212
13- # ####### Public functions ############# ########
13+ # ####### Public functions ########
1414
1515# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
1616# Used to add txt record
1717dns_exoscale_add () {
1818 fulldomain=$1
1919 txtvalue=$2
2020
21- if ! _checkAuth; then
21+ _debug " Using Exoscale DNS v2 API"
22+ _debug fulldomain " $fulldomain "
23+ _debug txtvalue " $txtvalue "
24+
25+ if ! _check_auth; then
2226 return 1
2327 fi
2428
25- _debug " First detect the root zone "
26- if ! _get_root " $fulldomain " ; then
27- _err " invalid domain"
29+ root_domain_id= $( _get_root_domain_id " $fulldomain " )
30+ if [ -z " $root_domain_id " ] ; then
31+ _err " Unable to determine root domain ID for $fulldomain "
2832 return 1
2933 fi
34+ _debug root_domain_id " $root_domain_id "
3035
31- _debug _sub_domain " $_sub_domain "
32- _debug _domain " $_domain "
36+ # Always get the subdomain part first
37+ sub_domain=$( _get_sub_domain " $fulldomain " " $root_domain_id " )
38+ _debug sub_domain " $sub_domain "
3339
34- _info " Adding record"
35- if _exoscale_rest POST " domains/$_domain_id /records" " {\" record\" :{\" name\" :\" $_sub_domain \" ,\" record_type\" :\" TXT\" ,\" content\" :\" $txtvalue \" ,\" ttl\" :120}}" " $_domain_token " ; then
36- if _contains " $response " " $txtvalue " ; then
37- _info " Added, OK"
38- return 0
39- fi
40+ # Build the record name properly
41+ if [ -z " $sub_domain " ]; then
42+ record_name=" _acme-challenge"
43+ else
44+ record_name=" _acme-challenge.$sub_domain "
4045 fi
41- _err " Add txt record error."
42- return 1
4346
47+ payload=$( printf ' {"name":"%s","type":"TXT","content":"%s","ttl":120}' " $record_name " " $txtvalue " )
48+ _debug payload " $payload "
49+
50+ response=$( _exoscale_rest POST " /dns-domain/${root_domain_id} /record" " $payload " )
51+ if _contains " $response " " \" id\" " ; then
52+ _info " TXT record added successfully."
53+ return 0
54+ else
55+ _err " Error adding TXT record: $response "
56+ return 1
57+ fi
4458}
4559
46- # Usage: fulldomain txtvalue
47- # Used to remove the txt record after validation
4860dns_exoscale_rm () {
4961 fulldomain=$1
50- txtvalue=$2
5162
52- if ! _checkAuth; then
63+ _debug " Using Exoscale DNS v2 API for removal"
64+ _debug fulldomain " $fulldomain "
65+
66+ if ! _check_auth; then
5367 return 1
5468 fi
5569
56- _debug " First detect the root zone "
57- if ! _get_root " $fulldomain " ; then
58- _err " invalid domain"
70+ root_domain_id= $( _get_root_domain_id " $fulldomain " )
71+ if [ -z " $root_domain_id " ] ; then
72+ _err " Unable to determine root domain ID for $fulldomain "
5973 return 1
6074 fi
6175
62- _debug _sub_domain " $_sub_domain "
63- _debug _domain " $_domain "
64-
65- _debug " Getting txt records"
66- _exoscale_rest GET " domains/${_domain_id} /records?type=TXT&name=$_sub_domain " " " " $_domain_token "
67- if _contains " $response " " \" name\" :\" $_sub_domain \" " > /dev/null; then
68- _record_id=$( echo " $response " | tr ' {' " \n" | grep " \" content\" :\" $txtvalue \" " | _egrep_o " \" id\" :[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \" )
76+ record_name=" _acme-challenge"
77+ sub_domain=$( _get_sub_domain " $fulldomain " " $root_domain_id " )
78+ if [ -n " $sub_domain " ]; then
79+ record_name=" _acme-challenge.$sub_domain "
6980 fi
7081
71- if [ -z " $_record_id " ]; then
72- _err " Can not get record id to remove."
82+ record_id=$( _find_record_id " $root_domain_id " " $record_name " )
83+ if [ -z " $record_id " ]; then
84+ _err " TXT record not found for deletion."
7385 return 1
7486 fi
7587
76- _debug " Deleting record $_record_id "
77-
78- if ! _exoscale_rest DELETE " domains/$_domain_id /records/$_record_id " " " " $_domain_token " ; then
79- _err " Delete record error."
88+ response=$( _exoscale_rest DELETE " /dns-domain/$root_domain_id /record/$record_id " )
89+ if _contains " $response " " \" state\" :\" success\" " ; then
90+ _info " TXT record deleted successfully."
91+ return 0
92+ else
93+ _err " Error deleting TXT record: $response "
8094 return 1
8195 fi
82-
83- return 0
8496}
8597
86- # ################### Private functions below ########################## ########
98+ # ####### Private helpers ########
8799
88- _checkAuth () {
100+ _check_auth () {
89101 EXOSCALE_API_KEY=" ${EXOSCALE_API_KEY:- $(_readaccountconf_mutable EXOSCALE_API_KEY)} "
90102 EXOSCALE_SECRET_KEY=" ${EXOSCALE_SECRET_KEY:- $(_readaccountconf_mutable EXOSCALE_SECRET_KEY)} "
91-
92103 if [ -z " $EXOSCALE_API_KEY " ] || [ -z " $EXOSCALE_SECRET_KEY " ]; then
93- EXOSCALE_API_KEY=" "
94- EXOSCALE_SECRET_KEY=" "
95- _err " You don't specify Exoscale application key and application secret yet."
96- _err " Please create you key and try again."
104+ _err " EXOSCALE_API_KEY and EXOSCALE_SECRET_KEY must be set."
97105 return 1
98106 fi
99-
100107 _saveaccountconf_mutable EXOSCALE_API_KEY " $EXOSCALE_API_KEY "
101108 _saveaccountconf_mutable EXOSCALE_SECRET_KEY " $EXOSCALE_SECRET_KEY "
102-
103109 return 0
104110}
105111
106- # _acme-challenge.www.domain.com
107- # returns
108- # _sub_domain=_acme-challenge.www
109- # _domain=domain.com
110- # _domain_id=sdjkglgdfewsdfg
111- # _domain_token=sdjkglgdfewsdfg
112- _get_root () {
113-
114- if ! _exoscale_rest GET " domains" ; then
115- return 1
116- fi
117-
112+ _get_root_domain_id () {
118113 domain=$1
119- i=2
120- p=1
114+ i=1
121115 while true ; do
122- h=$( printf " %s" " $domain " | cut -d . -f " $i " -100)
123- _debug h " $h "
124- if [ -z " $h " ]; then
125- # not valid
126- return 1
127- fi
128-
129- if _contains " $response " " \" name\" :\" $h \" " > /dev/null; then
130- _domain_id=$( echo " $response " | tr ' {' " \n" | grep " \" name\" :\" $h \" " | _egrep_o " \" id\" :[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \" )
131- _domain_token=$( echo " $response " | tr ' {' " \n" | grep " \" name\" :\" $h \" " | _egrep_o " \" token\" :\" [^\" ]*\" " | _head_n 1 | cut -d : -f 2 | tr -d \" )
132- if [ " $_domain_token " ] && [ " $_domain_id " ]; then
133- _sub_domain=$( printf " %s" " $domain " | cut -d . -f 1-" $p " )
134- _domain=$h
135- return 0
116+ candidate=$( printf " %s" " $domain " | cut -d . -f " ${i} -100" )
117+ [ -z " $candidate " ] && return 1
118+ _debug " Trying root domain candidate: $candidate "
119+ domains=$( _exoscale_rest GET " /dns-domain" )
120+ # Extract from dns-domains array
121+ result=$( echo " $domains " | _egrep_o ' "dns-domains":\[.*\]' | _egrep_o ' \{"id":"[^"]*","created-at":"[^"]*","unicode-name":"[^"]*"\}' | while read -r item; do
122+ name=$( echo " $item " | _egrep_o ' "unicode-name":"[^"]*"' | cut -d' "' -f4)
123+ id=$( echo " $item " | _egrep_o ' "id":"[^"]*"' | cut -d' "' -f4)
124+ if [ " $name " = " $candidate " ]; then
125+ echo " $id "
126+ break
136127 fi
137- return 1
128+ done)
129+ if [ -n " $result " ]; then
130+ echo " $result "
131+ return 0
138132 fi
139- p=$i
140133 i=$( _math " $i " + 1)
141134 done
142- return 1
143135}
144136
145- # returns response
137+ _get_sub_domain () {
138+ fulldomain=$1
139+ root_id=$2
140+ root_info=$( _exoscale_rest GET " /dns-domain/$root_id " )
141+ _debug root_info " $root_info "
142+ root_name=$( echo " $root_info " | _egrep_o " \" unicode-name\" :\" [^\" ]*\" " | cut -d\" -f4)
143+ sub=${fulldomain%% ." $root_name " }
144+
145+ if [ " $sub " = " _acme-challenge" ]; then
146+ echo " "
147+ else
148+ # Remove _acme-challenge. prefix to get the actual subdomain
149+ echo " ${sub# _acme-challenge.} "
150+ fi
151+ }
152+
153+ _find_record_id () {
154+ root_id=$1
155+ name=$2
156+ records=$( _exoscale_rest GET " /dns-domain/$root_id /record" )
157+
158+ # Convert search name to lowercase for case-insensitive matching
159+ name_lower=$( echo " $name " | tr ' [:upper:]' ' [:lower:]' )
160+
161+ echo " $records " | _egrep_o ' \{[^}]*"name":"[^"]*"[^}]*\}' | while read -r record; do
162+ record_name=$( echo " $record " | _egrep_o ' "name":"[^"]*"' | cut -d' "' -f4)
163+ record_name_lower=$( echo " $record_name " | tr ' [:upper:]' ' [:lower:]' )
164+ if [ " $record_name_lower " = " $name_lower " ]; then
165+ echo " $record " | _egrep_o ' "id":"[^"]*"' | _head_n 1 | cut -d' "' -f4
166+ break
167+ fi
168+ done
169+ }
170+
171+ _exoscale_sign () {
172+ k=$1
173+ shift
174+ hex_key=$( printf %b " $k " | _hex_dump | tr -d ' ' )
175+ printf %b " $@ " | _hmac sha256 " $hex_key " hex
176+ }
177+
146178_exoscale_rest () {
147179 method=$1
148- path=" $2 "
149- data=" $3 "
150- token=" $4 "
151- request_url=" $EXOSCALE_API /$path "
152- _debug " $path "
180+ path=$2
181+ data=$3
153182
154- export _H1=" Accept: application/json"
183+ url=" ${EXOSCALE_API}${path} "
184+ expiration=$( _math " $( date +%s) " + 300) # 5m from now
155185
156- if [ " $token " ]; then
157- export _H2=" X-DNS-Domain-Token: $token "
158- else
159- export _H2=" X-DNS-Token: $EXOSCALE_API_KEY :$EXOSCALE_SECRET_KEY "
160- fi
186+ # Build the message with the actual body or empty line
187+ message=$( printf " %s %s\n%s\n\n\n%s" " $method " " /v2$path " " $data " " $expiration " )
188+ signature=$( _exoscale_sign " $EXOSCALE_SECRET_KEY " " $message " | _base64)
189+ auth=" EXO2-HMAC-SHA256 credential=${EXOSCALE_API_KEY} ,expires=${expiration} ,signature=${signature} "
190+
191+ _debug " API request: $method $url "
192+ _debug " Signed message: [$message ]"
193+ _debug " Authorization header: [$auth ]"
194+
195+ export _H1=" Accept: application/json"
196+ export _H2=" Authorization: ${auth} "
161197
162198 if [ " $data " ] || [ " $method " = " DELETE" ]; then
163199 export _H3=" Content-Type: application/json"
164200 _debug data " $data "
165- response=" $( _post " $data " " $request_url " " " " $method " ) "
201+ response=" $( _post " $data " " $url " " " " $method " ) "
166202 else
167- response=" $( _get " $request_url " " " " " " $method " ) "
203+ response=" $( _get " $url " " " " " " $method " ) "
168204 fi
169205
170- if [ " $? " != " 0" ]; then
171- _err " error $request_url "
206+ # shellcheck disable=SC2181
207+ if [ " $? " -ne 0 ]; then
208+ _err " error $url "
172209 return 1
173210 fi
174211 _debug2 response " $response "
212+ echo " $response "
175213 return 0
176214}
0 commit comments