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.

335 lines
10 KiB

  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_yandex360_info='Yandex 360 for Business DNS API.
  4. Yandex 360 for Business is a digital environment for effective collaboration.
  5. Site: https://360.yandex.com/
  6. Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360
  7. Options:
  8. YANDEX360_CLIENT_ID OAuth 2.0 ClientID
  9. YANDEX360_CLIENT_SECRET OAuth 2.0 Client secret
  10. YANDEX360_ORG_ID Organization ID
  11. OptionsAlt:
  12. YANDEX360_ACCESS_TOKEN OAuth 2.0 Access token. Optional.
  13. Issues: https://github.com/acmesh-official/acme.sh/issues/5213
  14. Author: <Als@admin.ru.net>
  15. '
  16. YANDEX360_API_BASE='https://api360.yandex.net/directory/v1'
  17. YANDEX360_OAUTH_BASE='https://oauth.yandex.ru'
  18. ######## Public functions #####################
  19. dns_yandex360_add() {
  20. fulldomain=$1
  21. txtvalue=$2
  22. _info 'Using Yandex 360 DNS API'
  23. if ! _check_variables; then
  24. return 1
  25. fi
  26. if ! _get_root "$fulldomain"; then
  27. return 1
  28. fi
  29. sub_domain=$(echo "$fulldomain" | sed "s/\.$root_domain$//")
  30. _debug 'Adding Yandex 360 DNS record for subdomain' "$sub_domain"
  31. dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns"
  32. data='{"name":"'"$sub_domain"'","type":"TXT","ttl":60,"text":"'"$txtvalue"'"}'
  33. response="$(_post "$data" "$dns_api_url" '' 'POST' 'application/json')"
  34. response="$(echo "$response" | _normalizeJson)"
  35. if _contains "$response" 'recordId'; then
  36. return 0
  37. else
  38. _debug 'Response' "$response"
  39. return 1
  40. fi
  41. }
  42. dns_yandex360_rm() {
  43. fulldomain=$1
  44. txtvalue=$2
  45. _info 'Using Yandex 360 DNS API'
  46. if ! _check_variables; then
  47. return 1
  48. fi
  49. if ! _get_root "$fulldomain"; then
  50. return 1
  51. fi
  52. _debug 'Retrieving 100 records from Yandex 360 DNS'
  53. dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns?perPage=100"
  54. response="$(_get "$dns_api_url" '' '')"
  55. response="$(echo "$response" | _normalizeJson)"
  56. if ! _contains "$response" "$txtvalue"; then
  57. _info 'DNS record not found. Nothing to remove.'
  58. _debug 'Response' "$response"
  59. return 1
  60. fi
  61. record_id=$(
  62. echo "$response" |
  63. _egrep_o '\{[^}]*'"${txtvalue}"'[^}]*\}' |
  64. _egrep_o '"recordId":[0-9]*' |
  65. cut -d':' -f2
  66. )
  67. if [ -z "$record_id" ]; then
  68. _err 'Unable to get record ID to remove'
  69. return 1
  70. fi
  71. _debug 'Removing DNS record' "$record_id"
  72. delete_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns/${record_id}"
  73. response="$(_post '' "$delete_url" '' 'DELETE')"
  74. response="$(echo "$response" | _normalizeJson)"
  75. if _contains "$response" '{}'; then
  76. return 0
  77. else
  78. _debug 'Response' "$response"
  79. return 1
  80. fi
  81. }
  82. #################### Private functions below ##################################
  83. _check_variables() {
  84. YANDEX360_CLIENT_ID="${YANDEX360_CLIENT_ID:-$(_readaccountconf_mutable YANDEX360_CLIENT_ID)}"
  85. YANDEX360_CLIENT_SECRET="${YANDEX360_CLIENT_SECRET:-$(_readaccountconf_mutable YANDEX360_CLIENT_SECRET)}"
  86. YANDEX360_ORG_ID="${YANDEX360_ORG_ID:-$(_readaccountconf_mutable YANDEX360_ORG_ID)}"
  87. YANDEX360_ACCESS_TOKEN="${YANDEX360_ACCESS_TOKEN:-$(_readaccountconf_mutable YANDEX360_ACCESS_TOKEN)}"
  88. YANDEX360_REFRESH_TOKEN="${YANDEX360_REFRESH_TOKEN:-$(_readaccountconf_mutable YANDEX360_REFRESH_TOKEN)}"
  89. if [ -z "$YANDEX360_ORG_ID" ]; then
  90. _err '========================================='
  91. _err ' ERROR'
  92. _err '========================================='
  93. _err "A required environment variable YANDEX360_ORG_ID is not set"
  94. _err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
  95. _err '========================================='
  96. return 1
  97. fi
  98. _saveaccountconf_mutable YANDEX360_ORG_ID "$YANDEX360_ORG_ID"
  99. if [ -n "$YANDEX360_ACCESS_TOKEN" ]; then
  100. _info '========================================='
  101. _info ' ATTENTION'
  102. _info '========================================='
  103. _info 'A manually provided Yandex 360 access token has been detected, which is not recommended.'
  104. _info 'Please note that this token is valid for a limited time after issuance.'
  105. _info 'It is recommended to obtain the token interactively using acme.sh for one-time setup.'
  106. _info 'Subsequent token renewals will be handled automatically.'
  107. _info 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
  108. _info '========================================='
  109. _saveaccountconf_mutable YANDEX360_ACCESS_TOKEN "$YANDEX360_ACCESS_TOKEN"
  110. export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
  111. elif [ -z "$YANDEX360_CLIENT_ID" ] || [ -z "$YANDEX360_CLIENT_SECRET" ]; then
  112. _err '========================================='
  113. _err ' ERROR'
  114. _err '========================================='
  115. _err 'The required environment variables YANDEX360_CLIENT_ID and YANDEX360_CLIENT_SECRET are not set.'
  116. _err 'Alternatively, you can set YANDEX360_ACCESS_TOKEN environment variable.'
  117. _err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
  118. _err '========================================='
  119. return 1
  120. else
  121. _saveaccountconf_mutable YANDEX360_CLIENT_ID "$YANDEX360_CLIENT_ID"
  122. _saveaccountconf_mutable YANDEX360_CLIENT_SECRET "$YANDEX360_CLIENT_SECRET"
  123. if [ -n "$YANDEX360_REFRESH_TOKEN" ]; then
  124. _debug 'Refresh token found. Attempting to refresh access token.'
  125. if ! _refresh_token; then
  126. if ! _get_token; then
  127. return 1
  128. fi
  129. fi
  130. else
  131. if ! _get_token; then
  132. return 1
  133. fi
  134. fi
  135. fi
  136. return 0
  137. }
  138. _get_token() {
  139. _info "$(_red '=========================================')"
  140. _info "$(_red ' NOTICE')"
  141. _info "$(_red '=========================================')"
  142. _info "$(_red 'Before using the Yandex 360 API, you need to complete an authorization procedure.')"
  143. _info "$(_red 'The initial access token is obtained interactively and is a one-time operation.')"
  144. _info "$(_red 'Subsequent API requests will be handled automatically.')"
  145. _info "$(_red '=========================================')"
  146. _info 'Initiating device authorization flow'
  147. device_code_url="${YANDEX360_OAUTH_BASE}/device/code"
  148. hostname=$(uname -n)
  149. data="client_id=$YANDEX360_CLIENT_ID&device_id=acme.sh ${hostname}&device_name=acme.sh ${hostname}"
  150. response="$(_post "$data" "$device_code_url" '' 'POST')"
  151. response="$(echo "$response" | _normalizeJson)"
  152. if ! _contains "$response" 'device_code'; then
  153. _err 'Failed to get device code'
  154. _debug 'Response' "$response"
  155. return 1
  156. fi
  157. device_code=$(
  158. echo "$response" |
  159. _egrep_o '"device_code":"[^"]*"' |
  160. cut -d: -f2 |
  161. tr -d '"'
  162. )
  163. _debug 'Device code' "$device_code"
  164. user_code=$(
  165. echo "$response" |
  166. _egrep_o '"user_code":"[^"]*"' |
  167. cut -d: -f2 |
  168. tr -d '"'
  169. )
  170. _debug 'User code' "$user_code"
  171. verification_url=$(
  172. echo "$response" |
  173. _egrep_o '"verification_url":"[^"]*"' |
  174. cut -d: -f2- |
  175. tr -d '"'
  176. )
  177. _debug 'Verification URL' "$verification_url"
  178. interval=$(
  179. echo "$response" |
  180. _egrep_o '"interval":[[:space:]]*[0-9]+' |
  181. cut -d: -f2
  182. )
  183. _debug 'Polling interval' "$interval"
  184. _info "$(__red 'Please visit '"$verification_url"' and log in as an organization administrator')"
  185. _info "$(__red 'Once logged in, enter the code: '"$user_code"' on the page from the previous step')"
  186. _info "$(__red 'Waiting for authorization...')"
  187. _debug 'Polling for token'
  188. token_url="${YANDEX360_OAUTH_BASE}/token"
  189. while true; do
  190. data="grant_type=device_code&code=$device_code&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET"
  191. response="$(_post "$data" "$token_url" '' 'POST')"
  192. response="$(echo "$response" | _normalizeJson)"
  193. if _contains "$response" 'access_token'; then
  194. YANDEX360_ACCESS_TOKEN=$(
  195. echo "$response" |
  196. _egrep_o '"access_token":"[^"]*"' |
  197. cut -d: -f2- |
  198. tr -d '"'
  199. )
  200. YANDEX360_REFRESH_TOKEN=$(
  201. echo "$response" |
  202. _egrep_o '"refresh_token":"[^"]*"' |
  203. cut -d: -f2- |
  204. tr -d '"'
  205. )
  206. _secure_debug 'Obtained access token' "$YANDEX360_ACCESS_TOKEN"
  207. _secure_debug 'Obtained refresh token' "$YANDEX360_REFRESH_TOKEN"
  208. _saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN"
  209. export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
  210. _info 'Access token obtained successfully'
  211. return 0
  212. elif _contains "$response" 'authorization_pending'; then
  213. _debug 'Response' "$response"
  214. _debug "Authorization pending. Waiting $interval seconds before next attempt."
  215. _sleep "$interval"
  216. else
  217. _debug 'Response' "$response"
  218. _err 'Failed to get access token'
  219. return 1
  220. fi
  221. done
  222. }
  223. _refresh_token() {
  224. token_url="${YANDEX360_OAUTH_BASE}/token"
  225. data="grant_type=refresh_token&refresh_token=$YANDEX360_REFRESH_TOKEN&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET"
  226. response="$(_post "$data" "$token_url" '' 'POST')"
  227. response="$(echo "$response" | _normalizeJson)"
  228. if _contains "$response" 'access_token'; then
  229. YANDEX360_ACCESS_TOKEN=$(
  230. echo "$response" |
  231. _egrep_o '"access_token":"[^"]*"' |
  232. cut -d: -f2 |
  233. tr -d '"'
  234. )
  235. YANDEX360_REFRESH_TOKEN=$(
  236. echo "$response" |
  237. _egrep_o '"refresh_token":"[^"]*"' |
  238. cut -d: -f2- |
  239. tr -d '"'
  240. )
  241. _secure_debug 'Received access token' "$YANDEX360_ACCESS_TOKEN"
  242. _secure_debug 'Received refresh token' "$YANDEX360_REFRESH_TOKEN"
  243. _saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN"
  244. export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
  245. _info 'Access token refreshed successfully'
  246. return 0
  247. else
  248. _info 'Failed to refresh token. Will attempt to obtain a new one.'
  249. _debug 'Response' "$response"
  250. return 1
  251. fi
  252. }
  253. _get_root() {
  254. domain="$1"
  255. i=1
  256. while true; do
  257. h=$(echo "$domain" | cut -d . -f "$i"-)
  258. _debug "Checking domain: $h"
  259. if [ -z "$h" ]; then
  260. _err "Could not determine root domain"
  261. return 1
  262. fi
  263. dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${h}/dns"
  264. response="$(_get "$dns_api_url" '' '')"
  265. response="$(echo "$response" | _normalizeJson)"
  266. _debug 'Response' "$response"
  267. if _contains "$response" '"total":'; then
  268. root_domain="$h"
  269. _debug 'Root domain found' "$root_domain"
  270. return 0
  271. fi
  272. i=$(_math "$i" + 1)
  273. done
  274. }