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.

176 lines
5.1 KiB

2 years ago
4 years ago
4 years ago
4 years ago
  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC2034
  3. dns_gcloud_info='Google Cloud DNS
  4. Site: Cloud.Google.com/dns
  5. Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gcloud
  6. Options:
  7. CLOUDSDK_ACTIVE_CONFIG_NAME Active config name. E.g. "default"
  8. Author: Janos Lenart <janos@lenart.io>
  9. '
  10. ######## Public functions #####################
  11. # Usage: dns_gcloud_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  12. dns_gcloud_add() {
  13. fulldomain=$1
  14. txtvalue=$2
  15. _info "Using gcloud"
  16. _debug fulldomain "$fulldomain"
  17. _debug txtvalue "$txtvalue"
  18. _dns_gcloud_find_zone || return $?
  19. # Add an extra RR
  20. _dns_gcloud_start_tr || return $?
  21. _dns_gcloud_get_rrdatas || return $?
  22. echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
  23. printf "%s\n%s\n" "$rrdatas" "\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $?
  24. _dns_gcloud_execute_tr || return $?
  25. _info "$fulldomain record added"
  26. }
  27. # Usage: dns_gcloud_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
  28. # Remove the txt record after validation.
  29. dns_gcloud_rm() {
  30. fulldomain=$1
  31. txtvalue=$2
  32. _info "Using gcloud"
  33. _debug fulldomain "$fulldomain"
  34. _debug txtvalue "$txtvalue"
  35. _dns_gcloud_find_zone || return $?
  36. # Remove one RR
  37. _dns_gcloud_start_tr || return $?
  38. _dns_gcloud_get_rrdatas || return $?
  39. echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
  40. echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
  41. _dns_gcloud_execute_tr || return $?
  42. _info "$fulldomain record removed"
  43. }
  44. #################### Private functions below ##################################
  45. _dns_gcloud_start_tr() {
  46. if ! trd=$(mktemp -d); then
  47. _err "_dns_gcloud_start_tr: failed to create temporary directory"
  48. return 1
  49. fi
  50. tr="$trd/tr.yaml"
  51. _debug tr "$tr"
  52. if ! gcloud dns record-sets transaction start \
  53. --transaction-file="$tr" \
  54. --zone="$managedZone"; then
  55. rm -r "$trd"
  56. _err "_dns_gcloud_start_tr: failed to execute transaction"
  57. return 1
  58. fi
  59. }
  60. _dns_gcloud_execute_tr() {
  61. if ! gcloud dns record-sets transaction execute \
  62. --transaction-file="$tr" \
  63. --zone="$managedZone"; then
  64. _debug tr "$(cat "$tr")"
  65. rm -r "$trd"
  66. _err "_dns_gcloud_execute_tr: failed to execute transaction"
  67. return 1
  68. fi
  69. rm -r "$trd"
  70. for i in $(seq 1 120); do
  71. if gcloud dns record-sets changes list \
  72. --zone="$managedZone" \
  73. --filter='status != done' |
  74. grep -q '^.*'; then
  75. _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ($i/120)..."
  76. sleep 5
  77. else
  78. return 0
  79. fi
  80. done
  81. _err "_dns_gcloud_execute_tr: transaction is still pending after 10 minutes"
  82. rm -r "$trd"
  83. return 1
  84. }
  85. _dns_gcloud_remove_rrs() {
  86. if ! xargs -r gcloud dns record-sets transaction remove \
  87. --name="$fulldomain." \
  88. --ttl="$ttl" \
  89. --type=TXT \
  90. --zone="$managedZone" \
  91. --transaction-file="$tr" --; then
  92. _debug tr "$(cat "$tr")"
  93. rm -r "$trd"
  94. _err "_dns_gcloud_remove_rrs: failed to remove RRs"
  95. return 1
  96. fi
  97. }
  98. _dns_gcloud_add_rrs() {
  99. ttl=60
  100. if ! xargs -r gcloud dns record-sets transaction add \
  101. --name="$fulldomain." \
  102. --ttl="$ttl" \
  103. --type=TXT \
  104. --zone="$managedZone" \
  105. --transaction-file="$tr" --; then
  106. _debug tr "$(cat "$tr")"
  107. rm -r "$trd"
  108. _err "_dns_gcloud_add_rrs: failed to add RRs"
  109. return 1
  110. fi
  111. }
  112. _dns_gcloud_find_zone() {
  113. # Prepare a filter that matches zones that are suiteable for this entry.
  114. # For example, _acme-challenge.something.domain.com might need to go into something.domain.com or domain.com;
  115. # this function finds the longest postfix that has a managed zone.
  116. part="$fulldomain"
  117. filter="dnsName=( "
  118. while [ "$part" != "" ]; do
  119. filter="$filter$part. "
  120. part="$(echo "$part" | sed 's/[^.]*\.*//')"
  121. done
  122. filter="$filter) AND visibility=public"
  123. _debug filter "$filter"
  124. # List domains and find the zone with the deepest sub-domain (in case of some levels of delegation)
  125. if ! match=$(gcloud dns managed-zones list \
  126. --format="value(name, dnsName)" \
  127. --filter="$filter" |
  128. while read -r dnsName name; do
  129. printf "%s\t%s\t%s\n" "$(echo "$name" | awk -F"." '{print NF-1}')" "$dnsName" "$name"
  130. done |
  131. sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then
  132. _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?"
  133. return 1
  134. fi
  135. dnsName=$(echo "$match" | cut -f2)
  136. _debug dnsName "$dnsName"
  137. managedZone=$(echo "$match" | cut -f1)
  138. _debug managedZone "$managedZone"
  139. }
  140. _dns_gcloud_get_rrdatas() {
  141. if ! rrdatas=$(gcloud dns record-sets list \
  142. --zone="$managedZone" \
  143. --name="$fulldomain." \
  144. --type=TXT \
  145. --format="value(ttl,rrdatas)"); then
  146. _err "_dns_gcloud_get_rrdatas: Failed to list record-sets"
  147. rm -r "$trd"
  148. return 1
  149. fi
  150. ttl=$(echo "$rrdatas" | cut -f1)
  151. # starting with version 353.0.0 gcloud seems to
  152. # separate records with a semicolon instead of commas
  153. # see also https://cloud.google.com/sdk/docs/release-notes#35300_2021-08-17
  154. rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/"[,;]"/"\n"/g')
  155. }