Browse Source

Merge pull request #1983 from Neilpang/dev

Dev
neil 6 years ago
parent
commit
37792e9b38
6 changed files with 481 additions and 37 deletions
  1. 1 0
      README.md
  2. 50 6
      acme.sh
  3. 58 11
      dnsapi/README.md
  4. 168 0
      dnsapi/dns_exoscale.sh
  5. 19 20
      dnsapi/dns_linode.sh
  6. 185 0
      dnsapi/dns_linode_v4.sh

+ 1 - 0
README.md

@@ -330,6 +330,7 @@ You don't have to do anything manually!
 1. MyDNS.JP API (https://www.mydns.jp/)
 1. MyDNS.JP API (https://www.mydns.jp/)
 1. hosting.de (https://www.hosting.de)
 1. hosting.de (https://www.hosting.de)
 1. Neodigit.net API (https://www.neodigit.net)
 1. Neodigit.net API (https://www.neodigit.net)
+1. Exoscale.com API (https://www.exoscale.com/)
 
 
 And:
 And:
 
 

+ 50 - 6
acme.sh

@@ -37,6 +37,7 @@ VTYPE_HTTP="http-01"
 VTYPE_DNS="dns-01"
 VTYPE_DNS="dns-01"
 VTYPE_TLS="tls-sni-01"
 VTYPE_TLS="tls-sni-01"
 VTYPE_TLS2="tls-sni-02"
 VTYPE_TLS2="tls-sni-02"
+VTYPE_ALPN="tls-alpn-01"
 
 
 LOCAL_ANY_ADDRESS="0.0.0.0"
 LOCAL_ANY_ADDRESS="0.0.0.0"
 
 
@@ -48,6 +49,7 @@ NO_VALUE="no"
 
 
 W_TLS="tls"
 W_TLS="tls"
 W_DNS="dns"
 W_DNS="dns"
+W_ALPN="alpn"
 DNS_ALIAS_PREFIX="="
 DNS_ALIAS_PREFIX="="
 
 
 MODE_STATELESS="stateless"
 MODE_STATELESS="stateless"
@@ -1046,7 +1048,7 @@ _idn() {
   fi
   fi
 }
 }
 
 
-#_createcsr  cn  san_list  keyfile csrfile conf
+#_createcsr  cn  san_list  keyfile csrfile conf acmeValidationv1
 _createcsr() {
 _createcsr() {
   _debug _createcsr
   _debug _createcsr
   domain="$1"
   domain="$1"
@@ -1054,6 +1056,7 @@ _createcsr() {
   csrkey="$3"
   csrkey="$3"
   csr="$4"
   csr="$4"
   csrconf="$5"
   csrconf="$5"
+  acmeValidationv1="$6"
   _debug2 domain "$domain"
   _debug2 domain "$domain"
   _debug2 domainlist "$domainlist"
   _debug2 domainlist "$domainlist"
   _debug2 csrkey "$csrkey"
   _debug2 csrkey "$csrkey"
@@ -1062,7 +1065,9 @@ _createcsr() {
 
 
   printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf"
   printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf"
 
 
-  if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
+  if [ "$acmeValidationv1" ]; then
+    printf -- "\nsubjectAltName=DNS:$domainlist" >>"$csrconf"
+  elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
     #single domain
     #single domain
     _info "Single domain" "$domain"
     _info "Single domain" "$domain"
     printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf"
     printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf"
@@ -1084,6 +1089,10 @@ _createcsr() {
     printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
     printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
   fi
   fi
 
 
+  if [ "$acmeValidationv1" ]; then
+    printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}"
+  fi
+
   _csr_cn="$(_idn "$domain")"
   _csr_cn="$(_idn "$domain")"
   _debug2 _csr_cn "$_csr_cn"
   _debug2 _csr_cn "$_csr_cn"
   if _contains "$(uname -a)" "MINGW"; then
   if _contains "$(uname -a)" "MINGW"; then
@@ -2107,7 +2116,7 @@ _sleep() {
   fi
   fi
 }
 }
 
 
-# _starttlsserver  san_a  san_b port content _ncaddr
+# _starttlsserver  san_a  san_b port content _ncaddr acmeValidationv1
 _starttlsserver() {
 _starttlsserver() {
   _info "Starting tls server."
   _info "Starting tls server."
   san_a="$1"
   san_a="$1"
@@ -2115,10 +2124,12 @@ _starttlsserver() {
   port="$3"
   port="$3"
   content="$4"
   content="$4"
   opaddr="$5"
   opaddr="$5"
+  acmeValidationv1="$6"
 
 
   _debug san_a "$san_a"
   _debug san_a "$san_a"
   _debug san_b "$san_b"
   _debug san_b "$san_b"
   _debug port "$port"
   _debug port "$port"
+  _debug acmeValidationv1 "$acmeValidationv1"
 
 
   #create key TLS_KEY
   #create key TLS_KEY
   if ! _createkey "2048" "$TLS_KEY"; then
   if ! _createkey "2048" "$TLS_KEY"; then
@@ -2131,7 +2142,7 @@ _starttlsserver() {
   if [ "$san_b" ]; then
   if [ "$san_b" ]; then
     alt="$alt,$san_b"
     alt="$alt,$san_b"
   fi
   fi
-  if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF"; then
+  if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$acmeValidationv1"; then
     _err "Create tls validation csr error."
     _err "Create tls validation csr error."
     return 1
     return 1
   fi
   fi
@@ -2157,6 +2168,10 @@ _starttlsserver() {
     __S_OPENSSL="$__S_OPENSSL -6"
     __S_OPENSSL="$__S_OPENSSL -6"
   fi
   fi
 
 
+  if [ "$acmeValidationv1" ]; then
+    __S_OPENSSL="$__S_OPENSSL -alpn acme-tls/1"
+  fi
+
   _debug "$__S_OPENSSL"
   _debug "$__S_OPENSSL"
   if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
   if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
     $__S_OPENSSL -tlsextdebug &
     $__S_OPENSSL -tlsextdebug &
@@ -3067,8 +3082,8 @@ _on_before_issue() {
         _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
         _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
       fi
       fi
       _checkport="$Le_HTTPPort"
       _checkport="$Le_HTTPPort"
-    elif [ "$_currentRoot" = "$W_TLS" ]; then
-      _info "Standalone tls mode."
+    elif [ "$_currentRoot" = "$W_TLS" ] || [ "$_currentRoot" = "$W_ALPN" ]; then
+      _info "Standalone tls/alpn mode."
       if [ -z "$Le_TLSPort" ]; then
       if [ -z "$Le_TLSPort" ]; then
         Le_TLSPort=443
         Le_TLSPort=443
       else
       else
@@ -3694,6 +3709,10 @@ $_authorizations_map"
         fi
         fi
       fi
       fi
 
 
+      if [ "$_currentRoot" = "$W_ALPN" ]; then
+        vtype="$VTYPE_ALPN"
+      fi
+
       if [ "$ACME_VERSION" = "2" ]; then
       if [ "$ACME_VERSION" = "2" ]; then
         response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")"
         response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")"
         _debug2 "response" "$response"
         _debug2 "response" "$response"
@@ -4007,6 +4026,16 @@ $_authorizations_map"
         _on_issue_err "$_post_hook" "$vlist"
         _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
+    elif [ "$vtype" = "$VTYPE_ALPN" ]; then
+      acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
+      _debug acmevalidationv1 "$acmevalidationv1"
+      if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then
+        _err "Start tls server error."
+        _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
+        _clearup
+        _on_issue_err "$_post_hook" "$vlist"
+        return 1
+      fi
     fi
     fi
 
 
     if ! __trigger_validation "$uri" "$keyauthorization"; then
     if ! __trigger_validation "$uri" "$keyauthorization"; then
@@ -5469,6 +5498,7 @@ Parameters:
   --output-insecure                 Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure.
   --output-insecure                 Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure.
   --webroot, -w  /path/to/webroot   Specifies the web root folder for web root mode.
   --webroot, -w  /path/to/webroot   Specifies the web root folder for web root mode.
   --standalone                      Use standalone mode.
   --standalone                      Use standalone mode.
+  --alpn                            Use standalone alpn mode.
   --stateless                       Use stateless mode, see: $_STATELESS_WIKI
   --stateless                       Use stateless mode, see: $_STATELESS_WIKI
   --apache                          Use apache mode.
   --apache                          Use apache mode.
   --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file]   Use dns mode or dns api.
   --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file]   Use dns mode or dns api.
@@ -5499,6 +5529,7 @@ Parameters:
   --accountkey                      Specifies the account key path, only valid for the '--install' command.
   --accountkey                      Specifies the account key path, only valid for the '--install' command.
   --days                            Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
   --days                            Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
   --httpport                        Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
   --httpport                        Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
+  --tlsport                         Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
   --local-address                   Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
   --local-address                   Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
   --listraw                         Only used for '--list' command, list the certs in raw format.
   --listraw                         Only used for '--list' command, list the certs in raw format.
   --stopRenewOnError, -se           Only valid for '--renew-all' command. Stop if one cert has error in renewal.
   --stopRenewOnError, -se           Only valid for '--renew-all' command. Stop if one cert has error in renewal.
@@ -5823,6 +5854,14 @@ _process() {
           _webroot="$_webroot,$wvalue"
           _webroot="$_webroot,$wvalue"
         fi
         fi
         ;;
         ;;
+      --alpn)
+        wvalue="$W_ALPN"
+        if [ -z "$_webroot" ]; then
+          _webroot="$wvalue"
+        else
+          _webroot="$_webroot,$wvalue"
+        fi
+        ;;
       --stateless)
       --stateless)
         wvalue="$MODE_STATELESS"
         wvalue="$MODE_STATELESS"
         if [ -z "$_webroot" ]; then
         if [ -z "$_webroot" ]; then
@@ -5947,6 +5986,11 @@ _process() {
         Le_HTTPPort="$_httpport"
         Le_HTTPPort="$_httpport"
         shift
         shift
         ;;
         ;;
+      --tlsport)
+        _tlsport="$2"
+        Le_TLSPort="$_tlsport"
+        shift
+        ;;
       --listraw)
       --listraw)
         _listraw="raw"
         _listraw="raw"
         ;;
         ;;

+ 58 - 11
dnsapi/README.md

@@ -267,25 +267,26 @@ when needed.
 
 
 ## 14. Use Linode domain API
 ## 14. Use Linode domain API
 
 
-First you need to login to your Linode account to get your API Key.
+The tokens created in the classic manager and cloud manager are incompatible
+with one another. While the classic manager makes an all or nothing API, the
+newer cloud manager interface promises to produce API keys with a finer
+permission system. However, either way works just fine.
 
 
-  * [Classic Manager](https://manager.linode.com/profile/api)
+### Classic Manager ###
 
 
-   Under "Add an API key", Give the new key a "Label" (we recommend *ACME*),
-   set the expiry to never, "Create API Key", and copy the new key into the `LINODE_API_KEY` command
-   below.
+Classic Manager: https://manager.linode.com/profile/api
 
 
-  * [Cloud Manager](https://cloud.linode.com/profile/tokens)
+First you need to login to your Linode account to get your API Key.
 
 
-   Click on "Add a Personal Access Token". Give the new key a "Label" (we
-   recommend *ACME*), give it Read/Write access to "Domains". "Submit", and
-   copy the new key into the `LINODE_API_KEY` command below.
+Then add an API key with label *ACME* and copy the new key into the following
+command.
 
 
 ```sh
 ```sh
 export LINODE_API_KEY="..."
 export LINODE_API_KEY="..."
 ```
 ```
 
 
-Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect.
+Due to the reload time of any changes in the DNS records, we have to use the
+`dnssleep` option to wait at least 15 minutes for the changes to take effect.
 
 
 Ok, let's issue a cert now:
 Ok, let's issue a cert now:
 
 
@@ -293,7 +294,35 @@ Ok, let's issue a cert now:
 acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com
 acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com
 ```
 ```
 
 
-The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be
+reused when needed.
+
+### Cloud Manager ###
+
+Cloud Manager: https://cloud.linode.com/profile/tokens
+
+First you need to login to your Linode account to get your API Key.
+
+   1. Click on "Add a Personal Access Token".
+   2. Give the new key a "Label" (we recommend *ACME*)
+   3. Give it Read/Write access to "Domains"
+   4. "Submit" and copy the new key into the `LINODE_V4_API_KEY` command below.
+
+```sh
+export LINODE_V4_API_KEY="..."
+```
+
+Due to the reload time of any changes in the DNS records, we have to use the
+`dnssleep` option to wait at least 15 minutes for the changes to take effect.
+
+Ok, let's issue a cert now:
+
+```sh
+acme.sh --issue --dns dns_linode_v4 --dnssleep 900 -d example.com -d www.example.com
+```
+
+The `LINODE_V4_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be
+reused when needed.
 
 
 ## 15. Use FreeDNS
 ## 15. Use FreeDNS
 
 
@@ -1080,6 +1109,24 @@ acme.sh --issue --dns dns_neodigit -d example.com -d www.example.com
 
 
 Neodigit API Token will be saved in `~/.acme.sh/account.conf` and will be used when needed.
 Neodigit API Token will be saved in `~/.acme.sh/account.conf` and will be used when needed.
 
 
+## 57. Use Exoscale API
+
+Create an API key and secret key in the Exoscale account section
+
+Set your API and secret key:
+
+```
+export EXOSCALE_API_KEY='xxx'
+export EXOSCALE_SECRET_KEY='xxx'
+```
+
+Now, let's issue a cert:
+```
+acme.sh --issue --dns dns_netcup -d example.com -d www.example.com
+```
+
+The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
 # Use custom API
 # Use custom API
 
 
 If your API is not supported yet, you can write your own DNS API.
 If your API is not supported yet, you can write your own DNS API.

+ 168 - 0
dnsapi/dns_exoscale.sh

@@ -0,0 +1,168 @@
+#!/usr/bin/env sh
+
+EXOSCALE_API=https://api.exoscale.com/dns/v1
+
+########  Public functions #####################
+
+# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_exoscale_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! _checkAuth; then
+    return 1
+  fi
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _info "Adding record"
+  if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then
+    if _contains "$response" "$txtvalue"; then
+      _info "Added, OK"
+      return 0
+    fi
+  fi
+  _err "Add txt record error."
+  return 1
+
+}
+
+# Usage: fulldomain txtvalue
+# Used to remove the txt record after validation
+dns_exoscale_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! _checkAuth; then
+    return 1
+  fi
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _debug "Getting txt records"
+  _exoscale_rest GET "domains/${_domain_id}/records?type=TXT&name=$_sub_domain" "" "$_domain_token"
+  if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
+    _record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
+  fi
+
+  if [ -z "$_record_id" ]; then
+    _err "Can not get record id to remove."
+    return 1
+  fi
+
+  _debug "Deleting record $_record_id"
+
+  if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then
+    _err "Delete record error."
+    return 1
+  fi
+
+  return 0
+}
+
+####################  Private functions below ##################################
+
+_checkAuth() {
+  EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
+  EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
+
+  if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
+    EXOSCALE_API_KEY=""
+    EXOSCALE_SECRET_KEY=""
+    _err "You don't specify Exoscale application key and application secret yet."
+    _err "Please create you key and try again."
+    return 1
+  fi
+
+  _saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
+  _saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
+
+  return 0
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+# _domain_token=sdjkglgdfewsdfg
+_get_root() {
+
+  if ! _exoscale_rest GET "domains"; then
+    return 1
+  fi
+
+  domain=$1
+  i=2
+  p=1
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$h"
+    if [ -z "$h" ]; then
+      #not valid
+      return 1
+    fi
+
+    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+      _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
+      _domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
+      if [ "$_domain_token" ] && [ "$_domain_id" ]; then
+        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+        _domain=$h
+        return 0
+      fi
+      return 1
+    fi
+    p=$i
+    i=$(_math "$i" + 1)
+  done
+  return 1
+}
+
+# returns response
+_exoscale_rest() {
+  method=$1
+  path="$2"
+  data="$3"
+  token="$4"
+  request_url="$EXOSCALE_API/$path"
+  _debug "$path"
+
+  export _H1="Accept: application/json"
+
+  if [ "$token" ]; then
+    export _H2="X-DNS-Domain-Token: $token"
+  else
+    export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY"
+  fi
+
+  if [ "$data" ] || [ "$method" = "DELETE" ]; then
+    export _H3="Content-Type: application/json"
+    _debug data "$data"
+    response="$(_post "$data" "$request_url" "" "$method")"
+  else
+    response="$(_get "$request_url" "" "" "$method")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $request_url"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}

+ 19 - 20
dnsapi/dns_linode.sh

@@ -2,7 +2,7 @@
 
 
 #Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
 #Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
 
 
-LINODE_API_URL="https://api.linode.com/v4/domains"
+LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
 
 
 ########  Public functions #####################
 ########  Public functions #####################
 
 
@@ -27,14 +27,10 @@ dns_linode_add() {
   _debug _sub_domain "$_sub_domain"
   _debug _sub_domain "$_sub_domain"
   _debug _domain "$_domain"
   _debug _domain "$_domain"
 
 
-  _payload="{
-              \"type\": \"TXT\",
-              \"name\": \"$_sub_domain\",
-              \"target\": \"$txtvalue\"
-            }"
+  _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
 
 
-  if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then
-    _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+  if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
+    _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
     _debug _resource_id "$_resource_id"
     _debug _resource_id "$_resource_id"
 
 
     if [ -z "$_resource_id" ]; then
     if [ -z "$_resource_id" ]; then
@@ -69,21 +65,25 @@ dns_linode_rm() {
   _debug _sub_domain "$_sub_domain"
   _debug _sub_domain "$_sub_domain"
   _debug _domain "$_domain"
   _debug _domain "$_domain"
 
 
-  if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
+  _parameters="&DomainID=$_domain_id"
+
+  if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
 
 
-    resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")"
+    resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
     if [ "$resource" ]; then
     if [ "$resource" ]; then
-      _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+      _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
       if [ "$_resource_id" ]; then
       if [ "$_resource_id" ]; then
         _debug _resource_id "$_resource_id"
         _debug _resource_id "$_resource_id"
 
 
-        if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then
-          # On 200/OK, empty set is returned. Check for error, if any.
-          _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1)
+        _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
+
+        if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
+          _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+          _debug _resource_id "$_resource_id"
 
 
-          if [ -n "$_error_response" ]; then
-            _err "Error deleting the domain resource: $_error_response"
+          if [ -z "$_resource_id" ]; then
+            _err "Error deleting the domain resource."
             return 1
             return 1
           fi
           fi
 
 
@@ -127,7 +127,7 @@ _get_root() {
   i=2
   i=2
   p=1
   p=1
 
 
-  if _rest GET; then
+  if _rest GET "domain.list"; then
     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
     response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
     while true; do
     while true; do
       h=$(printf "%s" "$domain" | cut -d . -f $i-100)
       h=$(printf "%s" "$domain" | cut -d . -f $i-100)
@@ -137,9 +137,9 @@ _get_root() {
         return 1
         return 1
       fi
       fi
 
 
-      hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")"
+      hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
       if [ "$hostedzone" ]; then
       if [ "$hostedzone" ]; then
-        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
         if [ "$_domain_id" ]; then
         if [ "$_domain_id" ]; then
           _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
           _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
           _domain=$h
           _domain=$h
@@ -165,7 +165,6 @@ _rest() {
 
 
   export _H1="Accept: application/json"
   export _H1="Accept: application/json"
   export _H2="Content-Type: application/json"
   export _H2="Content-Type: application/json"
-  export _H3="Authorization: Bearer $LINODE_API_KEY"
 
 
   if [ "$mtd" != "GET" ]; then
   if [ "$mtd" != "GET" ]; then
     # both POST and DELETE.
     # both POST and DELETE.

+ 185 - 0
dnsapi/dns_linode_v4.sh

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#Original Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
+#v4 Update Author: Aaron W. Swenson <aaron@grandmasfridge.org>
+
+LINODE_V4_API_URL="https://api.linode.com/v4/domains"
+
+########  Public functions #####################
+
+#Usage: dns_linode_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_add() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+
+  if ! _Linode_API; then
+    return 1
+  fi
+
+  _info "Using Linode"
+  _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "Domain does not exist."
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _payload="{
+              \"type\": \"TXT\",
+              \"name\": \"$_sub_domain\",
+              \"target\": \"$txtvalue\"
+            }"
+
+  if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then
+    _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+    _debug _resource_id "$_resource_id"
+
+    if [ -z "$_resource_id" ]; then
+      _err "Error adding the domain resource."
+      return 1
+    fi
+
+    _info "Domain resource successfully added."
+    return 0
+  fi
+
+  return 1
+}
+
+#Usage: dns_linode_rm   _acme-challenge.www.domain.com
+dns_linode_rm() {
+  fulldomain="${1}"
+
+  if ! _Linode_API; then
+    return 1
+  fi
+
+  _info "Using Linode"
+  _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "Domain does not exist."
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
+    response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+
+    resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")"
+    if [ "$resource" ]; then
+      _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+      if [ "$_resource_id" ]; then
+        _debug _resource_id "$_resource_id"
+
+        if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then
+          # On 200/OK, empty set is returned. Check for error, if any.
+          _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1)
+
+          if [ -n "$_error_response" ]; then
+            _err "Error deleting the domain resource: $_error_response"
+            return 1
+          fi
+
+          _info "Domain resource successfully deleted."
+          return 0
+        fi
+      fi
+
+      return 1
+    fi
+
+    return 0
+  fi
+
+  return 1
+}
+
+####################  Private functions below ##################################
+
+_Linode_API() {
+  if [ -z "$LINODE_V4_API_KEY" ]; then
+    LINODE_V4_API_KEY=""
+
+    _err "You didn't specify the Linode v4 API key yet."
+    _err "Please create your key and try again."
+
+    return 1
+  fi
+
+  _saveaccountconf LINODE_V4_API_KEY "$LINODE_V4_API_KEY"
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+
+  if _rest GET; then
+    response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+    while true; do
+      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+      _debug h "$h"
+      if [ -z "$h" ]; then
+        #not valid
+        return 1
+      fi
+
+      hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")"
+      if [ "$hostedzone" ]; then
+        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+        if [ "$_domain_id" ]; then
+          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+          _domain=$h
+          return 0
+        fi
+        return 1
+      fi
+      p=$i
+      i=$(_math "$i" + 1)
+    done
+  fi
+  return 1
+}
+
+#method method action data
+_rest() {
+  mtd="$1"
+  ep="$2"
+  data="$3"
+
+  _debug mtd "$mtd"
+  _debug ep "$ep"
+
+  export _H1="Accept: application/json"
+  export _H2="Content-Type: application/json"
+  export _H3="Authorization: Bearer $LINODE_V4_API_KEY"
+
+  if [ "$mtd" != "GET" ]; then
+    # both POST and DELETE.
+    _debug data "$data"
+    response="$(_post "$data" "$LINODE_V4_API_URL$ep" "" "$mtd")"
+  else
+    response="$(_get "$LINODE_V4_API_URL$ep$data")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}