Browse Source

DNS plugin for Danish service gratisdns.dk

Currently only supports primary domains. My use case does not involve
secondary domains so I'm not sure how it behaves, and cannot test it.
Might be as simple as turning all "primary"-references into a variable
that's either "primary" or "secondary", and make an extra check for this
in _get_domain...

Cookie handling heavily inspired by freedns plugin, including caching
the cookie in the config file, so we can rm without re-authenticating
Herman Sletteng 7 years ago
parent
commit
1756bbff84
3 changed files with 189 additions and 0 deletions
  1. 1 0
      README.md
  2. 20 0
      dnsapi/README.md
  3. 168 0
      dnsapi/dns_gdnsdk.sh

+ 1 - 0
README.md

@@ -325,6 +325,7 @@ You don't have to do anything manually!
 1. Google Cloud DNS API
 1. Google Cloud DNS API
 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)
 
 
 And: 
 And: 
 
 

+ 20 - 0
dnsapi/README.md

@@ -970,6 +970,26 @@ acme.sh --issue --dns dns_netcup -d example.com -d www.example.com
 
 
 The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 
 
+## 52. Use GratisDNS.dk
+
+GratisDNS.dk (https://gratisdns.dj/) does not provide an API to update DNS records (other than IPv4 and IPv6
+dynamic DNS addresses).  The acme.sh plugin therefore retrieves and updates domain TXT records by logging
+into the GratisDNS website to read the HTML and posting updates as HTTP.  The plugin needs to know your
+userid and password for the GratisDNS website.
+
+```sh
+export GDNSDK_Username="..."
+export GDNSDK_Password="..."
+```
+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_gdnsdk -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.

+ 168 - 0
dnsapi/dns_gdnsdk.sh

@@ -0,0 +1,168 @@
+#!/usr/bin/env sh
+#Author: Herman Sletteng
+#Report Bugs here: https://github.com/loial/acme.sh
+#
+#
+# Note, gratisdns requires a login first, so the script needs to handle
+# temporary cookies. Since acme.sh _get/_post currently don't directly support
+# cookies, I've defined wrapper functions _myget/_mypost to set the headers
+
+GDNSDK_API="https://admin.gratisdns.com"
+########  Public functions #####################
+#Usage: dns_gdnsdk_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_gdnsdk_add() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using gratisdns.dk"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+  if ! _gratisdns_login; then
+    _err "Login failed!"
+    return 1
+  fi
+  #finding domain zone
+  if ! _get_domain; then
+    _err "No matching root domain for $fulldomain found"
+    return 1
+  fi
+  # adding entry
+  _info "Adding the entry"
+  _mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1"
+  if _successful_update; then return 0; fi
+  _err "Couldn't create entry!"
+  return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_gdnsdk_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using gratisdns.dk"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+  if ! _gratisdns_login; then
+    _err "Login failed!"
+    return 1
+  fi
+  if ! _get_domain; then
+    _err "No matching root domain for $fulldomain found"
+    return 1
+  fi
+  _findentry "$fulldomain" "$txtvalue"
+  if [ -z "$_id" ]; then
+    _info "Entry doesn't exist, nothing to delete"
+    return 0
+  fi
+  _debug "Deleting record..."
+  _mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id"
+  # removing entry
+
+  if _successful_update; then return 0; fi
+  _err "Couldn't delete entry!"
+  return 1
+}
+
+####################  Private functions below ##################################
+
+_checkcredentials() {
+  GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}"
+  GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}"
+
+  if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then
+    GDNSDK_Username=""
+    GDNSDK_Password=""
+    _err "You haven't specified gratisdns.dk username and password yet."
+    _err "Please add credentials and try again."
+    return 1
+  fi
+  #save the credentials to the account conf file.
+  _saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username"
+  _saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password"
+  return 0
+}
+
+_checkcookie() {
+  GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}"
+  if [ -z "$GDNSDK_Cookie" ]; then
+    _debug "No cached cookie found"
+    return 1
+  fi
+  _myget "action="
+  if (echo "$_result" | grep -q "logmeout"); then
+    _debug "Cached cookie still valid"
+    return 0
+  fi
+  _debug "Cached cookie no longer valid"
+  GDNSDK_Cookie=""
+  _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
+  return 1
+}
+
+_gratisdns_login() {
+  if ! _checkcredentials; then return 1; fi
+
+  if _checkcookie; then
+    _debug "Already logged in"
+    return 0
+  fi
+  _debug "Logging into GratisDNS with user $GDNSDK_Username"
+
+  if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then
+    _err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post"
+    return 1
+  fi
+
+  GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)"
+
+  if [ -z "$GDNSDK_Cookie" ]; then
+    _err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file"
+    return 1
+  fi
+  export GDNSDK_Cookie
+  _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
+  return 0
+}
+
+_myget() {
+  #Adds cookie to request
+  export _H1="Cookie: $GDNSDK_Cookie"
+  _result=$(_get "$GDNSDK_API?$1")
+}
+_mypost() {
+  #Adds cookie to request
+  export _H1="Cookie: $GDNSDK_Cookie"
+  _result=$(_post "$1" "$GDNSDK_API")
+}
+
+_get_domain() {
+  _myget 'action=dns_primarydns'
+  _domains=$(echo "$_result" | grep -o -P ' domain="\K([[:alnum:].-_]+)')
+  if [ -z "$_domains" ]; then
+    _err "Primary domain list not found!"
+    return 1
+  fi
+  for _domain in $_domains; do
+    if (_endswith "$fulldomain" "$_domain"); then
+      _debug "Root domain: $_domain"
+      return 0
+    fi
+  done
+  return 1
+}
+
+_successful_update() {
+  if (echo "$_result" | grep -q 'table-success'); then return 0; fi
+  return 1
+}
+
+_findentry() {
+  #returns id of dns entry, if it exists
+  _myget "action=dns_primary_changeDNSsetup&user_domain=$_domain"
+  _id=$(echo "$_result" | grep -o -P "$1</td>\s*<td>$2.*?id=\K(\d*)")
+  if [ -n "$_id" ]; then
+    _debug "Entry found with _id=$_id"
+    return 0
+  fi
+  return 1
+}