dns_virakcloud.sh 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_virakcloud_info='VirakCloud DNS API
  4. Site: VirakCloud.com
  5. Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_virakcloud
  6. Options:
  7. VIRAKCLOUD_API_TOKEN VirakCloud API Bearer Token
  8. '
  9. VIRAKCLOUD_API_URL="https://public-api.virakcloud.com/dns"
  10. ######## Public functions #####################
  11. #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  12. #Used to add txt record
  13. dns_virakcloud_add() {
  14. fulldomain=$1
  15. txtvalue=$2
  16. VIRAKCLOUD_API_TOKEN="${VIRAKCLOUD_API_TOKEN:-$(_readaccountconf_mutable VIRAKCLOUD_API_TOKEN)}"
  17. if [ -z "$VIRAKCLOUD_API_TOKEN" ]; then
  18. _err "You haven't configured your VirakCloud API token yet."
  19. _err "Please set VIRAKCLOUD_API_TOKEN environment variable or run:"
  20. _err " export VIRAKCLOUD_API_TOKEN=\"your-api-token\""
  21. return 1
  22. fi
  23. _saveaccountconf_mutable VIRAKCLOUD_API_TOKEN "$VIRAKCLOUD_API_TOKEN"
  24. _debug "First detect the root zone"
  25. if ! _get_root "$fulldomain"; then
  26. http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
  27. if [ "$http_code" = "401" ]; then
  28. return 1
  29. fi
  30. _err "Invalid domain"
  31. return 1
  32. fi
  33. _debug _domain "$_domain"
  34. _debug fulldomain "$fulldomain"
  35. _info "Adding TXT record"
  36. if _virakcloud_rest POST "domains/${_domain}/records" "{\"record\":\"${fulldomain}\",\"type\":\"TXT\",\"ttl\":3600,\"content\":\"${txtvalue}\"}"; then
  37. if echo "$response" | grep -q "success" || echo "$response" | grep -q "\"data\""; then
  38. _info "Added, OK"
  39. return 0
  40. elif echo "$response" | grep -q "already exists" || echo "$response" | grep -q "duplicate"; then
  41. _info "Record already exists, OK"
  42. return 0
  43. else
  44. _err "Add TXT record error."
  45. _err "Response: $response"
  46. return 1
  47. fi
  48. fi
  49. _err "Add TXT record error."
  50. return 1
  51. }
  52. #Usage: fulldomain txtvalue
  53. #Used to remove the txt record after validation
  54. dns_virakcloud_rm() {
  55. fulldomain=$1
  56. txtvalue=$2
  57. VIRAKCLOUD_API_TOKEN="${VIRAKCLOUD_API_TOKEN:-$(_readaccountconf_mutable VIRAKCLOUD_API_TOKEN)}"
  58. if [ -z "$VIRAKCLOUD_API_TOKEN" ]; then
  59. _err "You haven't configured your VirakCloud API token yet."
  60. return 1
  61. fi
  62. _debug "First detect the root zone"
  63. if ! _get_root "$fulldomain"; then
  64. http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
  65. if [ "$http_code" = "401" ]; then
  66. return 1
  67. fi
  68. _err "Invalid domain"
  69. return 1
  70. fi
  71. _debug _domain "$_domain"
  72. _debug fulldomain "$fulldomain"
  73. _debug txtvalue "$txtvalue"
  74. _info "Removing TXT record"
  75. _debug "Getting list of records to find content ID"
  76. if ! _virakcloud_rest GET "domains/${_domain}/records" ""; then
  77. return 1
  78. fi
  79. _debug2 "Records response" "$response"
  80. contentid=""
  81. # Extract innermost objects (content objects) which look like {"id":"...","content_raw":"..."}
  82. # We filter for the one containing txtvalue
  83. target_obj=$(echo "$response" | grep -o '{[^}]*}' | grep "$txtvalue" | _head_n 1)
  84. if [ -n "$target_obj" ]; then
  85. contentid=$(echo "$target_obj" | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4)
  86. fi
  87. if [ -z "$contentid" ]; then
  88. _debug "Could not find matching record ID in response"
  89. _info "Record not found, may have been already removed"
  90. return 0
  91. fi
  92. _debug contentid "$contentid"
  93. if _virakcloud_rest DELETE "domains/${_domain}/records/${fulldomain}/TXT/${contentid}" ""; then
  94. if echo "$response" | grep -q "success" || [ -z "$response" ]; then
  95. _info "Removed, OK"
  96. return 0
  97. elif echo "$response" | grep -q "not found" || echo "$response" | grep -q "404"; then
  98. _info "Record not found, OK"
  99. return 0
  100. else
  101. _err "Remove TXT record error."
  102. _err "Response: $response"
  103. return 1
  104. fi
  105. fi
  106. _err "Remove TXT record error."
  107. return 1
  108. }
  109. #################### Private functions below ##################################
  110. #_acme-challenge.www.domain.com
  111. #returns
  112. # _domain=domain.com
  113. _get_root() {
  114. domain=$1
  115. i=1
  116. p=1
  117. # Optimization: skip _acme-challenge subdomain to avoid 422 errors
  118. if echo "$domain" | grep -q "^_acme-challenge."; then
  119. i=2
  120. fi
  121. while true; do
  122. h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
  123. _debug h "$h"
  124. if [ -z "$h" ]; then
  125. return 1
  126. fi
  127. if ! _virakcloud_rest GET "domains/$h" ""; then
  128. http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
  129. if [ "$http_code" = "401" ]; then
  130. return 1
  131. fi
  132. p=$i
  133. i=$(_math "$i" + 1)
  134. continue
  135. fi
  136. if echo "$response" | grep -q "\"name\""; then
  137. _domain="$h"
  138. return 0
  139. fi
  140. p=$i
  141. i=$(_math "$i" + 1)
  142. done
  143. return 1
  144. }
  145. _virakcloud_rest() {
  146. m=$1
  147. ep="$2"
  148. data="$3"
  149. _debug "$ep"
  150. export _H1="Content-Type: application/json"
  151. export _H2="Authorization: Bearer $VIRAKCLOUD_API_TOKEN"
  152. if [ "$m" != "GET" ]; then
  153. _debug data "$data"
  154. response="$(_post "$data" "$VIRAKCLOUD_API_URL/$ep" "" "$m")"
  155. else
  156. response="$(_get "$VIRAKCLOUD_API_URL/$ep")"
  157. fi
  158. _ret="$?"
  159. if [ "$_ret" != "0" ]; then
  160. _err "error on $m $ep"
  161. return 1
  162. fi
  163. http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
  164. _debug "http response code" "$http_code"
  165. if [ "$http_code" = "401" ]; then
  166. _err "VirakCloud API returned 401 Unauthorized."
  167. _err "Your VIRAKCLOUD_API_TOKEN is invalid or expired."
  168. _err "Please check your API token and try again."
  169. return 1
  170. fi
  171. if [ "$http_code" = "403" ]; then
  172. _err "VirakCloud API returned 403 Forbidden."
  173. _err "Your API token does not have permission to access this resource."
  174. return 1
  175. fi
  176. if [ -n "$http_code" ] && [ "$http_code" -ge 400 ]; then
  177. _err "VirakCloud API error. HTTP code: $http_code"
  178. _err "Response: $response"
  179. return 1
  180. fi
  181. _debug2 response "$response"
  182. return 0
  183. }