You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
9.3 KiB

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