]> git.proxmox.com Git - mirror_acme.sh.git/blob - dnsapi/dns_huaweicloud.sh
fix: fix shellcheck
[mirror_acme.sh.git] / dnsapi / dns_huaweicloud.sh
1 #!/usr/bin/env sh
2
3 # HUAWEICLOUD_Username
4 # HUAWEICLOUD_Password
5 # HUAWEICLOUD_DomainName
6
7 iam_api="https://iam.myhuaweicloud.com"
8 dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work
9
10 ######## Public functions #####################
11
12 # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
13 # Used to add txt record
14 #
15 # Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/zh-cn_topic_0132421999.html
16 #
17 # About "DomainName" parameters see: https://support.huaweicloud.com/api-iam/iam_01_0006.html
18 #
19
20 dns_huaweicloud_add() {
21 fulldomain=$1
22 txtvalue=$2
23
24 HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
25 HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
26 HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
27
28 # Check information
29 if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
30 _err "Not enough information provided to dns_huaweicloud!"
31 return 1
32 fi
33
34 unset token # Clear token
35 token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
36 if [ -z "${token}" ]; then # Check token
37 _err "dns_api(dns_huaweicloud): Error getting token."
38 return 1
39 fi
40 _secure_debug "Access token is:" "${token}"
41
42 unset zoneid
43 zoneid="$(_get_zoneid "${token}" "${fulldomain}")"
44 if [ -z "${zoneid}" ]; then
45 _err "dns_api(dns_huaweicloud): Error getting zone id."
46 return 1
47 fi
48 _debug "Zone ID is:" "${zoneid}"
49
50 _debug "Adding Record"
51 _add_record "${token}" "${fulldomain}" "${txtvalue}"
52 ret="$?"
53 if [ "${ret}" != "0" ]; then
54 _err "dns_api(dns_huaweicloud): Error adding record."
55 return 1
56 fi
57
58 # Do saving work if all succeeded
59 _saveaccountconf_mutable HUAWEICLOUD_Username "${HUAWEICLOUD_Username}"
60 _saveaccountconf_mutable HUAWEICLOUD_Password "${HUAWEICLOUD_Password}"
61 _saveaccountconf_mutable HUAWEICLOUD_DomainName "${HUAWEICLOUD_DomainName}"
62 return 0
63 }
64
65 # Usage: fulldomain txtvalue
66 # Used to remove the txt record after validation
67 #
68 # Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/dns_api_64005.html
69 #
70
71 dns_huaweicloud_rm() {
72 fulldomain=$1
73 txtvalue=$2
74
75 HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
76 HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
77 HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
78
79 # Check information
80 if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
81 _err "Not enough information provided to dns_huaweicloud!"
82 return 1
83 fi
84
85 unset token # Clear token
86 token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
87 if [ -z "${token}" ]; then # Check token
88 _err "dns_api(dns_huaweicloud): Error getting token."
89 return 1
90 fi
91 _secure_debug "Access token is:" "${token}"
92
93 unset zoneid
94 zoneid="$(_get_zoneid "${token}" "${fulldomain}")"
95 if [ -z "${zoneid}" ]; then
96 _err "dns_api(dns_huaweicloud): Error getting zone id."
97 return 1
98 fi
99 _debug "Zone ID is:" "${zoneid}"
100
101 record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
102 _recursive_rm_record "${token}" "${fulldomain}" "${zoneid}" "${record_id}"
103 ret="$?"
104 if [ "${ret}" != "0" ]; then
105 _err "dns_api(dns_huaweicloud): Error removing record."
106 return 1
107 fi
108
109 return 0
110 }
111
112 ################### Private functions below ##################################
113
114
115 # _recursive_rm_record
116 # remove all records from the record set
117 #
118 # _token=$1
119 # _domain=$2
120 # _zoneid=$3
121 # _record_id=$4
122 #
123 # Returns 0 on success
124 _recursive_rm_record() {
125 _token=$1
126 _domain=$2
127 _zoneid=$3
128 _record_id=$4
129
130 # Most likely to have problems will huaweicloud side if more than 50 attempts but still cannot fully remove the record set
131 # Maybe can be removed manually in the dashboard
132 _retry_cnt=50
133
134 # Remove all records
135 # Therotically HuaweiCloud does not allow more than one record set
136 # But remove them recurringly to increase robusty
137
138 while [ "${_record_id}" != "0" ] && [ "${_retry_cnt}" != "0" ]; do
139 _debug "Removing Record"
140 _retry_cnt=$((_retry_cnt - 1))
141 _rm_record "${_token}" "${_zoneid}" "${_record_id}"
142 _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${_zoneid}")"
143 _debug2 "Checking record exists: record_id=${_record_id}"
144 done
145
146 # Check if retry count is reached
147 if [ "${_retry_cnt}" = "0" ]; then
148 _debug "Failed to remove record after 50 attempts, please try removing it manually in the dashboard"
149 return 1
150 fi
151
152 return 0
153 }
154
155 # _get_zoneid
156 #
157 # _token=$1
158 # _domain_string=$2
159 #
160 # printf "%s" "${_zoneid}"
161 _get_zoneid() {
162 _token=$1
163 _domain_string=$2
164 export _H1="X-Auth-Token: ${_token}"
165
166 i=1
167 while true; do
168 h=$(printf "%s" "${_domain_string}" | cut -d . -f "$i"-100)
169 if [ -z "$h" ]; then
170 #not valid
171 return 1
172 fi
173 _debug "$h"
174 response=$(_get "${dns_api}/v2/zones?name=${h}")
175 _debug2 "$response"
176 if _contains "${response}" '"id"'; then
177 zoneidlist=$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
178 zonenamelist=$(echo "${response}" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
179 _debug2 "Return Zone ID(s):" "${zoneidlist}"
180 _debug2 "Return Zone Name(s):" "${zonenamelist}"
181 zoneidnum=0
182 zoneidcount=$(echo "${zoneidlist}" | grep -c '^')
183 _debug "Retund Zone ID(s) Count:" "${zoneidcount}"
184 while [ "${zoneidnum}" -lt "${zoneidcount}" ]; do
185 zoneidnum=$(_math "$zoneidnum" + 1)
186 _zoneid=$(echo "${zoneidlist}" | sed -n "${zoneidnum}p")
187 zonename=$(echo "${zonenamelist}" | sed -n "${zoneidnum}p")
188 _debug "Check Zone Name" "${zonename}"
189 if [ "${zonename}" = "${h}." ]; then
190 _debug "Get Zone ID Success."
191 _debug "ZoneID:" "${_zoneid}"
192 printf "%s" "${_zoneid}"
193 return 0
194 fi
195 done
196 fi
197 i=$(_math "$i" + 1)
198 done
199 return 1
200 }
201
202 _get_recordset_id() {
203 _token=$1
204 _domain=$2
205 _zoneid=$3
206 export _H1="X-Auth-Token: ${_token}"
207
208 response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}")
209 if _contains "${response}" '"id"'; then
210 _id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
211 printf "%s" "${_id}"
212 return 0
213 fi
214 printf "%s" "0"
215 return 1
216 }
217
218 _add_record() {
219 _token=$1
220 _domain=$2
221 _txtvalue=$3
222
223 # Get Existing Records
224 export _H1="X-Auth-Token: ${_token}"
225 response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}")
226
227 _debug2 "${response}"
228 _exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')
229 _debug "${_exist_record}"
230
231 # Check if record exist
232 # Generate body data
233 if [ -z "${_exist_record}" ]; then
234 _post_body="{
235 \"name\": \"${_domain}.\",
236 \"description\": \"ACME Challenge\",
237 \"type\": \"TXT\",
238 \"ttl\": 1,
239 \"records\": [
240 \"\\\"${_txtvalue}\\\"\"
241 ]
242 }"
243 else
244 _post_body="{
245 \"name\": \"${_domain}.\",
246 \"description\": \"ACME Challenge\",
247 \"type\": \"TXT\",
248 \"ttl\": 1,
249 \"records\": [
250 ${_exist_record},
251 \"\\\"${_txtvalue}\\\"\"
252 ]
253 }"
254 fi
255
256 _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
257 _debug "Record Set ID is:" "${_record_id}"
258
259 # Remove all records
260 _recursive_rm_record "${token}" "${_domain}" "${_zoneid}" "${_record_id}"
261 ret="$?"
262 if [ "${ret}" != "0" ]; then
263 return 1
264 fi
265
266 # Add brand new records with all old and new records
267 export _H2="Content-Type: application/json"
268 export _H1="X-Auth-Token: ${_token}"
269
270 _debug2 "${_post_body}"
271 _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
272 _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
273 if [ "$_code" != "202" ]; then
274 _err "dns_huaweicloud: http code ${_code}"
275 return 1
276 fi
277 return 0
278 }
279
280 # _rm_record $token $zoneid $recordid
281 # assume ${dns_api} exist
282 # no output
283 # return 0
284 _rm_record() {
285 _token=$1
286 _zone_id=$2
287 _record_id=$3
288
289 export _H2="Content-Type: application/json"
290 export _H1="X-Auth-Token: ${_token}"
291
292 _post "" "${dns_api}/v2/zones/${_zone_id}/recordsets/${_record_id}" false "DELETE" >/dev/null
293 return $?
294 }
295
296 _get_token() {
297 _username=$1
298 _password=$2
299 _domain_name=$3
300
301 _debug "Getting Token"
302 body="{
303 \"auth\": {
304 \"identity\": {
305 \"methods\": [
306 \"password\"
307 ],
308 \"password\": {
309 \"user\": {
310 \"name\": \"${_username}\",
311 \"password\": \"${_password}\",
312 \"domain\": {
313 \"name\": \"${_domain_name}\"
314 }
315 }
316 }
317 },
318 \"scope\": {
319 \"project\": {
320 \"name\": \"ap-southeast-1\"
321 }
322 }
323 }
324 }"
325 export _H1="Content-Type: application/json;charset=utf8"
326 _post "${body}" "${iam_api}/v3/auth/tokens" >/dev/null
327 _code=$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")
328 _token=$(grep "^X-Subject-Token" "$HTTP_HEADER" | cut -d " " -f 2-)
329 _secure_debug "${_code}"
330 printf "%s" "${_token}"
331 return 0
332 }