Browse Source

Merge pull request #1831 from LLeny/master

Namecheap API
neil 7 years ago
parent
commit
56d6079c4a
3 changed files with 330 additions and 0 deletions
  1. 1 0
      README.md
  2. 21 0
      dnsapi/README.md
  3. 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. ConoHa (https://www.conoha.jp)
 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/)
 
 
 And: 
 And: 
 
 

+ 21 - 0
dnsapi/README.md

@@ -993,6 +993,27 @@ 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
 acme.sh --issue --dns dns_gdnsdk --dnssleep 300 -d example.com -d *.example.com
 ```
 ```
 
 
+## 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
+```
+
 # 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.

+ 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")
+}