Browse Source

Added support for Google Cloud DNS API (dns_gcloud)

Janos Lenart 7 years ago
parent
commit
0a3ac1f5c3
3 changed files with 189 additions and 0 deletions
  1. 1 0
      README.md
  2. 21 0
      dnsapi/README.md
  3. 167 0
      dnsapi/dns_gcloud.sh

+ 1 - 0
README.md

@@ -274,6 +274,7 @@ You don't have to do anything manually!
 
 
 ### Currently acme.sh supports:
 ### Currently acme.sh supports:
 
 
+1. Google Cloud DNS API
 1. CloudFlare.com API
 1. CloudFlare.com API
 1. DNSPod.cn API
 1. DNSPod.cn API
 1. CloudXNS.com API
 1. CloudXNS.com API

+ 21 - 0
dnsapi/README.md

@@ -4,6 +4,27 @@ If your dns provider doesn't provide api access, you can use our dns alias mode:
 
 
 https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode
 https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode
 
 
+## 1. Use Google Cloud DNS API to automatically issue cert
+
+First you need to authenticate to gcloud.
+
+```
+gcloud init
+```
+
+**The `dns_gcloud` script uses the active gcloud configuration and credentials.**
+There is no logic inside `dns_gcloud` to override the project and other settings.
+If needed, create additional [gcloud configurations](https://cloud.google.com/sdk/gcloud/reference/topic/configurations).
+You can change the configuration being used without *activating* it; simply set the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable.
+
+To issue a certificate you can:
+```
+export CLOUDSDK_ACTIVE_CONFIG_NAME=default  # see the note above
+acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com'
+```
+
+`dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode).
+
 ## 1. Use CloudFlare domain API to automatically issue cert
 ## 1. Use CloudFlare domain API to automatically issue cert
 
 
 First you need to login to your CloudFlare account to get your API key.
 First you need to login to your CloudFlare account to get your API key.

+ 167 - 0
dnsapi/dns_gcloud.sh

@@ -0,0 +1,167 @@
+#!/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_find_zone || return $?
+
+  # Add an extra RR
+  _dns_gcloud_start_tr || return $?
+  _dns_gcloud_get_rrdatas || return $?
+  echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
+  echo -e "$rrdatas\n\"$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" | fgrep -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
+  _dns_gcloud_execute_tr || return $?
+
+  _info "$fulldomain record added"
+}
+
+####################  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=lenart \
+          --filter='status != done' \
+          | grep -q '.*'; then
+       _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ..."
+       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 --no-run-if-empty 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 --no-run-if-empty 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)"
+  _debug filter "$filter"
+
+  # List domains and find the longest match (in case of some levels of delegation)
+  if ! match=$(gcloud dns managed-zones list \
+                 --format="value(name, dnsName)" \
+                 --filter="$filter" \
+                 | while read dnsName name; do
+                     echo -e "${#dnsName}\t$dnsName\t$name"
+                   done \
+                 | sort -n -r | head -n1 | 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')
+}