diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 56ca9afd..e0921bcb 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(tr '\n\r' '@#' <"$_cfullchain" | sed 's/@/\\n/g;s/#/\\r/g') - _key=$(tr '\n\r' '@#' <"$_ckey" | sed 's/@/\\n/g;s/#/\\r/g') + _fullchain=$(tr '\r\n' '*#' <"$_cfullchain" | sed 's/*#/#/g;s/##/#/g;s/#/\\n/g') + _key=$(tr '\r\n' '*#' <"$_ckey" | sed 's/*#/#/g;s/#/\\n/g') _debug _fullchain "$_fullchain" _debug _key "$_key" diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 5c1a40e2..2479aebd 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -1,8 +1,41 @@ #!/usr/bin/env sh -#Here is a script to deploy cert to haproxy server. - -#returns 0 means success, otherwise error. +# Script for acme.sh to deploy certificates to haproxy +# +# The following variables can be exported: +# +# export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem" +# +# Defines the name of the PEM file. +# Defaults to ".pem" +# +# export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy" +# +# Defines location of PEM file for HAProxy. +# Defaults to /etc/haproxy +# +# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" +# +# OPTIONAL: Reload command used post deploy +# This defaults to be a no-op (ie "true"). +# It is strongly recommended to set this something that makes sense +# for your distro. +# +# export DEPLOY_HAPROXY_ISSUER="no" +# +# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer" +# Note: Required for OCSP stapling to work +# +# export DEPLOY_HAPROXY_BUNDLE="no" +# +# OPTIONAL: Deploy this certificate as part of a multi-cert bundle +# This adds a suffix to the certificate based on the certificate type +# eg RSA certificates will have .rsa as a suffix to the file name +# HAProxy will load all certificates and provide one or the other +# depending on client capabilities +# Note: This functionality requires HAProxy was compiled against +# a version of OpenSSL that supports this. +# ######## Public functions ##################### @@ -14,45 +47,226 @@ haproxy_deploy() { _cca="$4" _cfullchain="$5" - _debug _cdomain "$_cdomain" - _debug _ckey "$_ckey" - _debug _ccert "$_ccert" - _debug _cca "$_cca" - _debug _cfullchain "$_cfullchain" - - # 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" + # Some defaults + DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy" + DEPLOY_HAPROXY_PEM_NAME_DEFAULT="${_cdomain}.pem" + DEPLOY_HAPROXY_BUNDLE_DEFAULT="no" + DEPLOY_HAPROXY_ISSUER_DEFAULT="no" + DEPLOY_HAPROXY_RELOAD_DEFAULT="true" + + if [ -f "${DOMAIN_CONF}" ]; then + # shellcheck disable=SC1090 + . "${DOMAIN_CONF}" + fi + + _debug _cdomain "${_cdomain}" + _debug _ckey "${_ckey}" + _debug _ccert "${_ccert}" + _debug _cca "${_cca}" + _debug _cfullchain "${_cfullchain}" + + # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then + Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}" + _savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}" + elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then + Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" 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." + # Ensure PEM_PATH exists + if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then + _debug "PEM_PATH ${Le_Deploy_haproxy_pem_path} exists" + else + _err "PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist" 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" + # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then + Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}" + _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}" + elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then + Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" + fi + + # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then + Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}" + _savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}" + elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then + Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" + fi - # restart HAProxy - _info "Run reload: $_reload" - if eval "$_reload"; then - _info "Reload success!" - return 0 + # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then + Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}" + _savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}" + elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then + Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}" + fi + + # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then + Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}" + _savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}" + elif [ -z "${Le_Deploy_haproxy_reload}" ]; then + Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}" + fi + + # Set the suffix depending if we are creating a bundle or not + if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then + _info "Bundle creation requested" + # Initialise $Le_Keylength if its not already set + if [ -z "${Le_Keylength}" ]; then + Le_Keylength="" + fi + if _isEccKey "${Le_Keylength}"; then + _info "ECC key type detected" + _suffix=".ecdsa" + else + _info "RSA key type detected" + _suffix=".rsa" + fi else - _err "Reload error" - return 1 + _suffix="" + fi + _debug _suffix "${_suffix}" + + # Set variables for later + _pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}" + _issuer="${_pem}.issuer" + _ocsp="${_pem}.ocsp" + _reload="${Le_Deploy_haproxy_reload}" + + _info "Deploying PEM file" + # Create a temporary PEM file + _temppem="$(_mktemp)" + _debug _temppem "${_temppem}" + cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}" + _ret="$?" + + # Check that we could create the temporary file + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned during PEM file creation" + [ -f "${_temppem}" ] && rm -f "${_temppem}" + return ${_ret} + fi + + # Move PEM file into place + _info "Moving new certificate into place" + _debug _pem "${_pem}" + cat "${_temppem}" >"${_pem}" + _ret=$? + + # Clean up temp file + [ -f "${_temppem}" ] && rm -f "${_temppem}" + + # Deal with any failure of moving PEM file into place + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned while moving new certificate into place" + return ${_ret} + fi + + # Update .issuer file if requested + if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then + _info "Updating .issuer file" + _debug _issuer "${_issuer}" + cat "${_cca}" >"${_issuer}" + _ret="$?" + + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned while copying issuer/CA certificate into place" + return ${_ret} + fi + else + [ -f "${_issuer}" ] _err "Issuer file update not requested but .issuer file exists" + fi + + # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option + if [ -z "${Le_OCSP_Staple}" ]; then + Le_OCSP_Staple="0" + fi + if [ "${Le_OCSP_Staple}" = "1" ]; then + _info "Updating OCSP stapling info" + _debug _ocsp "${_ocsp}" + _info "Extracting OCSP URL" + _ocsp_url=$(openssl x509 -noout -ocsp_uri -in "${_pem}") + _debug _ocsp_url "${_ocsp_url}" + + # Only process OCSP if URL was present + if [ "${_ocsp_url}" != "" ]; then + # Extract the hostname from the OCSP URL + _info "Extracting OCSP URL" + _ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3) + _debug _ocsp_host "${_ocsp_host}" + + # Only process the certificate if we have a .issuer file + if [ -r "${_issuer}" ]; then + # Check if issuer cert is also a root CA cert + _subjectdn=$(openssl x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) + _debug _subjectdn "${_subjectdn}" + _issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) + _debug _issuerdn "${_issuerdn}" + _info "Requesting OCSP response" + # Request the OCSP response from the issuer and store it + if [ "${_subjectdn}" = "${_issuerdn}" ]; then + # If the issuer is a CA cert then our command line has "-CAfile" added + openssl ocsp \ + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce \ + -CAfile "${_issuer}" \ + | grep -q "${_pem}: good" + _ret=$? + else + # Issuer is not a root CA so no "-CAfile" option + openssl ocsp \ + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce \ + | grep -q "${_pem}: good" + _ret=$? + fi + else + # Non fatal: No issuer file was present so no OCSP stapling file created + _err "OCSP stapling in use but no .issuer file was present" + fi + else + # Non fatal: No OCSP url was found int the certificate + _err "OCSP update requested but no OCSP URL was found in certificate" + fi + + # Non fatal: Check return code of openssl command + if [ "${_ret}" != "0" ]; then + _err "Updating OCSP stapling failed with return code ${_ret}" + fi + else + # An OCSP file was already present but certificate did not have OCSP extension + if [ -f "${_ocsp}" ]; then + _err "OCSP was not requested but .ocsp file exists." + # Could remove the file at this step, although HAProxy just ignores it in this case + # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" + fi + fi + + # Reload HAProxy + _debug _reload "${_reload}" + eval "${_reload}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} during reload" + return ${_ret} + else + _info "Reload successful" fi + return 0 } diff --git a/dnsapi/README.md b/dnsapi/README.md index 410dbf6e..4fa59cf2 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1,4 +1,6 @@ # How to use DNS API DNS api usage: -https://github.com/Neilpang/acme.sh/wiki/dnsapi \ No newline at end of file + +https://github.com/Neilpang/acme.sh/wiki/dnsapi + diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index dfe6dcb7..903b9619 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -119,7 +119,7 @@ _ddnss_rest() { # DDNSS uses GET to update domain info if [ "$method" = "GET" ]; then - response="$(_get "$url" | sed 's/<[^>]*>//g;/]*>//g' | _tail_n 1)" else _err "Unsupported method" return 1 diff --git a/dnsapi/dns_nsd.sh b/dnsapi/dns_nsd.sh new file mode 100644 index 00000000..83cc4cac --- /dev/null +++ b/dnsapi/dns_nsd.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env sh + +#Nsd_ZoneFile="/etc/nsd/zones/example.com.zone" +#Nsd_Command="sudo nsd-control reload" + +# args: fulldomain txtvalue +dns_nsd_add() { + fulldomain=$1 + txtvalue=$2 + ttlvalue=300 + + Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}" + Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}" + + # Arg checks + if [ -z "$Nsd_ZoneFile" ] || [ -z "$Nsd_Command" ]; then + Nsd_ZoneFile="" + Nsd_Command="" + _err "Specify ENV vars Nsd_ZoneFile and Nsd_Command" + return 1 + fi + + if [ ! -f "$Nsd_ZoneFile" ]; then + Nsd_ZoneFile="" + Nsd_Command="" + _err "No such file: $Nsd_ZoneFile" + return 1 + fi + + _savedomainconf Nsd_ZoneFile "$Nsd_ZoneFile" + _savedomainconf Nsd_Command "$Nsd_Command" + + echo "$fulldomain. $ttlvalue IN TXT \"$txtvalue\"" >>"$Nsd_ZoneFile" + _info "Added TXT record for $fulldomain" + _debug "Running $Nsd_Command" + if eval "$Nsd_Command"; then + _info "Successfully updated the zone" + return 0 + else + _err "Problem updating the zone" + return 1 + fi +} + +# args: fulldomain txtvalue +dns_nsd_rm() { + fulldomain=$1 + txtvalue=$2 + ttlvalue=300 + + Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}" + Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}" + + sed -i "/$fulldomain. $ttlvalue IN TXT \"$txtvalue\"/d" "$Nsd_ZoneFile" + _info "Removed TXT record for $fulldomain" + _debug "Running $Nsd_Command" + if eval "$Nsd_Command"; then + _info "Successfully reloaded NSD " + return 0 + else + _err "Problem reloading NSD" + return 1 + fi +} diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh new file mode 100644 index 00000000..c99c9c97 --- /dev/null +++ b/dnsapi/dns_one.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# one.com ui wrapper for acme.sh +# Author: github: @diseq +# Created: 2019-02-17 +# +# export ONECOM_USER="username" +# export ONECOM_PASSWORD="password" +# +# Usage: +# acme.sh --issue --dns dns_one -d example.com +# +# only single domain supported atm + +dns_one_add() { + mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + txtvalue=$2 + + # get credentials + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" + _err "You didn't specify a one.com username and password yet." + _err "Please create the key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" + _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" + + # Login with user and password + postdata="loginDomain=true" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&targetDomain=$mydomain" + postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&loginTarget=" + #_debug postdata "$postdata" + + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" + #_debug response "$response" + + JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + _debug jsessionid "$JSESSIONID" + + export _H1="Cookie: ${JSESSIONID}" + + # get entries + response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" + _debug response "$response" + + CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + export _H2="Cookie: ${CSRF_G_TOKEN}" + + # Update the IP address for domain entry + postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}" + _debug postdata "$postdata" + response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records" "" "POST" "application/json")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + id=$(printf -- "%s" "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") + + if [ -z "$id" ]; then + _err "Add txt record error." + return 1 + else + _info "Added, OK ($id)" + return 0 + fi + +} + +dns_one_rm() { + mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + txtvalue=$2 + + # get credentials + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" + _err "You didn't specify a one.com username and password yet." + _err "Please create the key and try again." + return 1 + fi + + # Login with user and password + postdata="loginDomain=true" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&targetDomain=$mydomain" + postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&loginTarget=" + + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" + #_debug response "$response" + + JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + _debug jsessionid "$JSESSIONID" + + export _H1="Cookie: ${JSESSIONID}" + + # get entries + response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + export _H2="Cookie: ${CSRF_G_TOKEN}" + + id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p") + + if [ -z "$id" ]; then + _err "Txt record not found." + return 1 + fi + + # delete entry + response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + if [ "$response" = '{"result":null,"metadata":null}' ]; then + _info "Removed, OK" + return 0 + else + _err "Removing txt record error." + return 1 + fi + +} diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh new file mode 100644 index 00000000..399c50e0 --- /dev/null +++ b/dnsapi/dns_schlundtech.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# Schlundtech DNS API +# Author: mod242 +# Created: 2019-40-29 +# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com +# +# export SCHLUNDTECH_USER="username" +# export SCHLUNDTECH_PASSWORD="password" +# +# Usage: +# acme.sh --issue --dns dns_schlundtech -d example.com + +SCHLUNDTECH_API="https://gateway.schlundtech.de" + +# Arguments: +# txtdomain +# txt +dns_schlundtech_add() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + return 1 + fi + + _saveaccountconf_mutable SCHLUNDTECH_USER "$SCHLUNDTECH_USER" + _saveaccountconf_mutable SCHLUNDTECH_PASSWORD "$SCHLUNDTECH_PASSWORD" + + _debug "First detect the root zone" + + if ! _get_autodns_zone "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _zone "$_zone" + _debug _system_ns "$_system_ns" + + _info "Adding TXT record" + + autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" + + if [ "$?" -eq "0" ]; then + _info "Added, OK" + return 0 + fi + + return 1 +} + +# Arguments: +# txtdomain +# txt +dns_schlundtech_rm() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + return 1 + fi + + _debug "First detect the root zone" + + if ! _get_autodns_zone "$fulldomain"; then + _err "zone not found" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _zone "$_zone" + _debug _system_ns "$_system_ns" + + _info "Delete TXT record" + + autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" + + if [ "$?" -eq "0" ]; then + _info "Deleted, OK" + return 0 + fi + + return 1 +} + +#################### Private functions below ################################## + +# Arguments: +# fulldomain +# Returns: +# _sub_domain=_acme-challenge.www +# _zone=domain.com +# _system_ns +_get_autodns_zone() { + domain="$1" + + 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 + + autodns_response="$(_autodns_zone_inquire "$h")" + + if [ "$?" -ne "0" ]; then + _err "invalid domain" + return 1 + fi + + if _contains "$autodns_response" "1" >/dev/null; then + _zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +_build_request_auth_xml() { + printf " + %s + %s + 10 + " "$SCHLUNDTECH_USER" "$SCHLUNDTECH_PASSWORD" +} + +# Arguments: +# zone +_build_zone_inquire_xml() { + printf " + + %s + + 0205 + + 1 + 1 + + + name + eq + %s + + + " "$(_build_request_auth_xml)" "$1" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_build_zone_update_xml() { + printf " + + %s + + 0202001 + + + %s + 600 + TXT + %s + + + + %s + %s + + + " "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4" +} + +# Arguments: +# zone +_autodns_zone_inquire() { + request_data="$(_build_zone_inquire_xml "$1")" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_autodns_zone_update() { + request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_autodns_zone_cleanup() { + request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" + # replace 'rr_add>' with 'rr_rem>' in request_data + request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# request_data +_autodns_api_call() { + request_data="$1" + + _debug request_data "$request_data" + + autodns_response="$(_post "$request_data" "$SCHLUNDTECH_API")" + ret="$?" + + _debug autodns_response "$autodns_response" + + if [ "$ret" -ne "0" ]; then + _err "error" + return 1 + fi + + if _contains "$autodns_response" "success" >/dev/null; then + _info "success" + printf "%s" "$autodns_response" + return 0 + fi + + return 1 +}