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.

224 lines
6.3 KiB

  1. #!/usr/bin/env sh
  2. #Rcode0 API Integration
  3. #https://my.rcodezero.at/api-doc
  4. #
  5. # log into https://my.rcodezero.at/enableapi and get your ACME API Token (the ACME API token has limited
  6. # access to the REST calls needed for acme.sh only)
  7. #
  8. #RCODE0_URL="https://my.rcodezero.at"
  9. #RCODE0_API_TOKEN="0123456789ABCDEF"
  10. #RCODE0_TTL=60
  11. DEFAULT_RCODE0_URL="https://my.rcodezero.at"
  12. DEFAULT_RCODE0_TTL=60
  13. ######## Public functions #####################
  14. #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
  15. #fulldomain
  16. #txtvalue
  17. dns_rcode0_add() {
  18. fulldomain=$1
  19. txtvalue=$2
  20. RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
  21. RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
  22. RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
  23. if [ -z "$RCODE0_URL" ]; then
  24. RCODE0_URL="$DEFAULT_RCODE0_URL"
  25. fi
  26. if [ -z "$RCODE0_API_TOKEN" ]; then
  27. RCODE0_API_TOKEN=""
  28. _err "Missing Rcode0 ACME API Token."
  29. _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
  30. return 1
  31. fi
  32. if [ -z "$RCODE0_TTL" ]; then
  33. RCODE0_TTL="$DEFAULT_RCODE0_TTL"
  34. fi
  35. #save the token to the account conf file.
  36. _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
  37. if [ "$RCODE0_URL" != "$DEFAULT_RCODE0_URL" ]; then
  38. _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
  39. fi
  40. if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
  41. _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
  42. fi
  43. _debug "Detect root zone"
  44. if ! _get_root "$fulldomain"; then
  45. _err "No 'MASTER' zone for $fulldomain found at RcodeZero Anycast."
  46. return 1
  47. fi
  48. _debug _domain "$_domain"
  49. _debug "Adding record"
  50. _record_string=""
  51. _build_record_string "$txtvalue"
  52. _list_existingchallenges
  53. for oldchallenge in $_existing_challenges; do
  54. _build_record_string "$oldchallenge"
  55. done
  56. _debug "Challenges: $_existing_challenges"
  57. if [ -z "$_existing_challenges" ]; then
  58. if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"add\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
  59. _err "Add txt record error."
  60. return 1
  61. fi
  62. else
  63. # try update in case a records exists (need for wildcard certs)
  64. if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
  65. _err "Set txt record error."
  66. return 1
  67. fi
  68. fi
  69. return 0
  70. }
  71. #fulldomain txtvalue
  72. dns_rcode0_rm() {
  73. fulldomain=$1
  74. txtvalue=$2
  75. RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
  76. RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
  77. RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
  78. if [ -z "$RCODE0_URL" ]; then
  79. RCODE0_URL="$DEFAULT_RCODE0_URL"
  80. fi
  81. if [ -z "$RCODE0_API_TOKEN" ]; then
  82. RCODE0_API_TOKEN=""
  83. _err "Missing Rcode0 API Token."
  84. _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
  85. return 1
  86. fi
  87. #save the api addr and key to the account conf file.
  88. _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
  89. _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
  90. if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
  91. _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
  92. fi
  93. if [ -z "$RCODE0_TTL" ]; then
  94. RCODE0_TTL="$DEFAULT_RCODE0_TTL"
  95. fi
  96. _debug "Detect root zone"
  97. if ! _get_root "$fulldomain"; then
  98. _err "invalid domain"
  99. return 1
  100. fi
  101. _debug "Remove record"
  102. #Enumerate existing acme challenges
  103. _list_existingchallenges
  104. if _contains "$_existing_challenges" "$txtvalue"; then
  105. #Delete all challenges (PowerDNS API does not allow to delete content)
  106. if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"delete\", \"name\": \"$fulldomain.\", \"type\": \"TXT\"}]"; then
  107. _err "Delete txt record error."
  108. return 1
  109. fi
  110. _record_string=""
  111. #If the only existing challenge was the challenge to delete: nothing to do
  112. if ! [ "$_existing_challenges" = "$txtvalue" ]; then
  113. for oldchallenge in $_existing_challenges; do
  114. #Build up the challenges to re-add, ommitting the one what should be deleted
  115. if ! [ "$oldchallenge" = "$txtvalue" ]; then
  116. _build_record_string "$oldchallenge"
  117. fi
  118. done
  119. #Recreate the existing challenges
  120. if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
  121. _err "Set txt record error."
  122. return 1
  123. fi
  124. fi
  125. else
  126. _info "Record not found, nothing to remove"
  127. fi
  128. return 0
  129. }
  130. #################### Private functions below ##################################
  131. #_acme-challenge.www.domain.com
  132. #returns
  133. # _domain=domain.com
  134. _get_root() {
  135. domain=$1
  136. i=1
  137. while true; do
  138. h=$(printf "%s" "$domain" | cut -d . -f $i-100)
  139. _debug "try to find: $h"
  140. if _rcode0_rest "GET" "/api/v1/acme/zones/$h"; then
  141. if [ "$response" = "[\"found\"]" ]; then
  142. _domain="$h"
  143. if [ -z "$h" ]; then
  144. _domain="=2E"
  145. fi
  146. return 0
  147. elif [ "$response" = "[\"not a master domain\"]" ]; then
  148. return 1
  149. fi
  150. fi
  151. if [ -z "$h" ]; then
  152. return 1
  153. fi
  154. i=$(_math $i + 1)
  155. done
  156. _debug "no matching domain for $domain found"
  157. return 1
  158. }
  159. _rcode0_rest() {
  160. method=$1
  161. ep=$2
  162. data=$3
  163. export _H1="Authorization: Bearer $RCODE0_API_TOKEN"
  164. if [ ! "$method" = "GET" ]; then
  165. _debug data "$data"
  166. response="$(_post "$data" "$RCODE0_URL$ep" "" "$method")"
  167. else
  168. response="$(_get "$RCODE0_URL$ep")"
  169. fi
  170. if [ "$?" != "0" ]; then
  171. _err "error $ep"
  172. return 1
  173. fi
  174. _debug2 response "$response"
  175. return 0
  176. }
  177. _build_record_string() {
  178. _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}"
  179. }
  180. _list_existingchallenges() {
  181. _rcode0_rest "GET" "/api/v1/acme/zones/$_domain/rrsets"
  182. _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p')
  183. _debug2 "$_existing_challenges"
  184. }