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.

355 lines
12 KiB

  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_openstack_info='OpenStack Designate API
  4. Depends on OpenStackClient and python-desginateclient.
  5. You will require Keystone V3 credentials loaded into your environment,
  6. which could be either password or v3 application credential type.
  7. Site: docs.openstack.org/api-ref/dns/
  8. Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openstack
  9. Options:
  10. OS_AUTH_URL Auth URL. E.g. "https://keystone.example.com:5000/"
  11. OS_USERNAME Username
  12. OS_PASSWORD Password
  13. OS_PROJECT_NAME Project name
  14. OS_PROJECT_DOMAIN_NAME Project domain name. E.g. "Default"
  15. OS_USER_DOMAIN_NAME User domain name. E.g. "Default"
  16. Issues: github.com/acmesh-official/acme.sh/issues/3054
  17. Author: Andy Botting <andy@andybotting.com>
  18. '
  19. ######## Public functions #####################
  20. # Usage: dns_openstack_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  21. dns_openstack_add() {
  22. fulldomain=$1
  23. txtvalue=$2
  24. _debug fulldomain "$fulldomain"
  25. _debug txtvalue "$txtvalue"
  26. _dns_openstack_credentials || return $?
  27. _dns_openstack_check_setup || return $?
  28. _dns_openstack_find_zone || return $?
  29. _dns_openstack_get_recordset || return $?
  30. _debug _recordset_id "$_recordset_id"
  31. if [ -n "$_recordset_id" ]; then
  32. _dns_openstack_get_records || return $?
  33. _debug _records "$_records"
  34. fi
  35. _dns_openstack_create_recordset || return $?
  36. }
  37. # Usage: dns_openstack_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  38. # Remove the txt record after validation.
  39. dns_openstack_rm() {
  40. fulldomain=$1
  41. txtvalue=$2
  42. _debug fulldomain "$fulldomain"
  43. _debug txtvalue "$txtvalue"
  44. _dns_openstack_credentials || return $?
  45. _dns_openstack_check_setup || return $?
  46. _dns_openstack_find_zone || return $?
  47. _dns_openstack_get_recordset || return $?
  48. _debug _recordset_id "$_recordset_id"
  49. if [ -n "$_recordset_id" ]; then
  50. _dns_openstack_get_records || return $?
  51. _debug _records "$_records"
  52. fi
  53. _dns_openstack_delete_recordset || return $?
  54. }
  55. #################### Private functions below ##################################
  56. _dns_openstack_create_recordset() {
  57. if [ -z "$_recordset_id" ]; then
  58. _info "Creating a new recordset"
  59. if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
  60. _err "No recordset ID found after create"
  61. return 1
  62. fi
  63. else
  64. _info "Updating existing recordset"
  65. # Build new list of --record=<rec> args for update
  66. _record_args="--record=$txtvalue"
  67. for _rec in $_records; do
  68. _record_args="$_record_args --record=$_rec"
  69. done
  70. # shellcheck disable=SC2086
  71. if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
  72. _err "Recordset update failed"
  73. return 1
  74. fi
  75. fi
  76. _max_retries=60
  77. _sleep_sec=5
  78. _retry_times=0
  79. while [ "$_retry_times" -lt "$_max_retries" ]; do
  80. _retry_times=$(_math "$_retry_times" + 1)
  81. _debug3 _retry_times "$_retry_times"
  82. _record_status=$(openstack recordset show -c status -f value "$_zone_id" "$_recordset_id")
  83. _info "Recordset status is $_record_status"
  84. if [ "$_record_status" = "ACTIVE" ]; then
  85. return 0
  86. elif [ "$_record_status" = "ERROR" ]; then
  87. return 1
  88. else
  89. _sleep $_sleep_sec
  90. fi
  91. done
  92. _err "Recordset failed to become ACTIVE"
  93. return 1
  94. }
  95. _dns_openstack_delete_recordset() {
  96. if [ "$_records" = "$txtvalue" ]; then
  97. _info "Only one record found, deleting recordset"
  98. if ! openstack recordset delete "$_zone_id" "$fulldomain." >/dev/null; then
  99. _err "Failed to delete recordset"
  100. return 1
  101. fi
  102. else
  103. _info "Found existing records, updating recordset"
  104. # Build new list of --record=<rec> args for update
  105. _record_args=""
  106. for _rec in $_records; do
  107. if [ "$_rec" = "$txtvalue" ]; then
  108. continue
  109. fi
  110. _record_args="$_record_args --record=$_rec"
  111. done
  112. # shellcheck disable=SC2086
  113. if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
  114. _err "Recordset update failed"
  115. return 1
  116. fi
  117. fi
  118. }
  119. _dns_openstack_get_root() {
  120. # Take the full fqdn and strip away pieces until we get an exact zone name
  121. # match. For example, _acme-challenge.something.domain.com might need to go
  122. # into something.domain.com or domain.com
  123. _zone_name=$1
  124. _zone_list=$2
  125. while [ "$_zone_name" != "" ]; do
  126. _zone_name="$(echo "$_zone_name" | sed 's/[^.]*\.*//')"
  127. echo "$_zone_list" | while read -r id name; do
  128. if _startswith "$_zone_name." "$name"; then
  129. echo "$id"
  130. fi
  131. done
  132. done | _head_n 1
  133. }
  134. _dns_openstack_find_zone() {
  135. if ! _zone_list="$(openstack zone list -c id -c name -f value)"; then
  136. _err "Can't list zones. Check your OpenStack credentials"
  137. return 1
  138. fi
  139. _debug _zone_list "$_zone_list"
  140. if ! _zone_id="$(_dns_openstack_get_root "$fulldomain" "$_zone_list")"; then
  141. _err "Can't find a matching zone. Check your OpenStack credentials"
  142. return 1
  143. fi
  144. _debug _zone_id "$_zone_id"
  145. }
  146. _dns_openstack_get_records() {
  147. if ! _records=$(openstack recordset show -c records -f value "$_zone_id" "$fulldomain."); then
  148. _err "Failed to get records"
  149. return 1
  150. fi
  151. return 0
  152. }
  153. _dns_openstack_get_recordset() {
  154. if ! _recordset_id=$(openstack recordset list -c id -f value --name "$fulldomain." "$_zone_id"); then
  155. _err "Failed to get recordset"
  156. return 1
  157. fi
  158. return 0
  159. }
  160. _dns_openstack_check_setup() {
  161. if ! _exists openstack; then
  162. _err "OpenStack client not found"
  163. return 1
  164. fi
  165. }
  166. _dns_openstack_credentials() {
  167. _debug "Check OpenStack credentials"
  168. # If we have OS_AUTH_URL already set in the environment, then assume we want
  169. # to use those, otherwise use stored credentials
  170. if [ -n "$OS_AUTH_URL" ]; then
  171. _debug "OS_AUTH_URL env var found, using environment"
  172. else
  173. _debug "OS_AUTH_URL not found, loading stored credentials"
  174. OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
  175. OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
  176. OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
  177. OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
  178. OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
  179. OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
  180. OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
  181. OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
  182. OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
  183. OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
  184. OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
  185. OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
  186. OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
  187. fi
  188. # Check each var and either save or clear it depending on whether its set.
  189. # The helps us clear out old vars in the case where a user may want
  190. # to switch between password and app creds
  191. _debug "OS_AUTH_URL" "$OS_AUTH_URL"
  192. if [ -n "$OS_AUTH_URL" ]; then
  193. export OS_AUTH_URL
  194. _saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
  195. else
  196. unset OS_AUTH_URL
  197. _clearaccountconf SAVED_OS_AUTH_URL
  198. fi
  199. _debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
  200. if [ -n "$OS_IDENTITY_API_VERSION" ]; then
  201. export OS_IDENTITY_API_VERSION
  202. _saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
  203. else
  204. unset OS_IDENTITY_API_VERSION
  205. _clearaccountconf SAVED_OS_IDENTITY_API_VERSION
  206. fi
  207. _debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
  208. if [ -n "$OS_AUTH_TYPE" ]; then
  209. export OS_AUTH_TYPE
  210. _saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
  211. else
  212. unset OS_AUTH_TYPE
  213. _clearaccountconf SAVED_OS_AUTH_TYPE
  214. fi
  215. _debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
  216. if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
  217. export OS_APPLICATION_CREDENTIAL_ID
  218. _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
  219. else
  220. unset OS_APPLICATION_CREDENTIAL_ID
  221. _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
  222. fi
  223. _secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
  224. if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
  225. export OS_APPLICATION_CREDENTIAL_SECRET
  226. _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
  227. else
  228. unset OS_APPLICATION_CREDENTIAL_SECRET
  229. _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
  230. fi
  231. _debug "OS_USERNAME" "$OS_USERNAME"
  232. if [ -n "$OS_USERNAME" ]; then
  233. export OS_USERNAME
  234. _saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
  235. else
  236. unset OS_USERNAME
  237. _clearaccountconf SAVED_OS_USERNAME
  238. fi
  239. _secure_debug "OS_PASSWORD" "$OS_PASSWORD"
  240. if [ -n "$OS_PASSWORD" ]; then
  241. export OS_PASSWORD
  242. _saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
  243. else
  244. unset OS_PASSWORD
  245. _clearaccountconf SAVED_OS_PASSWORD
  246. fi
  247. _debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
  248. if [ -n "$OS_PROJECT_NAME" ]; then
  249. export OS_PROJECT_NAME
  250. _saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
  251. else
  252. unset OS_PROJECT_NAME
  253. _clearaccountconf SAVED_OS_PROJECT_NAME
  254. fi
  255. _debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
  256. if [ -n "$OS_PROJECT_ID" ]; then
  257. export OS_PROJECT_ID
  258. _saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
  259. else
  260. unset OS_PROJECT_ID
  261. _clearaccountconf SAVED_OS_PROJECT_ID
  262. fi
  263. _debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
  264. if [ -n "$OS_USER_DOMAIN_NAME" ]; then
  265. export OS_USER_DOMAIN_NAME
  266. _saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
  267. else
  268. unset OS_USER_DOMAIN_NAME
  269. _clearaccountconf SAVED_OS_USER_DOMAIN_NAME
  270. fi
  271. _debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
  272. if [ -n "$OS_USER_DOMAIN_ID" ]; then
  273. export OS_USER_DOMAIN_ID
  274. _saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
  275. else
  276. unset OS_USER_DOMAIN_ID
  277. _clearaccountconf SAVED_OS_USER_DOMAIN_ID
  278. fi
  279. _debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
  280. if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
  281. export OS_PROJECT_DOMAIN_NAME
  282. _saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
  283. else
  284. unset OS_PROJECT_DOMAIN_NAME
  285. _clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
  286. fi
  287. _debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
  288. if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
  289. export OS_PROJECT_DOMAIN_ID
  290. _saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
  291. else
  292. unset OS_PROJECT_DOMAIN_ID
  293. _clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
  294. fi
  295. if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
  296. # Application Credential auth
  297. if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
  298. _err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
  299. _err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
  300. _err "Please check your credentials and try again."
  301. return 1
  302. fi
  303. else
  304. # Password auth
  305. if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
  306. _err "OpenStack username or password not found."
  307. _err "Please check your credentials and try again."
  308. return 1
  309. fi
  310. if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
  311. _err "When using password authentication, OS_PROJECT_NAME or"
  312. _err "OS_PROJECT_ID must be set."
  313. _err "Please check your credentials and try again."
  314. return 1
  315. fi
  316. fi
  317. return 0
  318. }