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.
225 lines
6.4 KiB
225 lines
6.4 KiB
#!/usr/bin/env sh
|
|
|
|
# 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_authenticate || return $?
|
|
|
|
_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_authenticate || return $?
|
|
|
|
_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 added"
|
|
}
|
|
|
|
#################### Private functions below ##################################
|
|
|
|
_dns_gcloud_authenticate() {
|
|
_info "_dns_gcloud_authenticate: authenticating gcloud"
|
|
_debug "_dns_gcloud_authenticate: checking authenticated status"
|
|
|
|
account=$(
|
|
gcloud auth list \
|
|
--filter "status:ACTIVE" \
|
|
--format "value(account)" \
|
|
--verbosity error
|
|
)
|
|
|
|
if [ "$account" ]; then
|
|
_info "_dns_gcloud_authenticate: already authenticated"
|
|
return 0
|
|
fi
|
|
|
|
_debug "_dns_gcloud_authenticate: attempting to authenticate using service account key"
|
|
|
|
GCLOUD_Service_Account_Key="${CF_Token:-$(_readaccountconf_mutable GCLOUD_Service_Account_Key)}"
|
|
GCLOUD_Project_ID="${CF_Account_ID:-$(_readaccountconf_mutable GCLOUD_Project_ID)}"
|
|
|
|
if [ -z "$GCLOUD_Service_Account_Key" ]; then
|
|
GCLOUD_Service_Account_Key=""
|
|
GCLOUD_Project_ID=""
|
|
_err "_dns_gcloud_authenticate: missing Google Cloud service account key"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$GCLOUD_Project_ID" ]; then
|
|
GCLOUD_Service_Account_Key=""
|
|
GCLOUD_Project_ID=""
|
|
_err "_dns_gcloud_authenticate: missing Google Cloud project ID"
|
|
return 1
|
|
fi
|
|
|
|
if ! echo "$GCLOUD_Service_Account_Key" | gcloud auth activate-service-account --key-file -; then
|
|
_err "_dns_gcloud_authenticate: failed to authenticate with service account key"
|
|
return 1
|
|
fi
|
|
|
|
_info "_dns_gcloud_authenticate: successfully authenticated using service account key"
|
|
|
|
gcloud config set project "$GCLOUD_Project_ID"
|
|
|
|
_info "_dns_gcloud_authenticate: configured gcloud project"
|
|
|
|
_saveaccountconf_mutable CF_Token "$CF_Token"
|
|
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
|
|
}
|
|
|
|
_dns_gcloud_authenticate() {
|
|
account=$(gcloud auth list --filter "status:ACTIVE" --format "value(account)")
|
|
}
|
|
|
|
_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)
|
|
rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/","/"\n"/g')
|
|
}
|