diff --git a/dnsapi/dns_ispman.sh b/dnsapi/dns_ispman.sh new file mode 100644 index 00000000..21447c56 --- /dev/null +++ b/dnsapi/dns_ispman.sh @@ -0,0 +1,263 @@ +#!/usr/bin/env sh +#!!!!!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!!!!!! +#! Make sure the verson of ISPMan supports TXT records ! +#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +######################## Forward ######################## + +#This file name is "dns_ispman.sh" +#So, here must be a method dns_ispman_add() +#Which will be called by acme.sh to add the txt record to your api system. +#returns 0 means success, otherwise error. + +#Authored by Adrian Fedoreanu (dns_1984hosting.sh) +#Modified by Gary C. New (dns_ispman.sh) +#Report Bugs here: https://github.com/acmesh-official/acme.sh +# or here... https://github.com/acmesh-official/acme.sh/issues/???? + +#################### Public Functions #################### + +# Export ISPMan Username and Password in following variables +# +# export ACME_USE_WGET=1 +# export ISPMan_Username=domain +# export ISPMan_Password=passwd +# +# Domain Cookie is saved in $LE_WORKING_DIR/account.conf +# Username/Password need to be set only when changed. + +# While ISPMan does not work with acme.sh curl implementation +# Force acme.sh to use wget by exporting the following variable + +export ACME_USE_WGET=1 + +#Usage: dns_ispman_add _acme-challenge.www.domain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ispman_add() { + fulldomain=$1 + domainname="$(echo ${fulldomain#_acme-challenge.} | sed "s/\-/_HYPHEN_/g" | sed "s/\./_DOT_/g")" + txtvalue=$2 + + _info "Add TXT record using ISPMan" + _debug fulldomain "$fulldomain" + _debug domainname "$domainname" + _debug txtvalue "$txtvalue" + + # fulldomain="_acme-challenge.example-domain.tld"; domain="${domain:-$(echo domain_${fulldomain#_acme-challenge.}_name)}"; echo "$domain" + ISPMan_Username="${ISPMan_Username:-$(_readaccountconf_mutable ISPMan_${domainname}_Username)}" + ISPMan_Password="${ISPMan_Password:-$(_readaccountconf_mutable ISPMan_${domainname}_Password)}" + + _debug2 ISPMan_Username "$ISPMan_Username" + _debug2 "ISPMan_${domainname}_Username" "${ISPMan_Username:-$(_readaccountconf_mutable ISPMan_${domainname}_Username)}" + + if ! _ispman_login; then + _err "ISPMan login failed for user $ISPMan_Username. Check $HTTP_HEADER file" + return 1 + fi + + _saveaccountconf_mutable ISPMan_${domainname}_Username "$ISPMan_Username" + _saveaccountconf_mutable ISPMan_${domainname}_Password "$ISPMan_Password" + + chmod 600 "$LE_WORKING_DIR/account.conf" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" "$fulldomain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Add TXT record $fulldomain with value '$txtvalue'" + host="$(printf '%s' "$fulldomain" | _url_encode)" + text="$(printf '%s' "$txtvalue" | _url_encode)" + url="http://192.168.0.49/secure/ispman/control_panel/index.cgi" + url="$url?mode=addDNSRecord&type=txt&host=$host&text=$text" + _debug2 url "$url" + + #_authpost "$postdata" "$url" + _authget "$url" + response="$(echo "$_response" | _normalizeJson)" + _debug2 response "$response" + + if _contains "$response" "Unauthorized"; then + _err "ISPMan failed to add TXT record for $_sub_domain. Invalid or expired cookie" + return 1 + fi + + _info "Added acme challenge TXT record for $fulldomain at ISPMan" + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_ispman_rm() { + fulldomain=$1 + domainname="$(echo ${fulldomain#_acme-challenge.} | sed "s/\-/_HYPHEN_/g" | sed "s/\./_DOT_/g")" + txtvalue=$2 + + _info "Delete TXT record using ISPMan" + _debug fulldomain "$fulldomain" + _debug domainname "$domainname" + _debug txtvalue "$txtvalue" + + # fulldomain="_acme-challenge.example-domain.tld"; domain="${domain:-$(echo domain_${fulldomain#_acme-challenge.}_name)}"; echo "$domain" + ISPMan_Username="${ISPMan_Username:-$(_readaccountconf_mutable ISPMan_${domainname}_Username)}" + ISPMan_Password="${ISPMan_Password:-$(_readaccountconf_mutable ISPMan_${domainname}_Password)}" + + _debug2 ISPMan_Username "$ISPMan_Username" + _debug2 "ISPMan_${domainname}_Username" "${ISPMan_Username:-$(_readaccountconf_mutable ISPMan_${domainname}_Username)}" + + if ! _ispman_login; then + _err "ISPMan login failed for user $ISPMan_Username. Check $HTTP_HEADER file" + return 1 + fi + + _saveaccountconf_mutable ISPMan_${domainname}_Username "$ISPMan_Username" + _saveaccountconf_mutable ISPMan_${domainname}_Password "$ISPMan_Password" + + chmod 600 "$LE_WORKING_DIR/account.conf" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" "$fulldomain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + _debug "Delete $fulldomain TXT record" + + host="$(printf '%s' "$fulldomain" | _url_encode)" + text="$(printf '%s' "$txtvalue" | _url_encode)" + url="http://192.168.0.49/secure/ispman/control_panel/index.cgi" + url="$url?mode=deleteDNSRecord&type=txt&host=$host&text=$text" + _debug2 url "$url" + + _authget "$url" + response="$(echo "$_response" | _normalizeJson)" + _debug2 response "$response" + + if _contains "$response" "Unauthorized"; then + _err "ISPMan failed to delete TXT record for $_sub_domain. Invalid or expired cookie" + return 1 + fi + + _info "Deleted acme challenge TXT record for $fulldomain at ISPMan" + return 0 +} + +#################### Private Functions #################### + +# usage: _ispman_login username password +# returns 0 success +_ispman_login() { + if ! _check_credentials; then return 1; fi + + if _check_cookies; then + _debug "Already logged in" + return 0 + fi + + _debug "Login to ISPMan as user $ISPMan_Username" + username=$(printf '%s' "$ISPMan_Username" | _url_encode) + password=$(printf '%s' "$ISPMan_Password" | _url_encode) + url="http://192.168.0.49/secure/ispman/control_panel/index.cgi?mode=index" + + # _post(body url [needbase64] [POST|PUT|DELETE] [ContentType]) + #response="$(_post "domain=$username&pass=$password" $url "" "POST" "application/x-www-form-urlencoded")" + response="$(_post "domain=$username&pass=$password" $url)" + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + + if _contains "$response" "$username"; then + # Set-Cookie: domain=domain.tld; path=/; expires=1h + ISPMan_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'domain=[^;]*;' | tr -d ';')" + export ISPMan_COOKIE + _saveaccountconf_mutable ISPMan_${domainname}_COOKIE "$ISPMan_COOKIE" + return 0 + fi + return 1 +} + +_check_credentials() { + if [ -z "$ISPMan_Username" ] || [ -z "$ISPMan_Password" ]; then + ISPMan_Username="" + ISPMan_Password="" + _err "You haven't specified ISPMan Username or Password yet." + _err "Please export as ISPMan_Username / ISPMan_Password and try again." + return 1 + fi + return 0 +} + +_check_cookies() { + ISPMan_COOKIE="${ISPMan_COOKIE:-$(_readaccountconf_mutable ISPMan_${domainname}_COOKIE)}" + if [ -z "$ISPMan_COOKIE" ]; then + _debug "No cached cookie(s) found" + return 1 + fi + + _authget "http://192.168.0.49/secure/ispman/control_panel/index.cgi" + if _contains "$response" "$username"; then + _debug "Cached cookies still valid" + return 0 + fi + + _debug "Cached cookies no longer valid" + ISPMan_COOKIE="" + _saveaccountconf_mutable ISPMan_${domainname}_COOKIE "$ISPMan_COOKIE" + return 1 +} + +#_acme-challenge.www.domain.tld +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.tld +_get_root() { + domain="$1" + i=1 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 h "$h" + + if [ -z "$h" ]; then + #not valid + return 1 + fi + + # The ISPMan Customer Control Panel doesn't allow access to the SOA Record + # The ISPMan_Username is the Domain Root for the Customer Control Panel + if _startswith "$h" "$ISPMan_Username"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +# add extra headers to request +_authget() { + export _H1="Cookie: $ISPMan_COOKIE" + _response=$(_get "$1" | _normalizeJson) + _debug2 _response "$_response" +} + +# truncate huge HTML response +# echo: Argument list too long +_htmlget() { + export _H1="Cookie: $ISPMan_COOKIE" + _response=$(_get "$1" | grep "$2") + if _contains "$_response" "@$2"; then + _response=$(echo "$_response" | grep -v "[@]" | _head_n 1) + fi +} + +# add extra headers to request +_authpost() { + export _H1="Cookie: $ISPMan_COOKIE" + _response=$(_post "$1" $2 | _normalizeJson) + _debug2 _response "$_response" +}