Browse Source

Merge pull request #1939 from Neilpang/dev

sync
neil 6 years ago
parent
commit
d0c97a589b
6 changed files with 363 additions and 1 deletions
  1. 1 0
      Dockerfile
  2. 2 0
      README.md
  3. 1 1
      acme.sh
  4. 40 0
      dnsapi/README.md
  5. 109 0
      dnsapi/dns_hostingde.sh
  6. 210 0
      dnsapi/dns_mydnsjp.sh

+ 1 - 0
Dockerfile

@@ -4,6 +4,7 @@ RUN apk update -f \
   && apk --no-cache add -f \
   && apk --no-cache add -f \
   openssl \
   openssl \
   coreutils \
   coreutils \
+  bind-tools \
   curl \
   curl \
   socat \
   socat \
   && rm -rf /var/cache/apk/*
   && rm -rf /var/cache/apk/*

+ 2 - 0
README.md

@@ -327,6 +327,8 @@ You don't have to do anything manually!
 1. netcup DNS API (https://www.netcup.de)
 1. netcup DNS API (https://www.netcup.de)
 1. GratisDNS.dk (https://gratisdns.dk)
 1. GratisDNS.dk (https://gratisdns.dk)
 1. Namecheap API (https://www.namecheap.com/)
 1. Namecheap API (https://www.namecheap.com/)
+1. MyDNS.JP API (https://www.mydns.jp/)
+1. hosting.de (https://www.hosting.de)
 
 
 And: 
 And: 
 
 

+ 1 - 1
acme.sh

@@ -5485,7 +5485,7 @@ Parameters:
 
 
   --server SERVER                   ACME Directory Resource URI. (default: https://acme-v01.api.letsencrypt.org/directory)
   --server SERVER                   ACME Directory Resource URI. (default: https://acme-v01.api.letsencrypt.org/directory)
   --accountconf                     Specifies a customized account config file.
   --accountconf                     Specifies a customized account config file.
-  --home                            Specifies the home dir for $PROJECT_NAME .
+  --home                            Specifies the home dir for $PROJECT_NAME.
   --cert-home                       Specifies the home dir to save all the certs, only valid for '--install' command.
   --cert-home                       Specifies the home dir to save all the certs, only valid for '--install' command.
   --config-home                     Specifies the home dir to save all the configurations.
   --config-home                     Specifies the home dir to save all the configurations.
   --useragent                       Specifies the user agent string. it will be saved for future use too.
   --useragent                       Specifies the user agent string. it will be saved for future use too.

+ 40 - 0
dnsapi/README.md

@@ -1014,6 +1014,46 @@ Now you can issue a certificate.
 acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com
 acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com
 ```
 ```
 
 
+## 54. Use MyDNS.JP API
+
+First, register to MyDNS.JP and get MasterID and Password.
+
+```
+export MYDNSJP_MasterID=MasterID
+export MYDNSJP_Password=Password
+```
+
+To issue a certificate:
+
+```
+acme.sh --issue --dns dns_mydnsjp -d example.com -d www.example.com
+```
+The `MYDNSJP_MasterID` and `MYDNSJP_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+## 55. Use hosting.de API
+
+Create an API key in your hosting.de account here: https://secure.hosting.de
+
+The key needs the following rights:
+- DNS_ZONES_EDIT
+- DNS_ZONES_LIST
+
+Set your API Key and endpoint:
+
+```
+export HOSTINGDE_APIKEY='xxx'
+export HOSTINGDE_ENDPOINT='https://secure.hosting.de'
+```
+
+The plugin can also be used for the http.net API. http.net customers have to set endpoint to https://partner.http.net.
+
+Ok, let's issue a cert now:
+```
+acme.sh --issue --dns dns_hostingde -d example.com -d *.example.com
+```
+
+The hosting.de API key and endpoint 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.

+ 109 - 0
dnsapi/dns_hostingde.sh

@@ -0,0 +1,109 @@
+#!/usr/bin/env sh
+
+# hosting.de API
+
+# Values to export:
+# export HOSTINGDE_ENDPOINT='https://secure.hosting.de'
+# export HOSTINGDE_APIKEY='xxxxx'
+
+########  Public functions #####################
+
+dns_hostingde_add() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+  _debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'"
+  _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord
+}
+
+dns_hostingde_rm() {
+  fulldomain="${1}"
+  txtvalue="${2}"
+  _debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'"
+  _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord
+}
+
+#################### own Private functions below ##################################
+
+_hostingde_apiKey() {
+  HOSTINGDE_APIKEY="${HOSTINGDE_APIKEY:-$(_readaccountconf_mutable HOSTINGDE_APIKEY)}"
+  if [ -z "$HOSTINGDE_APIKEY" ] || [ -z "$HOSTINGDE_ENDPOINT" ]; then
+    HOSTINGDE_APIKEY=""
+    HOSTINGDE_ENDPOINT=""
+    _err "You haven't specified hosting.de API key or endpoint yet."
+    _err "Please create your key and try again."
+    return 1
+  fi
+
+  _saveaccountconf_mutable HOSTINGDE_APIKEY "$HOSTINGDE_APIKEY"
+  _saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT"
+}
+
+_hostingde_getZoneConfig() {
+  _info "Getting ZoneConfig"
+  curZone="${fulldomain#*.}"
+  returnCode=1
+  while _contains "${curZone}" "\\."; do
+    curData="{\"filter\":{\"field\":\"zoneName\",\"value\":\"${curZone}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
+    curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind")"
+    _debug "Calling zoneConfigsFind: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind'"
+    _debug "Result of zoneConfigsFind: '$curResult'"
+    if _contains "${curResult}" '"status": "error"'; then
+      if _contains "${curResult}" '"code": 10109'; then
+        _err "The API-Key is invalid or could not be found"
+      else
+        _err "UNKNOWN API ERROR"
+      fi
+      returnCode=1
+      break
+    fi
+    if _contains "${curResult}" '"totalEntries": 1'; then
+      _info "Retrieved zone data."
+      _debug "Zone data: '${curResult}'"
+
+      # read ZoneConfigId for later update
+      zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
+      _debug "zoneConfigId '${zoneConfigId}'"
+      returnCode=0
+      break
+    fi
+    curZone="${curZone#*.}"
+  done
+  if [ $returnCode -ne 0 ]; then
+    _info "ZoneEnd reached, Zone ${curZone} not found in hosting.de API"
+  fi
+  return $returnCode
+}
+
+_hostingde_addRecord() {
+  _info "Adding record to zone"
+  curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}"
+  curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
+  _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
+  _debug "Result of zoneUpdate: '$curResult'"
+  if _contains "${curResult}" '"status": "error"'; then
+    if _contains "${curResult}" '"code": 10109'; then
+      _err "The API-Key is invalid or could not be found"
+    else
+      _err "UNKNOWN API ERROR"
+    fi
+    return 1
+  fi
+  return 0
+}
+
+_hostingde_removeRecord() {
+  _info "Removing record from zone"
+  curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}"
+  curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
+  _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
+  _debug "Result of zoneUpdate: '$curResult'"
+  if _contains "${curResult}" '"status": "error"'; then
+    if _contains "${curResult}" '"code": 10109'; then
+      _err "The API-Key is invalid or could not be found"
+    else
+      _err "UNKNOWN API ERROR"
+    fi
+    return 1
+  fi
+  return 0
+}

+ 210 - 0
dnsapi/dns_mydnsjp.sh

@@ -0,0 +1,210 @@
+#!/usr/bin/env sh
+
+#Here is a api script for MyDNS.JP.
+#This file name is "dns_mydnsjp.sh"
+#So, here must be a method   dns_mydnsjp_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: epgdatacapbon
+#Report Bugs here: https://github.com/epgdatacapbon/acme.sh
+#
+########  Public functions #####################
+
+# Export MyDNS.JP MasterID and Password in following variables...
+#  MYDNSJP_MasterID=MasterID
+#  MYDNSJP_Password=Password
+
+MYDNSJP_API="https://www.mydns.jp"
+
+#Usage: dns_mydnsjp_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_mydnsjp_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _info "Using mydnsjp"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  # Load the credentials from the account conf file
+  MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
+  MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
+  if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
+    MYDNSJP_MasterID=""
+    MYDNSJP_Password=""
+    _err "You don't specify mydnsjp api MasterID and Password yet."
+    _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again."
+    return 1
+  fi
+
+  # Save the credentials to the account conf file
+  _saveaccountconf_mutable MYDNSJP_MasterID "$MYDNSJP_MasterID"
+  _saveaccountconf_mutable MYDNSJP_Password "$MYDNSJP_Password"
+
+  _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"
+
+  if _mydnsjp_api "REGIST" "$_domain" "$txtvalue"; then
+    if printf -- "%s" "$response" | grep "OK." >/dev/null; then
+      _info "Added, OK"
+      return 0
+    else
+      _err "Add txt record error."
+      return 1
+    fi
+  fi
+  _err "Add txt record error."
+
+  return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_mydnsjp_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _info "Removing TXT record"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  # Load the credentials from the account conf file
+  MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
+  MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
+  if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
+    MYDNSJP_MasterID=""
+    MYDNSJP_Password=""
+    _err "You don't specify mydnsjp api MasterID and Password yet."
+    _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again."
+    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"
+
+  if _mydnsjp_api "DELETE" "$_domain" "$txtvalue"; then
+    if printf -- "%s" "$response" | grep "OK." >/dev/null; then
+      _info "Deleted, OK"
+      return 0
+    else
+      _err "Delete txt record error."
+      return 1
+    fi
+  fi
+  _err "Delete txt record error."
+
+  return 1
+}
+
+####################  Private functions below ##################################
+# _acme-challenge.www.domain.com
+# returns
+#  _sub_domain=_acme-challenge.www
+#  _domain=domain.com
+_get_root() {
+  fulldomain=$1
+  i=2
+  p=1
+
+  # Get the root domain
+  _mydnsjp_retrieve_domain
+  if [ "$?" != "0" ]; then
+    # not valid
+    return 1
+  fi
+
+  while true; do
+    _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+
+    if [ -z "$_domain" ]; then
+      # not valid
+      return 1
+    fi
+
+    if [ "$_domain" = "$_root_domain" ]; then
+      _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+      return 0
+    fi
+
+    p=$i
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}
+
+# Retrieve the root domain
+# returns 0 success
+_mydnsjp_retrieve_domain() {
+  _debug "Login to MyDNS.JP"
+
+  response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")"
+  cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
+
+  # If cookies is not empty then logon successful
+  if [ -z "$cookie" ]; then
+    _err "Fail to get a cookie."
+    return 1
+  fi
+
+  _debug "Retrieve DOMAIN INFO page"
+
+  export _H1="Cookie:${cookie}"
+
+  response="$(_get "$MYDNSJP_API/?MENU=300")"
+
+  if [ "$?" != "0" ]; then
+    _err "Fail to retrieve DOMAIN INFO."
+    return 1
+  fi
+
+  _root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/')
+
+  # Logout
+  response="$(_get "$MYDNSJP_API/?MENU=090")"
+
+  _debug _root_domain "$_root_domain"
+
+  if [ -z "$_root_domain" ]; then
+    _err "Fail to get the root domain."
+    return 1
+  fi
+
+  return 0
+}
+
+_mydnsjp_api() {
+  cmd=$1
+  domain=$2
+  txtvalue=$3
+
+  # Base64 encode the credentials
+  credentials=$(printf "%s:%s" "$MYDNSJP_MasterID" "$MYDNSJP_Password" | _base64)
+
+  # Construct the HTTP Authorization header
+  export _H1="Content-Type: application/x-www-form-urlencoded"
+  export _H2="Authorization: Basic ${credentials}"
+
+  response="$(_post "CERTBOT_DOMAIN=$domain&CERTBOT_VALIDATION=$txtvalue&EDIT_CMD=$cmd" "$MYDNSJP_API/directedit.html")"
+
+  if [ "$?" != "0" ]; then
+    _err "error $domain"
+    return 1
+  fi
+
+  _debug2 response "$response"
+
+  return 0
+}