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.

187 lines
5.5 KiB

  1. #!/usr/bin/env sh
  2. ################################################################################
  3. # ACME.sh 3rd party DNS API plugin for ArtFiles.de
  4. ################################################################################
  5. # Author: Martin Arndt, https://troublezone.net/
  6. # Released: 2022-02-27
  7. # Issues: https://github.com/acmesh-official/acme.sh/issues/XXXX
  8. ################################################################################
  9. # Usage:
  10. # 1. export AF_API_USERNAME='api12345678'
  11. # 2. export AF_API_PASSWORD='apiPassword'
  12. # 3. acme.sh --issue -d example.com --dns dns_artfiles
  13. ################################################################################
  14. ########## API configuration ###################################################
  15. AF_API_SUCCESS='status":"OK'
  16. AF_URL_DCP='https://dcp.c.artfiles.de/api/'
  17. AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
  18. AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
  19. ########## Public functions ####################################################
  20. # Adds a new TXT record for given ACME challenge value & domain.
  21. # Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
  22. dns_artfiles_add()
  23. {
  24. domain="$1"
  25. txtValue="$2"
  26. _info 'Using ArtFiles.de DNS addition API…'
  27. _debug 'Domain' "$domain"
  28. _debug 'txtValue' "$txtValue"
  29. _set_credentials
  30. _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
  31. _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
  32. _set_headers
  33. _get_zone "$domain"
  34. _dns 'GET'
  35. if ! _contains "$response" 'TXT'; then
  36. _err 'Retrieving TXT records failed.'
  37. return 1
  38. fi
  39. _clean_records
  40. _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
  41. if ! _contains "$response" "$AF_API_SUCCESS"; then
  42. _err 'Adding ACME challenge value failed.'
  43. return 1
  44. fi
  45. }
  46. # Removes the existing TXT record for given ACME challenge value & domain.
  47. # Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
  48. dns_artfiles_rm()
  49. {
  50. domain="$1"
  51. txtValue="$2"
  52. _info 'Using ArtFiles.de DNS removal API…'
  53. _debug 'Domain' "$domain"
  54. _debug 'txtValue' "$txtValue"
  55. _set_credentials
  56. _set_headers
  57. _get_zone "$domain"
  58. if ! _dns 'GET'; then
  59. return 1
  60. fi
  61. if ! _contains "$response" "$txtValue"; then
  62. _err 'Retrieved TXT records are missing given ACME challenge value.'
  63. return 1
  64. fi
  65. _clean_records
  66. response="$(printf -- '%s' "$response" | sed '$d')"
  67. _dns 'SET' "$response"
  68. if ! _contains "$response" "$AF_API_SUCCESS"; then
  69. _err 'Removing ACME challenge value failed.'
  70. return 1
  71. fi
  72. }
  73. ########## Private functions ###################################################
  74. # Cleans awful TXT records response of ArtFiles's API & pretty prints it.
  75. # Usage: _clean_records
  76. _clean_records()
  77. {
  78. _info 'Cleaning TXT records…'
  79. # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
  80. # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
  81. # from '\"' & turn '\n' into real LF characters.
  82. # Yup, awful API to use - but that's all we got to get this working, so… ;)
  83. _debug2 'Raw ' "$response"
  84. response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
  85. _debug2 'Clean' "$response"
  86. }
  87. # Executes an HTTP GET or POST request for getting or setting DNS records,
  88. # containing given payload upon POST.
  89. # Usage: _dns [GET | SET] [payload]
  90. _dns()
  91. {
  92. _info 'Executing HTTP request…'
  93. action="$1"
  94. payload="$(printf -- '%s' "$2" | _url_encode)"
  95. url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
  96. if [ "$action" = 'SET' ]; then
  97. _debug2 'Payload' "$payload"
  98. response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
  99. else
  100. response="$(_get "$url" '' 10)"
  101. fi
  102. if ! _contains "$response" "$AF_API_SUCCESS"; then
  103. _err "DNS API error: $response"
  104. return 1
  105. fi
  106. _debug 'Response' "$response"
  107. return 0
  108. }
  109. # Gets the root domain zone for given domain.
  110. # Usage: _get_zone _acme-challenge.www.example.com
  111. _get_zone()
  112. {
  113. fqdn="$1"
  114. domains="$(_get "$AF_URL_DOMAINS" '' 10)"
  115. _info 'Getting domain zone…'
  116. _debug2 'FQDN' "$fqdn"
  117. _debug2 'Domains' "$domains"
  118. while _contains "$fqdn" "."; do
  119. if _contains "$domains" "$fqdn"; then
  120. domain="$fqdn"
  121. _info "Found root domain zone: $domain"
  122. break
  123. else
  124. fqdn="${fqdn#*.}"
  125. _debug2 'FQDN' "$fqdn"
  126. fi
  127. done
  128. if [ "$domain" = "$fqdn" ]; then
  129. return 0
  130. fi
  131. _err 'Couldn'\''t find root domain zone.'
  132. return 1
  133. }
  134. # Sets the credentials for accessing ArtFiles's API
  135. # Usage: _set_credentials
  136. _set_credentials()
  137. {
  138. _info 'Setting credentials…'
  139. AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
  140. AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
  141. if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
  142. _err 'Missing ArtFiles.de username and/or password.'
  143. _err 'Please ensure both are set via export command & try again.'
  144. return 1
  145. fi
  146. }
  147. # Adds the HTTP Authorization & Content-Type headers to a follow-up request.
  148. # Usage: _set_headers
  149. _set_headers()
  150. {
  151. _info 'Setting headers…'
  152. export _H1="$(printf -- 'Authorization: Basic %s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
  153. export _H2='Content-Type: application/json'
  154. }