Browse Source

Merge pull request #1717 from initit/master

added dnsapi for euserv.eu
neil 6 years ago
parent
commit
4f5995abc0
5 changed files with 435 additions and 3 deletions
  1. 1 0
      README.md
  2. 20 0
      deploy/README.md
  3. 34 2
      deploy/haproxy.sh
  4. 22 1
      dnsapi/README.md
  5. 358 0
      dnsapi/dns_euserv.sh

+ 1 - 0
README.md

@@ -320,6 +320,7 @@ You don't have to do anything manually!
 1. Loopia.se API
 1. Loopia.se API
 1. acme-dns (https://github.com/joohoi/acme-dns)
 1. acme-dns (https://github.com/joohoi/acme-dns)
 1. TELE3 (https://www.tele3.cz)
 1. TELE3 (https://www.tele3.cz)
+1. EUSERV.EU (https://www.euserv.eu)
 
 
 And: 
 And: 
 
 

+ 20 - 0
deploy/README.md

@@ -255,3 +255,23 @@ acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox
 ```sh
 ```sh
 acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
 acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
 ```
 ```
+
+## 10. Deploy the cert to HAProxy
+
+You must specify the path where you want the concatenated key and certificate chain written.
+```sh
+export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy
+```
+
+You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable.
+
+```sh
+export DEPLOY_HAPROXY_RELOAD="/usr/sbin/service haproxy restart"
+```
+
+You can then deploy the certificate as follows
+```sh
+acme.sh --deploy -d haproxy.example.com --deploy-hook haproxy
+```
+
+The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed.

+ 34 - 2
deploy/haproxy.sh

@@ -20,7 +20,39 @@ haproxy_deploy() {
   _debug _cca "$_cca"
   _debug _cca "$_cca"
   _debug _cfullchain "$_cfullchain"
   _debug _cfullchain "$_cfullchain"
 
 
-  _err "deploy cert to haproxy server, Not implemented yet"
-  return 1
+  # handle reload preference
+  DEFAULT_HAPROXY_RELOAD="/usr/sbin/service haproxy restart"
+  if [ -z "${DEPLOY_HAPROXY_RELOAD}" ]; then
+    _reload="${DEFAULT_HAPROXY_RELOAD}"
+    _cleardomainconf DEPLOY_HAPROXY_RELOAD
+  else
+    _reload="${DEPLOY_HAPROXY_RELOAD}"
+    _savedomainconf DEPLOY_HAPROXY_RELOAD "$DEPLOY_HAPROXY_RELOAD"
+  fi
+  _savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH"
+
+  # work out the path where the PEM file should go
+  _pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
+  if [ -z "$_pem_path" ]; then
+    _err "Path to save PEM file not found. Please define DEPLOY_HAPROXY_PEM_PATH."
+    return 1
+  fi
+  _pem_full_path="$_pem_path/$_cdomain.pem"
+  _info "Full path to PEM $_pem_full_path"
+
+  # combine the key and fullchain into a single pem and install
+  cat "$_cfullchain" "$_ckey" >"$_pem_full_path"
+  chmod 600 "$_pem_full_path"
+  _info "Certificate successfully deployed"
+
+  # restart HAProxy
+  _info "Run reload: $_reload"
+  if eval "$_reload"; then
+    _info "Reload success!"
+    return 0
+  else
+    _err "Reload error"
+    return 1
+  fi
 
 
 }
 }

+ 22 - 1
dnsapi/README.md

@@ -876,6 +876,27 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com
 ```
 ```
 
 
 The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed.
 The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed.
+## 47. Use Euserv.eu API
+
+First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung).
+[https://support.euserv.com](https://support.euserv.com)
+
+Once you've activate, login to your API Admin Interface and create an API account.
+Please specify the scope (active groups: domain) and assign the allowed IPs.
+
+```
+export EUSERV_Username="99999.user123"
+export EUSERV_Password="Asbe54gHde"
+```
+
+Ok, let's issue a cert now: (Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates!)
+```
+acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure
+```
+
+The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+Please report any issues to https://github.com/initit/acme.sh or to <github@initit.de>
 # 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.
@@ -896,4 +917,4 @@ See:  https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
 
 
 # Use lexicon DNS API
 # Use lexicon DNS API
 
 
-https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
+https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api

+ 358 - 0
dnsapi/dns_euserv.sh

@@ -0,0 +1,358 @@
+#!/usr/bin/env sh
+
+#This is the euserv.eu api wrapper for acme.sh
+#
+#Author: Michael Brueckner
+#Report Bugs: https://www.github.com/initit/acme.sh  or  mbr@initit.de
+
+#
+#EUSERV_Username="username"
+#
+#EUSERV_Password="password"
+#
+# Dependencies:
+# -------------
+# - none -
+
+EUSERV_Api="https://api.euserv.net"
+
+########  Public functions #####################
+
+#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_euserv_add() {
+  fulldomain="$(echo "$1" | _lower_case)"
+  txtvalue=$2
+
+  EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
+  EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
+  if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
+    EUSERV_Username=""
+    EUSERV_Password=""
+    _err "You don't specify euserv user and password yet."
+    _err "Please create your key and try again."
+    return 1
+  fi
+
+  #save the user and email to the account conf file.
+  _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
+  _saveaccountconf_mutable EUSERV_Password "$EUSERV_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"
+  _info "Adding record"
+  if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
+    return 1
+  fi
+
+}
+
+#fulldomain txtvalue
+dns_euserv_rm() {
+
+  fulldomain="$(echo "$1" | _lower_case)"
+  txtvalue=$2
+
+  EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
+  EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
+  if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
+    EUSERV_Username=""
+    EUSERV_Password=""
+    _err "You don't specify euserv user and password yet."
+    _err "Please create your key and try again."
+    return 1
+  fi
+
+  #save the user and email to the account conf file.
+  _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
+  _saveaccountconf_mutable EUSERV_Password "$EUSERV_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"
+
+  _debug "Getting txt records"
+
+  xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
+  <methodCall>
+    <methodName>domain.dns_get_active_records</methodName>
+    <params>
+      <param>
+       <value>
+         <struct>
+           <member>
+             <name>login</name>
+             <value>
+               <string>%s</string>
+             </value>
+            </member>
+            <member>
+              <name>password</name>
+              <value>
+                <string>%s</string>
+              </value>
+            </member>
+            <member>
+              <name>domain_id</name>
+              <value>
+                <int>%s</int>
+              </value>
+            </member>
+          </struct>
+        </value>
+      </param>
+    </params>
+  </methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id")
+
+  export _H1="Content-Type: text/xml"
+  response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
+
+  if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
+    _err "Error could not get txt records"
+    _debug "xml_content" "$xml_content"
+    _debug "response" "$response"
+    return 1
+  fi
+
+  if ! echo "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then
+    _info "Do not need to delete record"
+  else
+    # find XML block where txtvalue is in. The record_id is allways prior this line!
+    _endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1)
+    # record_id is the last <name> Tag with a number before the row _endLine, identified by </name><value><struct> 
+    _record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '</name><value><struct>' | _tail_n 1 | sed 's/.*<name>\([0-9]*\)<\/name>.*/\1/')
+    _info "Deleting record"
+    _euserv_delete_record "$_record_id"
+  fi
+
+}
+
+####################  Private functions below ##################################
+
+_get_root() {
+  domain=$1
+  _debug "get root"
+
+  # Just to read the domain_orders once
+
+  domain=$1
+  i=2
+  p=1
+
+  if ! _euserv_get_domain_orders; then
+    return 1
+  fi
+
+  # Get saved response with domain_orders
+  response="$_euserv_domain_orders"
+
+  while true; do
+    h=$(echo "$domain" | cut -d . -f $i-100)
+    _debug h "$h"
+    if [ -z "$h" ]; then
+      #not valid
+      return 1
+    fi
+
+    if _contains "$response" "$h"; then
+      _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+      _domain="$h"
+      if ! _euserv_get_domain_id "$_domain"; then
+        _err "invalid domain"
+        return 1
+      fi
+      return 0
+    fi
+    p=$i
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}
+
+_euserv_get_domain_orders() {
+  # returns: _euserv_domain_orders
+
+  _debug "get domain_orders"
+
+  xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
+  <methodCall>
+    <methodName>domain.get_domain_orders</methodName>
+    <params>
+      <param>
+        <value>
+          <struct>
+            <member>
+              <name>login</name>
+              <value><string>%s</string></value>
+            </member>
+            <member>
+              <name>password</name>
+              <value><string>%s</string></value>
+            </member>
+          </struct>
+        </value>
+      </param>
+    </params>
+  </methodCall>' "$EUSERV_Username" "$EUSERV_Password")
+
+  export _H1="Content-Type: text/xml"
+  response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
+
+  if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
+    _err "Error could not get domain orders"
+    _debug "xml_content" "$xml_content"
+    _debug "response" "$response"
+    return 1
+  fi
+
+  # save response to reduce API calls
+  _euserv_domain_orders="$response"
+  return 0
+}
+
+_euserv_get_domain_id() {
+  # returns: _euserv_domain_id
+  domain=$1
+  _debug "get domain_id"
+
+  # find line where the domain name is within the $response
+  _startLine=$(echo "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1)
+  # next occurency of domain_id after the domain_name is the correct one
+  _euserv_domain_id=$(echo "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | _head_n 1 | sed 's/.*<i4>\([0-9]*\)<\/i4>.*/\1/')
+
+  if [ -z "$_euserv_domain_id" ]; then
+    _err "Could not find domain_id for domain $domain"
+    _debug "_euserv_domain_orders" "$_euserv_domain_orders"
+    return 1
+  fi
+
+  return 0
+}
+
+_euserv_delete_record() {
+  record_id=$1
+  xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
+  <methodCall>
+    <methodName>domain.dns_delete_record</methodName>
+    <params>
+      <param>
+       <value>
+         <struct>
+           <member>
+             <name>login</name>
+             <value>
+               <string>%s</string>
+             </value>
+            </member>
+            <member>
+              <name>password</name>
+              <value>
+                <string>%s</string>
+              </value>
+            </member>
+            <member>
+              <name>dns_record_id</name>
+              <value>
+                <int>%s</int>
+              </value>
+            </member>
+          </struct>
+        </value>
+      </param>
+    </params>
+  </methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$record_id")
+
+  export _H1="Content-Type: text/xml"
+  response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
+
+  if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
+    _err "Error deleting record"
+    _debug "xml_content" "$xml_content"
+    _debug "response" "$response"
+    return 1
+  fi
+
+  return 0
+
+}
+
+_euserv_add_record() {
+  domain=$1
+  sub_domain=$2
+  txtval=$3
+
+  xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
+  <methodCall>
+  <methodName>domain.dns_create_record</methodName>
+  <params>
+   <param>
+    <value>
+     <struct>
+      <member>
+       <name>login</name>
+       <value>
+        <string>%s</string>
+       </value>
+      </member>
+      <member>
+       <name>password</name>
+       <value>
+        <string>%s</string></value>
+      </member>
+      <member>
+       <name>domain_id</name>
+       <value>
+        <int>%s</int>
+       </value>
+      </member>
+      <member>
+       <name>dns_record_subdomain</name>
+       <value>
+        <string>%s</string>
+       </value>
+      </member>
+      <member>
+       <name>dns_record_type</name>
+       <value>
+        <string>TXT</string>
+       </value>
+      </member>
+      <member>
+       <name>dns_record_value</name>
+       <value>
+        <string>%s</string>
+       </value>
+      </member>
+      <member>
+       <name>dns_record_ttl</name>
+       <value>
+        <int>300</int>
+       </value>
+      </member>
+     </struct>
+    </value>
+   </param>
+  </params>
+  </methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval")
+
+  export _H1="Content-Type: text/xml"
+  response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
+
+  if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
+    _err "Error could not create record"
+    _debug "xml_content" "$xml_content"
+    _debug "response" "$response"
+    return 1
+  fi
+
+  return 0
+}