From 360dc140ea101a319973afeaead4cdb2a016f027 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Thu, 3 May 2018 01:28:56 -0500 Subject: [PATCH 01/14] implement basic haproxy deploy HAProxy requires the certificate chain and key to be concatenated and placed somewhere (can be anywhere). This script expects a single environment variable with the path where the concatenated PEM file should be written --- deploy/haproxy.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 34efbb1..7eb23e2 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -20,7 +20,16 @@ haproxy_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _err "deploy cert to haproxy server, Not implemented yet" - return 1 + # combine the key and fullchain into a single pem and install + _savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH" + + _pem_full_path="$DEPLOY_HAPROXY_PEM_PATH/$_cdomain.pem" + _info "Full path to PEM $_pem_full_path" + + cat "$_cfullchain" "$_ckey" > "$_pem_full_path" + chmod 600 "$_pem_full_path" + + _info "Certificate successfully deployed" + return 0 } From 1eae73105a04e296e5c3d3524ccb2ab929196cd1 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Thu, 3 May 2018 01:33:06 -0500 Subject: [PATCH 02/14] add docs for HAProxy deployment --- deploy/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index 0b820df..8fb6595 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -255,3 +255,17 @@ acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox ```sh 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 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. From 7573e560b63d009963711ef5df61d41837466a03 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Thu, 3 May 2018 10:06:05 -0500 Subject: [PATCH 03/14] Add conditional check to ensure path is provided --- deploy/haproxy.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 7eb23e2..c263ab7 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -23,7 +23,13 @@ haproxy_deploy() { # combine the key and fullchain into a single pem and install _savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH" - _pem_full_path="$DEPLOY_HAPROXY_PEM_PATH/$_cdomain.pem" + _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" cat "$_cfullchain" "$_ckey" > "$_pem_full_path" From ec73aeba169cb2650491931db6fed4e62033ab2e Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Thu, 3 May 2018 12:17:26 -0500 Subject: [PATCH 04/14] remove whitespace --- deploy/haproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index c263ab7..d5cab9f 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -32,7 +32,7 @@ haproxy_deploy() { _pem_full_path="$_pem_path/$_cdomain.pem" _info "Full path to PEM $_pem_full_path" - cat "$_cfullchain" "$_ckey" > "$_pem_full_path" + cat "$_cfullchain" "$_ckey" >"$_pem_full_path" chmod 600 "$_pem_full_path" _info "Certificate successfully deployed" From 5f593994c785f74732ef3728d68d86b56713eca4 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Thu, 3 May 2018 12:25:11 -0500 Subject: [PATCH 05/14] remove more whitespace (trying to get TravisCI working) --- deploy/haproxy.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index d5cab9f..77f9c94 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -28,7 +28,6 @@ haproxy_deploy() { _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" From e9e999542d62ef30f417f84254264051060068a6 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Fri, 4 May 2018 10:14:31 -0500 Subject: [PATCH 06/14] add reload --- deploy/haproxy.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 77f9c94..0b89b7a 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -20,9 +20,18 @@ haproxy_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - # combine the key and fullchain into a single pem and install + # 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." @@ -31,10 +40,19 @@ haproxy_deploy() { _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" - return 0 + + # restart HAProxy + _info "Run reload: $_reload" + if eval "$_reload"; then + _info "Reload success!" + return 0 + else + _err "Reload error" + return 1 + fi } From afe5cb588d97ba723680c181ec5c1bd69892cd2c Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Fri, 4 May 2018 10:25:54 -0500 Subject: [PATCH 07/14] update for POSIX compliance --- deploy/haproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 0b89b7a..5c1a40e 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -22,7 +22,7 @@ haproxy_deploy() { # handle reload preference DEFAULT_HAPROXY_RELOAD="/usr/sbin/service haproxy restart" - if [[ -z "${DEPLOY_HAPROXY_RELOAD}" ]]; then + if [ -z "${DEPLOY_HAPROXY_RELOAD}" ]; then _reload="${DEFAULT_HAPROXY_RELOAD}" _cleardomainconf DEPLOY_HAPROXY_RELOAD else From c9818ea2c46183ba8db09a21fe6308f93a159028 Mon Sep 17 00:00:00 2001 From: Daniel Watrous Date: Fri, 4 May 2018 13:03:27 -0500 Subject: [PATCH 08/14] add documentation for reload command --- deploy/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index 8fb6595..181989d 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -263,6 +263,12 @@ You must specify the path where you want the concatenated key and certificate ch 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 From 28e4bcf67f82c9aa6e88224aa528cb629eff5743 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:04:18 +0200 Subject: [PATCH 09/14] initial version with Euserv.eu DNS API Support --- dnsapi/dns_euserv.sh | 358 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 dnsapi/dns_euserv.sh diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh new file mode 100644 index 0000000..1a58df5 --- /dev/null +++ b/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" + _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + +} + +#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 ' + + domain.dns_get_active_records + + + + + + login + + %s + + + + password + + %s + + + + domain_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get txt records" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + +# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) +# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + _info "Do not need to delete record" + else + # find block where txtvalue is in. the record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last entry with a number, identified by the postfix of + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _info "Deleting record" + _euserv_delete_record "$_record_id" + fi + +} + +#################### Private functions below ################################## + +_euserv_get_domain_orders() { +# returns: _euserv_domain_orders + + _debug "get domain_orders" + + xml_content=$(printf ' + + domain.get_domain_orders + + + + + + login + %s + + + password + %s + + + + + + ' "$EUSERV_Username" "$EUSERV_Password") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get domain orders" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + _euserv_domain_orders="$response" + return 0 +} + +_euserv_get_domain_id() { +# returns: _euserv_domain_id + domain=$1 + _debug "get domain_id" + + _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) + _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([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 + +} + +_get_root() { + domain=$1 + _debug "get root" + + # Just to read the domain_orders once + + domain=$1 + i=2 + p=1 + _euserv_get_domain_orders + response="$_euserv_domain_orders" + + 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 + _sub_domain=$(printf "%s" "$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 + +} + +# TODO +_euserv_delete_record() { + record_id=$1 + xml_content=$(printf ' + + domain.dns_delete_record + + + + + + login + + %s + + + + password + + %s + + + + dns_record_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$record_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; 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 ' + + domain.dns_create_record + + + + + + login + + %s + + + + password + + %s + + + domain_id + + %s + + + + dns_record_subdomain + + %s + + + + dns_record_type + + TXT + + + + dns_record_value + + %s + + + + dns_record_ttl + + 300 + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not create record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi +# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" +# _debug "_dns_record_id" "$_dns_record_id" + return 0 +} From 94f91ae6878f8652cc947a98aa7b78dd42334c0c Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:04:18 +0200 Subject: [PATCH 10/14] initial version with Euserv.eu DNS API Support - added dnsapi/dns_euserv.sh - modified dnsapi/README.md --- dnsapi/README.md | 25 ++- dnsapi/dns_euserv.sh | 358 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_euserv.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index ef6c9d0..9f60764 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,6 +876,29 @@ 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. +## 47. euserv.eu API to automatically issue cert + +First you need to login to your euserv.eu account to 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 Account. +Please specify the scope (active groups: domain) and assign the allowed IPs. + +Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! + +``` +export EUSERV_Username="99999.user123" +export EUSERV_Password="Asbe54gHde" +``` + +Ok, let's issue a cert now: +``` +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 # Use custom API If your API is not supported yet, you can write your own DNS API. @@ -896,4 +919,4 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # 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 \ No newline at end of file diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh new file mode 100644 index 0000000..1a58df5 --- /dev/null +++ b/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" + _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + +} + +#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 ' + + domain.dns_get_active_records + + + + + + login + + %s + + + + password + + %s + + + + domain_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get txt records" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + +# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) +# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + _info "Do not need to delete record" + else + # find block where txtvalue is in. the record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last entry with a number, identified by the postfix of + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _info "Deleting record" + _euserv_delete_record "$_record_id" + fi + +} + +#################### Private functions below ################################## + +_euserv_get_domain_orders() { +# returns: _euserv_domain_orders + + _debug "get domain_orders" + + xml_content=$(printf ' + + domain.get_domain_orders + + + + + + login + %s + + + password + %s + + + + + + ' "$EUSERV_Username" "$EUSERV_Password") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get domain orders" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + _euserv_domain_orders="$response" + return 0 +} + +_euserv_get_domain_id() { +# returns: _euserv_domain_id + domain=$1 + _debug "get domain_id" + + _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) + _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([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 + +} + +_get_root() { + domain=$1 + _debug "get root" + + # Just to read the domain_orders once + + domain=$1 + i=2 + p=1 + _euserv_get_domain_orders + response="$_euserv_domain_orders" + + 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 + _sub_domain=$(printf "%s" "$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 + +} + +# TODO +_euserv_delete_record() { + record_id=$1 + xml_content=$(printf ' + + domain.dns_delete_record + + + + + + login + + %s + + + + password + + %s + + + + dns_record_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$record_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; 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 ' + + domain.dns_create_record + + + + + + login + + %s + + + + password + + %s + + + domain_id + + %s + + + + dns_record_subdomain + + %s + + + + dns_record_type + + TXT + + + + dns_record_value + + %s + + + + dns_record_ttl + + 300 + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not create record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi +# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" +# _debug "_dns_record_id" "$_dns_record_id" + return 0 +} From d99968ee6d39c759909c8742592c349a00f336fd Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:25:35 +0200 Subject: [PATCH 11/14] Modified dnsapi/README.md --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 9f60764..49f2625 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -878,10 +878,10 @@ 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. ## 47. euserv.eu API to automatically issue cert -First you need to login to your euserv.eu account to activate your API Administration (API Verwaltung). +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 Account. +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. Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! From 616b0b6baa0eb08670ac0876f6b74a7627076629 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 22:50:52 +0200 Subject: [PATCH 12/14] fixed shfmt related errors in dns_euserv.sh and modified README.md --- README.md | 1 + dnsapi/README.md | 6 +- dnsapi/dns_euserv.sh | 142 +++++++++++++++++++++---------------------- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index f395e49..bda7252 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,7 @@ You don't have to do anything manually! 1. Loopia.se API 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) +1. EUSERV.EU (https://www.euserv.eu) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 49f2625..1f394f9 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,7 +876,7 @@ 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. -## 47. euserv.eu API to automatically issue cert +## 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) @@ -884,14 +884,12 @@ First you need to login to your euserv.eu account and activate your API Administ 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. -Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! - ``` export EUSERV_Username="99999.user123" export EUSERV_Password="Asbe54gHde" ``` -Ok, let's issue a cert now: +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 ``` diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index 1a58df5..cb1e0a4 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -42,11 +42,12 @@ dns_euserv_add() { _err "invalid domain" return 1 fi - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - + _debug "_sub_domain" "$_sub_domain" + _debug "_domain" "$_domain" _info "Adding record" - _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then + return 1 + fi } @@ -114,24 +115,20 @@ dns_euserv_rm() { export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then - _err "Error could not get txt records" + if ! _contains "$response" "status100"; then + _err "Error could not get txt records" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi -# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) -# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) - if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then _info "Do not need to delete record" else - # find block where txtvalue is in. the record_id is allways prior this line! - _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) - # record_id is the last entry with a number, identified by the postfix of - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + # find XML block where txtvalue is in. The record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last Tag with a number before the row _endLine, identified by + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -140,11 +137,52 @@ dns_euserv_rm() { #################### 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=$(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 + _sub_domain=$(printf "%s" "$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 + # returns: _euserv_domain_orders _debug "get domain_orders" - + xml_content=$(printf ' domain.get_domain_orders @@ -165,76 +203,41 @@ _euserv_get_domain_orders() { ' "$EUSERV_Username" "$EUSERV_Password") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + + if ! _contains "$response" "status100"; 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 + # returns: _euserv_domain_id domain=$1 _debug "get domain_id" - _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) - _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + # find line where the domain name is within the $response + _startLine=$(printf '%s' "$_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=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') - if [ -z "$_euserv_domain_id" ] ; then + 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 - -} - -_get_root() { - domain=$1 - _debug "get root" - - # Just to read the domain_orders once - - domain=$1 - i=2 - p=1 - _euserv_get_domain_orders - response="$_euserv_domain_orders" - - 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 - _sub_domain=$(printf "%s" "$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 + return 0 } -# TODO _euserv_delete_record() { record_id=$1 xml_content=$(printf ' @@ -271,14 +274,13 @@ _euserv_delete_record() { export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + if ! _contains "$response" "status100"; then _err "Error deleting record" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi - + return 0 } @@ -340,19 +342,17 @@ _euserv_add_record() { - ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval") export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + if ! _contains "$response" "status100"; then _err "Error could not create record" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi -# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" -# _debug "_dns_record_id" "$_dns_record_id" + return 0 } From 261cc448f78729ec52992b9a010e7eb32daca0e1 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 23:00:26 +0200 Subject: [PATCH 13/14] fixed shfmt related errors in dns_euserv.sh and modified README.md --- dnsapi/dns_euserv.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index cb1e0a4..44120a3 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -43,10 +43,10 @@ dns_euserv_add() { return 1 fi _debug "_sub_domain" "$_sub_domain" - _debug "_domain" "$_domain" + _debug "_domain" "$_domain" _info "Adding record" - if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then - return 1 + if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then + return 1 fi } @@ -80,7 +80,7 @@ dns_euserv_rm() { _debug "_domain" "$_domain" _debug "Getting txt records" - + xml_content=$(printf ' domain.dns_get_active_records @@ -111,7 +111,7 @@ dns_euserv_rm() { ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" @@ -126,9 +126,9 @@ dns_euserv_rm() { _info "Do not need to delete record" else # find XML block where txtvalue is in. The record_id is allways prior this line! - _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) # record_id is the last Tag with a number before the row _endLine, identified by - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -168,7 +168,7 @@ _get_root() { if ! _euserv_get_domain_id "$_domain"; then _err "invalid domain" return 1 - fi + fi return 0 fi p=$i @@ -343,7 +343,7 @@ _euserv_add_record() { ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" From 2945b230e4dd4c03de5c48f1f65ca07fe45045fd Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 9 Jul 2018 22:54:34 +0200 Subject: [PATCH 14/14] replaced tail/head with _tail_n/_head_n and printf with echo --- dnsapi/dns_euserv.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index 44120a3..3810156 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -122,13 +122,13 @@ dns_euserv_rm() { return 1 fi - if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + 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=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) + _endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) # record_id is the last Tag with a number before the row _endLine, identified by - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') + _record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '' | _tail_n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -155,7 +155,7 @@ _get_root() { response="$_euserv_domain_orders" while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) + h=$(echo "$domain" | cut -d . -f $i-100) _debug h "$h" if [ -z "$h" ]; then #not valid @@ -163,7 +163,7 @@ _get_root() { fi if _contains "$response" "$h"; then - _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _sub_domain=$(echo "$domain" | cut -d . -f 1-$p) _domain="$h" if ! _euserv_get_domain_id "$_domain"; then _err "invalid domain" @@ -225,9 +225,9 @@ _euserv_get_domain_id() { _debug "get domain_id" # find line where the domain name is within the $response - _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1) + _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=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') + _euserv_domain_id=$(echo "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | _head_n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') if [ -z "$_euserv_domain_id" ]; then _err "Could not find domain_id for domain $domain"