Browse Source

Merge pull request #1332 from Neilpang/dev

sync
neil 7 years ago
parent
commit
c3a289cebc
6 changed files with 252 additions and 64 deletions
  1. 1 0
      README.md
  2. 3 1
      acme.sh
  3. 7 7
      deploy/README.md
  4. 29 1
      dnsapi/README.md
  5. 184 0
      dnsapi/dns_da.sh
  6. 28 55
      dnsapi/dns_namecom.sh

+ 1 - 0
README.md

@@ -327,6 +327,7 @@ You don't have to do anything manually!
 1. selectel.com(selectel.ru) DNS API
 1. selectel.com(selectel.ru) DNS API
 1. zonomi.com DNS API
 1. zonomi.com DNS API
 1. DreamHost.com API
 1. DreamHost.com API
+1. DirectAdmin API
 
 
 
 
 And: 
 And: 

+ 3 - 1
acme.sh

@@ -4078,7 +4078,9 @@ $_authorizations_map"
       _info "Your cert key is in $(__green " $CERT_KEY_PATH ")"
       _info "Your cert key is in $(__green " $CERT_KEY_PATH ")"
     fi
     fi
 
 
-    cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
+    if [ "$ACME_VERSION" != "2" ]; then
+      cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
+    fi
 
 
     if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then
     if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then
       USER_PATH="$PATH"
       USER_PATH="$PATH"

+ 7 - 7
deploy/README.md

@@ -114,8 +114,8 @@ user
 Any backups older than 180 days will be deleted when new certificates
 Any backups older than 180 days will be deleted when new certificates
 are deployed.  This defaults to "yes" set to "no" to disable backup.
 are deployed.  This defaults to "yes" set to "no" to disable backup.
 
 
-###Eamples using SSH deploy
-The following example illustrates deploying certifcates to a QNAP NAS
+###Examples using SSH deploy
+The following example illustrates deploying certificates to a QNAP NAS
 (tested with QTS version 4.2.3)
 (tested with QTS version 4.2.3)
 
 
 ```sh
 ```sh
@@ -132,8 +132,8 @@ the same file.  This will result in the certificate being appended
 to the same file as the private key... a common requirement of several
 to the same file as the private key... a common requirement of several
 services.
 services.
 
 
-The next example illustates deploying certificates to a Unifi
-Contolller (tested with version 5.4.11).
+The next example illustrates deploying certificates to a Unifi
+Controller (tested with version 5.4.11).
 
 
 ```sh
 ```sh
 export DEPLOY_SSH_USER="root"
 export DEPLOY_SSH_USER="root"
@@ -153,9 +153,9 @@ export DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \
 
 
 acme.sh --deploy -d unifi.example.com --deploy-hook ssh
 acme.sh --deploy -d unifi.example.com --deploy-hook ssh
 ```
 ```
-In this exmple we execute several commands on the remote host
+In this example we execute several commands on the remote host
 after the certificate files have been copied... to generate a pkcs12 file
 after the certificate files have been copied... to generate a pkcs12 file
-compatible with Unifi, to import it into the Unifi keystore and then finaly
+compatible with Unifi, to import it into the Unifi keystore and then finally
 to restart the service.
 to restart the service.
 
 
 Note also that once the certificate is imported
 Note also that once the certificate is imported
@@ -233,7 +233,7 @@ DEPLOY_CPANEL_USER is required only if you run the script as root and it should
 export DEPLOY_CPANEL_USER=username
 export DEPLOY_CPANEL_USER=username
 acme.sh  --deploy  -d example.com  --deploy-hook cpanel_uapi
 acme.sh  --deploy  -d example.com  --deploy-hook cpanel_uapi
 ```
 ```
-Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain. 
+Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separate certificate for each domain. 
 
 
 ## 8. Deploy the cert to your FRITZ!Box router
 ## 8. Deploy the cert to your FRITZ!Box router
 
 

+ 29 - 1
dnsapi/README.md

@@ -354,7 +354,7 @@ acme.sh --issue --dns dns_gandi_livedns -d example.com -d www.example.com
 First, generate a TSIG key for updating the zone.
 First, generate a TSIG key for updating the zone.
 
 
 ```
 ```
-keymgr tsig generate acme_key algorithm hmac-sha512 > /etc/knot/acme.key
+keymgr tsig generate -t acme_key hmac-sha512 > /etc/knot/acme.key
 ```
 ```
 
 
 Include this key in your knot configuration file.
 Include this key in your knot configuration file.
@@ -757,6 +757,34 @@ acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com
 The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will
 The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will
 be reused when needed.
 be reused when needed.
 
 
+## 41. Use DirectAdmin API
+The DirectAdmin interface has it's own Let's encrypt functionality, but this
+script can be used to generate certificates for names which are not hosted on
+DirectAdmin
+
+User must provide login data and URL to the DirectAdmin incl. port.
+You can create an user which only has access to
+
+- CMD_API_DNS_CONTROL
+- CMD_API_SHOW_DOMAINS
+
+By using the Login Keys function.
+See also https://www.directadmin.com/api.php and https://www.directadmin.com/features.php?id=1298
+
+```
+export DA_Api="https://remoteUser:remotePassword@da.domain.tld:8443"
+export DA_Api_Insecure=1
+```
+Set `DA_Api_Insecure` to 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+
+Ok, let's issue a cert now:
+```
+acme.sh --issue --dns dns_da -d example.com -d www.example.com
+```
+
+The `DA_Api` and `DA_Api_Insecure` 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.

+ 184 - 0
dnsapi/dns_da.sh

@@ -0,0 +1,184 @@
+#!/usr/bin/env sh
+# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
+# vim: et ts=2 sw=2
+#
+# DirectAdmin 1.41.0 API
+# The DirectAdmin interface has it's own Let's encrypt functionality, but this
+# script can be used to generate certificates for names which are not hosted on
+# DirectAdmin
+#
+# User must provide login data and URL to DirectAdmin incl. port.
+# You can create login key, by using the Login Keys function
+# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to 
+# - CMD_API_DNS_CONTROL
+# - CMD_API_SHOW_DOMAINS
+#
+# See also https://www.directadmin.com/api.php and
+# https://www.directadmin.com/features.php?id=1298
+#
+# Report bugs to https://github.com/TigerP/acme.sh/issues
+#
+# Values to export:
+# export DA_Api="https://remoteUser:remotePassword@da.example.com:8443"
+# export DA_Api_Insecure=1
+#
+# Set DA_Api_Insecure to 1 for insecure and 0 for secure -> difference is
+# whether ssl cert is checked for validity (0) or whether it is just accepted
+# (1)
+#
+########  Public functions #####################
+
+# Usage: dns_myapi_add  _acme-challenge.www.example.com  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_da_add() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+  _debug "Calling: dns_da_add() '${fulldomain}' '${txtvalue}'"
+  _DA_credentials && _DA_getDomainInfo && _DA_addTxt
+}
+
+# Usage: dns_da_rm  _acme-challenge.www.example.com  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to remove the txt record after validation
+dns_da_rm() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+  _debug "Calling: dns_da_rm() '${fulldomain}' '${txtvalue}'"
+  _DA_credentials && _DA_getDomainInfo && _DA_rmTxt
+}
+
+####################  Private functions below ##################################
+# Usage: _DA_credentials
+# It will check if the needed settings are available
+_DA_credentials() {
+  DA_Api="${DA_Api:-$(_readaccountconf_mutable DA_Api)}"
+  DA_Api_Insecure="${DA_Api_Insecure:-$(_readaccountconf_mutable DA_Api_Insecure)}"
+  if [ -z "${DA_Api}" ] || [ -z "${DA_Api_Insecure}" ]; then
+    DA_Api=""
+    DA_Api_Insecure=""
+    _err "You haven't specified the DirectAdmin Login data, URL and whether you want check the DirectAdmin SSL cert. Please try again."
+    return 1
+  else
+    _saveaccountconf_mutable DA_Api "${DA_Api}"
+    _saveaccountconf_mutable DA_Api_Insecure "${DA_Api_Insecure}"
+    # Set whether curl should use secure or insecure mode
+    export HTTPS_INSECURE="${DA_Api_Insecure}"
+  fi
+}
+
+# Usage: _get_root _acme-challenge.www.example.com
+# Split the full domain to a domain and subdomain
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=example.com
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+  # Get a list of all the domains
+  # response will contain "list[]=example.com&list[]=example.org"
+  _da_api CMD_API_SHOW_DOMAINS "" "${domain}"
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$h"
+    if [ -z "$h" ]; then
+      # not valid
+      _debug "The given domain $h is not valid"
+      return 1
+    fi
+    if _contains "$response" "$h" >/dev/null; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+      _domain=$h
+      return 0
+    fi
+    p=$i
+    i=$(_math "$i" + 1)
+  done
+  _debug "Stop on 100"
+  return 1
+}
+
+# Usage: _da_api CMD_API_* data example.com
+# Use the DirectAdmin API and check the result
+# returns
+#  response="error=0&text=Result text&details="
+_da_api() {
+  cmd=$1
+  data=$2
+  domain=$3
+  _debug "$domain; $data"
+  response="$(_post "$data" "$DA_Api/$cmd" "" "POST")"
+
+  if [ "$?" != "0" ]; then
+    _err "error $cmd"
+    return 1
+  fi
+  _debug response "$response"
+
+  case "${cmd}" in
+    CMD_API_DNS_CONTROL)
+      # Parse the result in general
+      # error=0&text=Records Deleted&details=
+      # error=1&text=Cannot View Dns Record&details=No domain provided
+      err_field="$(_getfield "$response" 1 '&')"
+      txt_field="$(_getfield "$response" 2 '&')"
+      details_field="$(_getfield "$response" 3 '&')"
+      error="$(_getfield "$err_field" 2 '=')"
+      text="$(_getfield "$txt_field" 2 '=')"
+      details="$(_getfield "$details_field" 2 '=')"
+      _debug "error: ${error}, text: ${text}, details: ${details}"
+      if [ "$error" != "0" ]; then
+        _err "error $response"
+        return 1
+      fi
+      ;;
+    CMD_API_SHOW_DOMAINS) ;;
+  esac
+  return 0
+}
+
+# Usage: _DA_getDomainInfo
+# Get the root zone if possible
+_DA_getDomainInfo() {
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  else
+    _debug "The root domain: $_domain"
+    _debug "The sub domain: $_sub_domain"
+  fi
+  return 0
+}
+
+# Usage: _DA_addTxt
+# Use the API to add a record
+_DA_addTxt() {
+  curData="domain=${_domain}&action=add&type=TXT&name=${_sub_domain}&value=\"${txtvalue}\""
+  _debug "Calling _DA_addTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'"
+  _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"
+  _debug "Result of _DA_addTxt: '$response'"
+  if _contains "${response}" 'error=0'; then
+    _debug "Add TXT succeeded"
+    return 0
+  fi
+  _debug "Add TXT failed"
+  return 1
+}
+
+# Usage: _DA_rmTxt
+# Use the API to remove a record
+_DA_rmTxt() {
+  curData="domain=${_domain}&action=select&txtrecs0=name=${_sub_domain}&value=\"${txtvalue}\""
+  _debug "Calling _DA_rmTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'"
+  if _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"; then
+    _debug "Result of _DA_rmTxt: '$response'"
+  else
+    _err "Result of _DA_rmTxt: '$response'"
+  fi
+  if _contains "${response}" 'error=0'; then
+    _debug "RM TXT succeeded"
+    return 0
+  fi
+  _debug "RM TXT failed"
+  return 1
+}

+ 28 - 55
dnsapi/dns_namecom.sh

@@ -1,11 +1,12 @@
 #!/usr/bin/env sh
 #!/usr/bin/env sh
 
 
-#Author: RaidneII
+#Author: RaidenII
 #Created 06/28/2017
 #Created 06/28/2017
+#Updated 03/01/2018, rewrote to support name.com API v4
 #Utilize name.com API to finish dns-01 verifications.
 #Utilize name.com API to finish dns-01 verifications.
 ########  Public functions #####################
 ########  Public functions #####################
 
 
-Namecom_API="https://api.name.com/api"
+Namecom_API="https://api.name.com/v4"
 
 
 #Usage: dns_namecom_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 #Usage: dns_namecom_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 dns_namecom_add() {
 dns_namecom_add() {
@@ -39,21 +40,18 @@ dns_namecom_add() {
   # Find domain in domain list.
   # Find domain in domain list.
   if ! _namecom_get_root "$fulldomain"; then
   if ! _namecom_get_root "$fulldomain"; then
     _err "Unable to find domain specified."
     _err "Unable to find domain specified."
-    _namecom_logout
     return 1
     return 1
   fi
   fi
 
 
   # Add TXT record.
   # Add TXT record.
-  _namecom_addtxt_json="{\"hostname\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":\"300\",\"priority\":\"10\"}"
-  if _namecom_rest POST "dns/create/$_domain" "$_namecom_addtxt_json"; then
-    retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100")
-    if [ "$retcode" ]; then
+  _namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}"
+  if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then
+    _retvalue=$(printf "%s\n" "$response" | _egrep_o "\"$_sub_domain\"")
+    if [ "$_retvalue" ]; then
       _info "Successfully added TXT record, ready for validation."
       _info "Successfully added TXT record, ready for validation."
-      _namecom_logout
       return 0
       return 0
     else
     else
       _err "Unable to add the DNS record."
       _err "Unable to add the DNS record."
-      _namecom_logout
       return 1
       return 1
     fi
     fi
   fi
   fi
@@ -72,37 +70,28 @@ dns_namecom_rm() {
   # Find domain in domain list.
   # Find domain in domain list.
   if ! _namecom_get_root "$fulldomain"; then
   if ! _namecom_get_root "$fulldomain"; then
     _err "Unable to find domain specified."
     _err "Unable to find domain specified."
-    _namecom_logout
     return 1
     return 1
   fi
   fi
 
 
   # Get the record id.
   # Get the record id.
-  if _namecom_rest GET "dns/list/$_domain"; then
-    retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100")
-    if [ "$retcode" ]; then
-      _record_id=$(printf "%s\n" "$response" | _egrep_o "\"record_id\":\"[0-9]+\",\"name\":\"$fulldomain\",\"type\":\"TXT\"" | cut -d \" -f 4)
-      _debug record_id "$_record_id"
+  if _namecom_rest GET "domains/$_domain/records"; then
+    _record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+)
+    _debug record_id "$_record_id"
+    if [ "$_record_id" ]; then
       _info "Successfully retrieved the record id for ACME challenge."
       _info "Successfully retrieved the record id for ACME challenge."
     else
     else
       _err "Unable to retrieve the record id."
       _err "Unable to retrieve the record id."
-      _namecom_logout
       return 1
       return 1
     fi
     fi
   fi
   fi
 
 
   # Remove the DNS record using record id.
   # Remove the DNS record using record id.
-  _namecom_rmtxt_json="{\"record_id\":\"$_record_id\"}"
-  if _namecom_rest POST "dns/delete/$_domain" "$_namecom_rmtxt_json"; then
-    retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100")
-    if [ "$retcode" ]; then
-      _info "Successfully removed the TXT record."
-      _namecom_logout
-      return 0
-    else
-      _err "Unable to remove the DNS record."
-      _namecom_logout
-      return 1
-    fi
+  if _namecom_rest DELETE "domains/$_domain/records/$_record_id"; then
+    _info "Successfully removed the TXT record."
+    return 0
+  else
+    _err "Unable to delete record id."
+    return 1
   fi
   fi
 }
 }
 
 
@@ -112,8 +101,9 @@ _namecom_rest() {
   param=$2
   param=$2
   data=$3
   data=$3
 
 
-  export _H1="Content-Type: application/json"
-  export _H2="Api-Session-Token: $sessionkey"
+  export _H1="Authorization: Basic $_namecom_auth"
+  export _H2="Content-Type: application/json"
+
   if [ "$method" != "GET" ]; then
   if [ "$method" != "GET" ]; then
     response="$(_post "$data" "$Namecom_API/$param" "" "$method")"
     response="$(_post "$data" "$Namecom_API/$param" "" "$method")"
   else
   else
@@ -130,20 +120,15 @@ _namecom_rest() {
 }
 }
 
 
 _namecom_login() {
 _namecom_login() {
-  namecom_login_json="{\"username\":\"$Namecom_Username\",\"api_token\":\"$Namecom_Token\"}"
+  # Auth string
+  # Name.com API v4 uses http basic auth to authenticate
+  # need to convert the token for http auth
+  _namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | base64)
 
 
-  if _namecom_rest POST "login" "$namecom_login_json"; then
-    retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100")
+  if _namecom_rest GET "hello"; then
+    retcode=$(printf "%s\n" "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"")
     if [ "$retcode" ]; then
     if [ "$retcode" ]; then
-      _info "Successfully logged in. Fetching session token..."
-      sessionkey=$(printf "%s\n" "$response" | _egrep_o "\"session_token\":\".+" | cut -d \" -f 4)
-      if [ ! -z "$sessionkey" ]; then
-        _debug sessionkey "$sessionkey"
-        _info "Session key obtained."
-      else
-        _err "Unable to get session key."
-        return 1
-      fi
+      _info "Successfully logged in."
     else
     else
       _err "Logging in failed."
       _err "Logging in failed."
       return 1
       return 1
@@ -151,24 +136,12 @@ _namecom_login() {
   fi
   fi
 }
 }
 
 
-_namecom_logout() {
-  if _namecom_rest GET "logout"; then
-    retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100")
-    if [ "$retcode" ]; then
-      _info "Successfully logged out."
-    else
-      _err "Error logging out."
-      return 1
-    fi
-  fi
-}
-
 _namecom_get_root() {
 _namecom_get_root() {
   domain=$1
   domain=$1
   i=2
   i=2
   p=1
   p=1
 
 
-  if ! _namecom_rest GET "domain/list"; then
+  if ! _namecom_rest GET "domains"; then
     return 1
     return 1
   fi
   fi