#!/usr/bin/env sh # This script has been created at June 2020, based on knowledge base of wedos.com provider. # It is intended to allow DNS-01 challenges for acme.sh using wedos's WAPI using XML. # Author Michal Tuma # For issues send me an email WEDOS_WAPI_ENDPOINT="https://api.wedos.com/wapi/xml" TESTING_STAGE= ######## Public functions ##################### #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_wedos_add() { fulldomain=$1 txtvalue=$2 WEDOS_Username="${WEDOS_Username:-$(_readaccountconf_mutable WEDOS_Username)}" WEDOS_Wapipass="${WEDOS_Wapipass:-$(_readaccountconf_mutable WEDOS_Wapipass)}" WEDOS_Authtoken="${WEDOS_Authtoken:-$(_readaccountconf_mutable WEDOS_Authtoken)}" if [ "${WEDOS_Authtoken}" ]; then _debug "WEDOS Authtoken was already saved, using saved one" _saveaccountconf_mutable WEDOS_Authtoken "${WEDOS_Authtoken}" else if [ -z "${WEDOS_Username}" ] || [ -z "${WEDOS_Wapipass}" ]; then WEDOS_Username="" WEDOS_Wapipass="" _err "You didn't specify a WEDOS's username and wapi key yet." _err "Please type: export WEDOS_Username=" _err "And: export WEDOS_Wapipass=" _err "After you export those variables, run the script again, the values will be saved for future" return 1 fi #build WEDOS_Authtoken _debug "WEDOS Authtoken were not saved yet, building" WEDOS_Authtoken=$(printf '%s' "${WEDOS_Wapipass}" | sha1sum | head -c 40) _debug "WEDOS_Authtoken step 1, WAPI PASS sha1 sum: '${WEDOS_Authtoken}'" WEDOS_Authtoken="${WEDOS_Username}${WEDOS_Authtoken}" _debug "WEDOS_Authtoken step 2, username concat with token without hours: '${WEDOS_Authtoken}'" #save details _saveaccountconf_mutable WEDOS_Username "${WEDOS_Username}" _saveaccountconf_mutable WEDOS_Wapipass "${WEDOS_Wapipass}" _saveaccountconf_mutable WEDOS_Authtoken "${WEDOS_Authtoken}" fi if ! _get_root "${fulldomain}"; then _err "WEDOS Account do not contain primary domain to fullfill add of ${fulldomain}!" return 1 fi _debug _sub_domain "${_sub_domain}" _debug _domain "${_domain}" if _wapi_row_add "${_domain}" "${_sub_domain}" "${txtvalue}" "300"; then _info "WEDOS WAPI: dns record added and dns changes were commited" return 0 else _err "FAILED TO ADD DNS RECORD OR COMMIT DNS CHANGES" return 1 fi } #fulldomain txtvalue dns_wedos_rm() { fulldomain=$1 txtvalue=$2 WEDOS_Username="${WEDOS_Username:-$(_readaccountconf_mutable WEDOS_Username)}" WEDOS_Wapipass="${WEDOS_Wapipass:-$(_readaccountconf_mutable WEDOS_Wapipass)}" WEDOS_Authtoken="${WEDOS_Authtoken:-$(_readaccountconf_mutable WEDOS_Authtoken)}" if [ "${WEDOS_Authtoken}" ]; then _debug "WEDOS Authtoken was already saved, using saved one" _saveaccountconf_mutable WEDOS_Authtoken "${WEDOS_Authtoken}" else if [ -z "${WEDOS_Username}" ] || [ -z "${WEDOS_Wapipass}" ]; then WEDOS_Username="" WEDOS_Wapipass="" _err "You didn't specify a WEDOS's username and wapi key yet." _err "Please type: export WEDOS_Username=" _err "And: export WEDOS_Wapipass=" _err "After you export those variables, run the script again, the values will be saved for future" return 1 fi #build WEDOS_Authtoken _debug "WEDOS Authtoken were not saved yet, building" WEDOS_Authtoken=$(printf '%s' "${WEDOS_Wapipass}" | sha1sum | head -c 40) _debug "WEDOS_Authtoken step 1, WAPI PASS sha1 sum: '${WEDOS_Authtoken}'" WEDOS_Authtoken="${WEDOS_Username}${WEDOS_Authtoken}" _debug "WEDOS_Authtoken step 2, username concat with token without hours: '${WEDOS_Authtoken}'" #save details _saveaccountconf_mutable WEDOS_Username "${WEDOS_Username}" _saveaccountconf_mutable WEDOS_Wapipass "${WEDOS_Wapipass}" _saveaccountconf_mutable WEDOS_Authtoken "${WEDOS_Authtoken}" fi if ! _get_root "${fulldomain}"; then _err "WEDOS Account do not contain primary domain to fullfill add of ${fulldomain}!" return 1 fi _debug _sub_domain "${_sub_domain}" _debug _domain "${_domain}" if _wapi_find_row "${_domain}" "${_sub_domain}" "${txtvalue}"; then _info "WEDOS WAPI: dns record found with id '${_row_id}'" if _wapi_delete_row "${_domain}" "${_row_id}"; then _info "WEDOS WAPI: dns row were deleted and changes commited!" return 0 fi fi _err "Requested dns row were not found or was imposible to delete it, do it manually" _err "Delete: ${fulldomain}" _err "Value: ${txtvalue}" return 1 } #################### Private functions below ################################## # Function _wapi_post(), only takes data, prepares auth token and provide result # $1 - WAPI command string, like 'dns-domains-list' # $2 - WAPI data for given command, is not required # returns WAPI response if request were successfully delivered to WAPI endpoint _wapi_post() { command=$1 data=$2 _debug "Command : ${command}" _debug "Data : ${data}" if [ -z "${command}" ]; then _err "No command were provided, implamantation error!" return 1 fi # Prepare authentification token hour=$(date +%H) token=$(printf '%s' "${WEDOS_Authtoken}${hour}" | sha1sum | head -c 40) _debug "Authentification token is '${token}'" # Build xml request request="request=\ \ ${WEDOS_Username}\ ${token}\ ${command}" if [ -z "${data}" ]; then echo "" 1>/dev/null else request="${request}${data}" fi if [ -z "$TESTING_STAGE" ]; then echo "" 1>/dev/null else request="${request}\ 1" fi request="${request}\ " _debug "Request to WAPI is: ${request}" if ! response="$(_post "${request}" "$WEDOS_WAPI_ENDPOINT")"; then _err "Error contacting WEDOS WAPI with command ${command}" return 1 fi _debug "Response : ${response}" echo "${response}" | grep "1000" 1>/dev/null 2>/dev/null return "$?" } # _get_root() function, for provided full domain, like _acme_challenge.www.example.com verify if WEDOS contains a primary active domain and found what is subdomain # $1 - full domain to verify, ie _acme_challenge.www.example.com # build ${_domain} found at WEDOS, like example.com and ${_sub_domain} from provided full domain, like _acme_challenge.www _get_root() { domain=$1 if [ -z "${domain}" ]; then _err "Function _get_root was called without argument, implementation error!" return 1 fi _debug "Get root for domain: ${domain}" _debug "Getting list of domains using WAPI ..." if ! _wapi_post "dns-domains-list"; then _err "Error on WAPI request for list of domains, response : ${response}" return 1 else _debug "DNS list were successfully retrieved, response : ${response}" fi for xml_domain in $(echo "${response}" | tr -d '\012\015' | grep -o -E "( )*.*( )*primary( )*active" | grep -o -E ".*"); do _debug "Active and primary XML DOMAIN found: ${xml_domain}" end_of_name=$((${#xml_domain} - 7)) xml_domain_name=$(echo "${xml_domain}" | cut -c 7-${end_of_name}) _debug "Found primary active domain: ${xml_domain_name}" regex=".*\\."$(echo "${xml_domain_name}" | sed 's/\./\\./g') _debug "Regex for matching domain: '${regex}'" if ! echo "${domain}" | grep -E "${regex}" 1>/dev/null 2>/dev/null; then _debug "found domain do not match required" else end_of_name=$((${#domain} - ${#xml_domain_name} - 1)) _domain=${xml_domain_name} _sub_domain=$(echo "${domain}" | cut -c -${end_of_name}) _info "Domain '${_domain}' was found at WEDOS account as primary, and subdomain is '${_sub_domain}'!" return 0 fi done return 1 } # for provided domain, it commites all performed changes _wapi_dns_commit() { domain=$1 if [ -z "${domain}" ]; then _err "Invalid request to commit dns changes, domain is empty, implementation error!" return 1 fi data=" \ ${domain}\ " if ! _wapi_post "dns-domain-commit" "${data}"; then _err "Error on WAPI request to commit DNS changes, response : ${response}" _err "PLEASE USE WEB ACCESS TO CHECK IF CHANGES ARE REQUIRED TO COMMIT OR ROLLBACKED IMMEDIATELLY!" return 1 else _debug "DNS CHANGES COMMITED, response : ${response}" _info "WEDOS DNS WAPI: Changes were commited to domain '${domain}'" fi return 0 } # add one TXT dns row to a specified fomain _wapi_row_add() { domain=$1 sub_domain=$2 value=$3 ttl=$4 if [ -z "${domain}" ] || [ -z "${sub_domain}" ] || [ -z "${value}" ] || [ -z "${ttl}" ]; then _err "Invalid request to add record, domain: '${domain}', sub_domain: '${sub_domain}', value: '${value}' and ttl: '${ttl}', on of required input were not provided, implementation error!" return 1 fi # Prepare data for request to WAPI data=" \ ${domain}\ ${sub_domain}\ ${ttl}\ TXT\ ${value}\ Created using WAPI from acme.sh\ " _debug "Adding row using WAPI ..." if ! _wapi_post "dns-row-add" "${data}"; then _err "Error on WAPI request to add new TXT row, response : ${response}" return 1 else _debug "ROW ADDED, response : ${response}" _info "WEDOS DNS WAPI: Row to domain '${domain}' with name '${sub_domain}' were successfully added with value '${value}' and ttl set to ${ttl}" fi # Now we have to commit _wapi_dns_commit "${domain}" return "$?" } _wapi_find_row() { domain=$1 sub_domain=$2 value=$3 if [ -z "${domain}" ] || [ -z "${sub_domain}" ] || [ -z "${value}" ]; then _err "Invalud request to finad a row, domain: '${domain}', sub_domain: '${sub_domain}' and value: '${value}', one of required input were not provided, implementation error!" return 1 fi data=" \ ${domain}\ " _debug "Searching rows using WAPI ..." if ! _wapi_post "dns-rows-list" "${data}"; then _err "Error on WAPI request to list domain rows, response : ${response}" return 1 fi _debug "Domain rows found, response : ${response}" sub_domain_regex=$(echo "${sub_domain}" | sed "s/\./\\\\./g") _debug "Subdomain regex '${sub_domain_regex}'" for xml_row in $(echo "${response}" | tr -d '\012\015' | grep -o -E "( )*[0-9]*( )*${sub_domain_regex}( )*[0-9]*( )*TXT( )*${value}" | grep -o -e "[0-9]*"); do _debug "Found row in DNS with ID : ${xml_row}" _row_id=$(echo "${xml_row}" | grep -o -E "[0-9]*") _info "WEDOS API: Found DNS row id ${_row_id} for domain ${domain}" return 0 done _info "WEDOS API: No TXT row found for domain '${domain}' with name '${sub_domain}' and value '${value}'" return 1 } _wapi_delete_row() { domain=$1 row_id=$2 if [ -z "${domain}" ] || [ -z "${row_id}" ]; then _err "Invalid request to delete domain dns row, domain: '${domain}' and row_id: '${row_id}', one of required input were not provided, implementation error!" return 1 fi data=" \ ${domain} ${row_id} " _debug "Deleting dns row using WAPI ..." if ! _wapi_post "dns-row-delete" "${data}"; then _err "Error on WAPI request to delete dns row, response: ${response}" return 1 fi _debug "DNS row were deleted, response: ${response}" _info "WEDOS API: Required dns domain row with row_id '${row_id}' were correctly deleted at domain '${domain}'" # Now we have to commit changes _wapi_dns_commit "${domain}" return "$?" }