Browse Source

Merge branch 'dev' into feature/hosting.de-plugin

Oliver Dick 6 years ago
parent
commit
d8885984ab
4 changed files with 340 additions and 4 deletions
  1. 1 0
      README.md
  2. 9 3
      deploy/fritzbox.sh
  3. 22 1
      dnsapi/README.md
  4. 308 0
      dnsapi/dns_namecheap.sh

+ 1 - 0
README.md

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

+ 9 - 3
deploy/fritzbox.sh

@@ -28,8 +28,10 @@ fritzbox_deploy() {
   _debug _cfullchain "$_cfullchain"
 
   if ! _exists iconv; then
-    _err "iconv not found"
-    return 1
+    if ! _exists perl; then
+      _err "iconv or perl not found"
+      return 1
+    fi
   fi
 
   _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
@@ -61,7 +63,11 @@ fritzbox_deploy() {
 
   _info "Log in to the FRITZ!Box"
   _fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
-  _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
+  if _exists iconv; then
+    _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
+  else
+    _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | md5sum | awk '{print $1}')"
+  fi
   _fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
 
   if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then

+ 22 - 1
dnsapi/README.md

@@ -993,7 +993,28 @@ and in rare cases I have seen over 5 minutes before google DNS catches it. There
 acme.sh --issue --dns dns_gdnsdk --dnssleep 300 -d example.com -d *.example.com
 ```
 
-## 53. Use hosting.de API
+## 53. Use Namecheap
+
+You will need your namecheap username, API KEY (https://www.namecheap.com/support/api/intro.aspx) and your external IP address (or an URL to get it), this IP will need to be whitelisted at Namecheap.
+Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
+
+```sh
+export NAMECHEAP_USERNAME="..."
+export NAMECHEAP_API_KEY="..."
+export NAMECHEAP_SOURCEIP="..."
+```
+
+NAMECHEAP_SOURCEIP can either be an IP address or an URL to provide it (e.g. https://ifconfig.co/ip).
+
+The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+Now you can issue a certificate.
+
+```sh
+acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com
+```
+
+## 54. Use hosting.de API
 
 Create an API key in your hosting.de account here: https://secure.hosting.de
 

+ 308 - 0
dnsapi/dns_namecheap.sh

@@ -0,0 +1,308 @@
+#!/usr/bin/env sh
+
+# Namecheap API
+# https://www.namecheap.com/support/api/intro.aspx
+#
+# Requires Namecheap API key set in NAMECHEAP_API_KEY, NAMECHEAP_SOURCEIP and NAMECHEAP_USERNAME set as environment variable
+# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
+
+########  Public functions #####################
+
+if [ "$STAGE" -eq 1 ]; then
+  NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response"
+else
+  NAMECHEAP_API="https://api.namecheap.com/xml.response"
+fi
+
+#Usage: dns_namecheap_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_namecheap_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! _namecheap_check_config; then
+    _err "$error"
+    return 1
+  fi
+
+  if ! _namecheap_set_publicip; then
+    return 1
+  fi
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+  _debug domain "$_domain"
+  _debug sub_domain "$_sub_domain"
+
+  _set_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_namecheap_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! _namecheap_set_publicip; then
+    return 1
+  fi
+
+  if ! _namecheap_check_config; then
+    _err "$error"
+    return 1
+  fi
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+  _debug domain "$_domain"
+  _debug sub_domain "$_sub_domain"
+
+  _del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+  domain=$1
+
+  if ! _namecheap_post "namecheap.domains.getList"; then
+    _err "$error"
+    return 1
+  fi
+
+  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" "$h"; then
+      _debug "$h not found"
+    else
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+      _domain="$h"
+      return 0
+    fi
+    p="$i"
+    i=$(_math "$i" + 1)
+  done
+  return 1
+}
+
+_namecheap_set_publicip() {
+
+  if [ -z "$NAMECHEAP_SOURCEIP" ]; then
+    _err "No Source IP specified for Namecheap API."
+    _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
+    return 1
+  else
+    _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
+    _debug sourceip "$NAMECHEAP_SOURCEIP"
+
+    ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
+    addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*')
+
+    _debug2 ip "$ip"
+    _debug2 addr "$addr"
+
+    if [ -n "$ip" ]; then
+      _publicip="$ip"
+    elif [ -n "$addr" ]; then
+      _publicip=$(_get "$addr")
+    else
+      _err "No Source IP specified for Namecheap API."
+      _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
+      return 1
+    fi
+  fi
+
+  _debug publicip "$_publicip"
+
+  return 0
+}
+
+_namecheap_post() {
+  command=$1
+  data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}"
+
+  response="$(_post "$data" "$NAMECHEAP_API" "" "POST")"
+  _debug2 response "$response"
+
+  if _contains "$response" "Status=\"ERROR\"" >/dev/null; then
+    error=$(echo "$response" | _egrep_o ">.*<\\/Error>" | cut -d '<' -f 1 | tr -d '>')
+    _err "error $error"
+    return 1
+  fi
+
+  return 0
+}
+
+_namecheap_parse_host() {
+  _host=$1
+  _debug _host "$_host"
+
+  _hostid=$(echo "$_host" | _egrep_o '\sHostId="[^"]*' | cut -d '"' -f 2)
+  _hostname=$(echo "$_host" | _egrep_o '\sName="[^"]*' | cut -d '"' -f 2)
+  _hosttype=$(echo "$_host" | _egrep_o '\sType="[^"]*' | cut -d '"' -f 2)
+  _hostaddress=$(echo "$_host" | _egrep_o '\sAddress="[^"]*' | cut -d '"' -f 2)
+  _hostmxpref=$(echo "$_host" | _egrep_o '\sMXPref="[^"]*' | cut -d '"' -f 2)
+  _hostttl=$(echo "$_host" | _egrep_o '\sTTL="[^"]*' | cut -d '"' -f 2)
+
+  _debug hostid "$_hostid"
+  _debug hostname "$_hostname"
+  _debug hosttype "$_hosttype"
+  _debug hostaddress "$_hostaddress"
+  _debug hostmxpref "$_hostmxpref"
+  _debug hostttl "$_hostttl"
+}
+
+_namecheap_check_config() {
+
+  if [ -z "$NAMECHEAP_API_KEY" ]; then
+    _err "No API key specified for Namecheap API."
+    _err "Create your key and export it as NAMECHEAP_API_KEY"
+    return 1
+  fi
+
+  if [ -z "$NAMECHEAP_USERNAME" ]; then
+    _err "No username key specified for Namecheap API."
+    _err "Create your key and export it as NAMECHEAP_USERNAME"
+    return 1
+  fi
+
+  _saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
+  _saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
+
+  return 0
+}
+
+_set_namecheap_TXT() {
+  subdomain=$2
+  txt=$3
+  tld=$(echo "$1" | cut -d '.' -f 2)
+  sld=$(echo "$1" | cut -d '.' -f 1)
+  request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld"
+
+  if ! _namecheap_post "$request"; then
+    _err "$error"
+    return 1
+  fi
+
+  hosts=$(echo "$response" | _egrep_o '<host[^>]*')
+  _debug hosts "$hosts"
+
+  if [ -z "$hosts" ]; then
+    _error "Hosts not found"
+    return 1
+  fi
+
+  _namecheap_reset_hostList
+
+  while read -r host; do
+    if _contains "$host" "<host"; then
+      _namecheap_parse_host "$host"
+      _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
+    fi
+  done <<EOT
+echo "$hosts"
+EOT
+
+  _namecheap_add_host "$subdomain" "TXT" "$txt" 10 120
+
+  _debug hostrequestfinal "$_hostrequest"
+
+  request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}"
+
+  if ! _namecheap_post "$request"; then
+    _err "$error"
+    return 1
+  fi
+
+  return 0
+}
+
+_del_namecheap_TXT() {
+  subdomain=$2
+  txt=$3
+  tld=$(echo "$1" | cut -d '.' -f 2)
+  sld=$(echo "$1" | cut -d '.' -f 1)
+  request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld"
+
+  if ! _namecheap_post "$request"; then
+    _err "$error"
+    return 1
+  fi
+
+  hosts=$(echo "$response" | _egrep_o '<host[^>]*')
+  _debug hosts "$hosts"
+
+  if [ -z "$hosts" ]; then
+    _error "Hosts not found"
+    return 1
+  fi
+
+  _namecheap_reset_hostList
+
+  found=0
+
+  while read -r host; do
+    if _contains "$host" "<host"; then
+      _namecheap_parse_host "$host"
+      if [ "$_hosttype" = "TXT" ] && [ "$_hostname" = "$subdomain" ] && [ "$_hostaddress" = "$txt" ]; then
+        _debug "TXT entry found"
+        found=1
+      else
+        _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
+      fi
+    fi
+  done <<EOT
+echo "$hosts"
+EOT
+
+  if [ $found -eq 0 ]; then
+    _debug "TXT entry not found"
+    return 0
+  fi
+
+  _debug hostrequestfinal "$_hostrequest"
+
+  request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}"
+
+  if ! _namecheap_post "$request"; then
+    _err "$error"
+    return 1
+  fi
+
+  return 0
+}
+
+_namecheap_reset_hostList() {
+  _hostindex=0
+  _hostrequest=""
+}
+
+#Usage: _namecheap_add_host HostName RecordType Address MxPref TTL
+_namecheap_add_host() {
+  _hostindex=$(_math "$_hostindex" + 1)
+  _hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5")
+}