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
176 lines
5.1 KiB
#!/usr/bin/env sh |
|
# shellcheck disable=SC2034 |
|
dns_gcloud_info='Google Cloud DNS |
|
Site: Cloud.Google.com/dns |
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gcloud |
|
Options: |
|
CLOUDSDK_ACTIVE_CONFIG_NAME Active config name. E.g. "default" |
|
Author: Janos Lenart <janos@lenart.io> |
|
' |
|
|
|
######## Public functions ##################### |
|
|
|
# Usage: dns_gcloud_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|
dns_gcloud_add() { |
|
fulldomain=$1 |
|
txtvalue=$2 |
|
_info "Using gcloud" |
|
_debug fulldomain "$fulldomain" |
|
_debug txtvalue "$txtvalue" |
|
|
|
_dns_gcloud_find_zone || return $? |
|
|
|
# Add an extra RR |
|
_dns_gcloud_start_tr || return $? |
|
_dns_gcloud_get_rrdatas || return $? |
|
echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? |
|
printf "%s\n%s\n" "$rrdatas" "\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $? |
|
_dns_gcloud_execute_tr || return $? |
|
|
|
_info "$fulldomain record added" |
|
} |
|
|
|
# Usage: dns_gcloud_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|
# Remove the txt record after validation. |
|
dns_gcloud_rm() { |
|
fulldomain=$1 |
|
txtvalue=$2 |
|
_info "Using gcloud" |
|
_debug fulldomain "$fulldomain" |
|
_debug txtvalue "$txtvalue" |
|
|
|
_dns_gcloud_find_zone || return $? |
|
|
|
# Remove one RR |
|
_dns_gcloud_start_tr || return $? |
|
_dns_gcloud_get_rrdatas || return $? |
|
echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? |
|
echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $? |
|
_dns_gcloud_execute_tr || return $? |
|
|
|
_info "$fulldomain record removed" |
|
} |
|
|
|
#################### Private functions below ################################## |
|
|
|
_dns_gcloud_start_tr() { |
|
if ! trd=$(mktemp -d); then |
|
_err "_dns_gcloud_start_tr: failed to create temporary directory" |
|
return 1 |
|
fi |
|
tr="$trd/tr.yaml" |
|
_debug tr "$tr" |
|
|
|
if ! gcloud dns record-sets transaction start \ |
|
--transaction-file="$tr" \ |
|
--zone="$managedZone"; then |
|
rm -r "$trd" |
|
_err "_dns_gcloud_start_tr: failed to execute transaction" |
|
return 1 |
|
fi |
|
} |
|
|
|
_dns_gcloud_execute_tr() { |
|
if ! gcloud dns record-sets transaction execute \ |
|
--transaction-file="$tr" \ |
|
--zone="$managedZone"; then |
|
_debug tr "$(cat "$tr")" |
|
rm -r "$trd" |
|
_err "_dns_gcloud_execute_tr: failed to execute transaction" |
|
return 1 |
|
fi |
|
rm -r "$trd" |
|
|
|
for i in $(seq 1 120); do |
|
if gcloud dns record-sets changes list \ |
|
--zone="$managedZone" \ |
|
--filter='status != done' | |
|
grep -q '^.*'; then |
|
_info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ($i/120)..." |
|
sleep 5 |
|
else |
|
return 0 |
|
fi |
|
done |
|
|
|
_err "_dns_gcloud_execute_tr: transaction is still pending after 10 minutes" |
|
rm -r "$trd" |
|
return 1 |
|
} |
|
|
|
_dns_gcloud_remove_rrs() { |
|
if ! xargs -r gcloud dns record-sets transaction remove \ |
|
--name="$fulldomain." \ |
|
--ttl="$ttl" \ |
|
--type=TXT \ |
|
--zone="$managedZone" \ |
|
--transaction-file="$tr" --; then |
|
_debug tr "$(cat "$tr")" |
|
rm -r "$trd" |
|
_err "_dns_gcloud_remove_rrs: failed to remove RRs" |
|
return 1 |
|
fi |
|
} |
|
|
|
_dns_gcloud_add_rrs() { |
|
ttl=60 |
|
if ! xargs -r gcloud dns record-sets transaction add \ |
|
--name="$fulldomain." \ |
|
--ttl="$ttl" \ |
|
--type=TXT \ |
|
--zone="$managedZone" \ |
|
--transaction-file="$tr" --; then |
|
_debug tr "$(cat "$tr")" |
|
rm -r "$trd" |
|
_err "_dns_gcloud_add_rrs: failed to add RRs" |
|
return 1 |
|
fi |
|
} |
|
|
|
_dns_gcloud_find_zone() { |
|
# Prepare a filter that matches zones that are suiteable for this entry. |
|
# For example, _acme-challenge.something.domain.com might need to go into something.domain.com or domain.com; |
|
# this function finds the longest postfix that has a managed zone. |
|
part="$fulldomain" |
|
filter="dnsName=( " |
|
while [ "$part" != "" ]; do |
|
filter="$filter$part. " |
|
part="$(echo "$part" | sed 's/[^.]*\.*//')" |
|
done |
|
filter="$filter) AND visibility=public" |
|
_debug filter "$filter" |
|
|
|
# List domains and find the zone with the deepest sub-domain (in case of some levels of delegation) |
|
if ! match=$(gcloud dns managed-zones list \ |
|
--format="value(name, dnsName)" \ |
|
--filter="$filter" | |
|
while read -r dnsName name; do |
|
printf "%s\t%s\t%s\n" "$(echo "$name" | awk -F"." '{print NF-1}')" "$dnsName" "$name" |
|
done | |
|
sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then |
|
_err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" |
|
return 1 |
|
fi |
|
|
|
dnsName=$(echo "$match" | cut -f2) |
|
_debug dnsName "$dnsName" |
|
managedZone=$(echo "$match" | cut -f1) |
|
_debug managedZone "$managedZone" |
|
} |
|
|
|
_dns_gcloud_get_rrdatas() { |
|
if ! rrdatas=$(gcloud dns record-sets list \ |
|
--zone="$managedZone" \ |
|
--name="$fulldomain." \ |
|
--type=TXT \ |
|
--format="value(ttl,rrdatas)"); then |
|
_err "_dns_gcloud_get_rrdatas: Failed to list record-sets" |
|
rm -r "$trd" |
|
return 1 |
|
fi |
|
ttl=$(echo "$rrdatas" | cut -f1) |
|
# starting with version 353.0.0 gcloud seems to |
|
# separate records with a semicolon instead of commas |
|
# see also https://cloud.google.com/sdk/docs/release-notes#35300_2021-08-17 |
|
rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/"[,;]"/"\n"/g') |
|
}
|
|
|