Skip to content

Commit b986b65

Browse files
committed
fix: update Exoscale DNS script
This updates the Exoscale DNS script to work with v2 of their API.
1 parent 0c9d2da commit b986b65

File tree

1 file changed

+132
-94
lines changed

1 file changed

+132
-94
lines changed

dnsapi/dns_exoscale.sh

100755100644
Lines changed: 132 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1717
dns_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
4860
dns_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

Comments
 (0)