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.

167 lines
4.7 KiB

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