From e3980ee345224ddb6899d83490a534303dd04769 Mon Sep 17 00:00:00 2001 From: smythe811 Date: Tue, 31 Oct 2017 16:03:10 -0400 Subject: [PATCH] Create dns_cpanel.sh New DNS method used for DNS-01 verify requests against cPanel controlled domains. --- dnsapi/dns_cpanel.sh | 281 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 dnsapi/dns_cpanel.sh diff --git a/dnsapi/dns_cpanel.sh b/dnsapi/dns_cpanel.sh new file mode 100644 index 00000000..c4ead3fa --- /dev/null +++ b/dnsapi/dns_cpanel.sh @@ -0,0 +1,281 @@ +#!/usr/bin/env bash + +#This is a dns hook for cpanel +#This file name is "dns_cpanel.sh" +#This hook is compatible with cpdyndns from https://forums.cpanel.net/threads/can-cpanel-update-dynamic-ip-information-to-dns-records.261951/ +# cpdyndns is not required, but this was designed to update a domain on a dd-wrt router to a cpanel hosted public domain. It may work elsewhere +# test and use at your own peril. +#returns 0 means success, otherwise error. +# +#Author: smithec +#Report Bugs here: https://github.com/Neilpang/acme.sh +# +#Tested on DD-WRT, Linux Mint 18 +# +# This is released without ANY warranty or guarantee of use. USE THIS AT YOUR OWN RISK. +# Your use of this API signifies an agreement to hold blameless the developers for any results or damages that may occur to you or to others. +# Always backup your data files and cPanel zones prior to using any tool that you allow to make edits. + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_cpanel_add() { + fulldomain="$1" + txtvalue="$2" + _info "Using cPanel add" + _debug fulldomain: "$fulldomain" + _debug txtvalue: "$txtvalue" + _get_root + _setup_vars + _setup_timeout + _load_config + _check_config + _generate_auth_string + + REQUEST="GET /xml-api/cpanel?cpanel_xmlapi_module=ZoneEdit&cpanel_xmlapi_func=add_zone_record&cpanel_xmlapi_apiversion=2&domain=$_domain&name=$_sub_domain&type=TXT&txtdata=$txtvalue&ttl=300 HTTP/1.0\r\nConnection: close\r\nAuthorization: Basic $AUTH_STRING\r\nUser-Agent: $USERAGENT $VERSION\r\n\r\n\r\n" + RESULT=`echo -e "$REQUEST" | openssl s_client -quiet -connect $CPANEL_SERVER:2083 2>&1` + _check_results_for_error "$RESULT" "$REQUEST" + _terminate + return 1 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_cpanel_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using cpanel rm" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _get_root + _setup_vars + _setup_timeout + _load_config + _check_config + _generate_auth_string + + _retreive_zone + _parse_zone_lines +} + +#################### Private functions below ################################## + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { +_debug "IN _get_root()" + domain="$fulldomain" + i=3 + p=2 + _domain=$(printf "$domain" | cut -d . -f $i-100) + _sub_domain=$(printf "$domain" | cut -d . -f 1-$p) + _debug domain "$_domain" + _debug subdomain "$_sub_domain" +_debug "OUT _get_root()" +} + +# This loads a pre-existing config file from cpdyndns +# your config file should be located at ~/etc/cpdyndns.conf +# a proper file will contain: +#CONTACT_EMAIL="my_email_here@cpanel.net" +#CPANEL_SERVER="my_server_here.cpanel.net" +#DOMAIN="my_domain_here.tld" +#SUBDOMAIN="my_subdomain_here" +#CPANEL_USER="my_username_here" +#CPANEL_PASS="my_password_here" +_load_config () +{ + if [ -e "/etc/$BASEDIR.conf" ]; then + chmod 0600 /etc/$BASEDIR.conf + . /etc/$BASEDIR.conf + _debug "== /etc/$BASEDIR.conf is being used for configuration" + else + _debug "== /etc/$BASEDIR.conf does not exist" + fi + if [ -e "$HOMEDIR/etc/$BASEDIR.conf" ]; then + chmod 0600 $HOMEDIR/etc/$BASEDIR.conf + . $HOMEDIR/etc/$BASEDIR.conf + _debug "== $HOMEDIR/etc/$BASEDIR.conf is being used for configuration" + else + _debug "== $HOMEDIR/etc/$BASEDIR.conf does not exist" + fi +} + +#Prime needed Variables +#These are meant to be compatible with cpdyndns/cpanel-dynamic-dns.sh +#You can use the CPANEL Values here, but this is designed to use a config file see _load_config() +_setup_vars () +{ + USERAGENT="acme.sh/dns_cpanel.sh" + VERSION="0.1" + APINAME="" + PARENTPID=$$ + HOMEDIR=`echo ~` + TIMEOUT="300" + BASEDIR="cpdyndns" + CPANEL_SERVER="" + CPANEL_USER="" + CPANEL_PASS="" +} + +_exit_timeout () +{ + ALARMPID="" + _err "Timeout while connecting to $LAST_CONNECT_HOST" + exit +} + +_setup_timeout () +{ + (sleep $TIMEOUT; kill -ALRM $PARENTPID) & + ALARMPID=$! + trap exit_timeout SIGALRM +} + +#Generate an Authentication String for cPanel +_generate_auth_string () { + AUTH_STRING=`echo -n "$CPANEL_USER:$CPANEL_PASS" | openssl enc -base64` +} + +#verify our configuration +_check_config () { + if [ -z "$CPANEL_SERVER" ]; then + _err "= Error: CPANEL_SERVER must be set in a configuration file" + exit + fi + if [ -z "$CPANEL_USER" ]; then + _err "= Error: CPANEL_USER must be set in a configuration file" + exit + fi + if [ -z "$CPANEL_PASS" ]; then + _err "= Error: CPANEL_PASS must be set in a configuration file" + exit + fi +} + +_terminate () { + if [ -z "$ALARMPID" ]; then + kill $ALARMPID + fi + exit +} + +_retreive_zone(){ + _info "In _retreive_zone" + _debug "matching for: TXT $_sub_domain.$_domain." + REQUEST="GET /xml-api/cpanel?cpanel_xmlapi_module=ZoneEdit&cpanel_xmlapi_func=fetchzone&cpanel_xmlapi_apiversion=2&domain=$DOMAIN HTTP/1.0\r\nConnection: close\r\nAuthorization: Basic $AUTH_STRING\r\nUser-Agent: cpanel-dynamic-dns.sh $VERSION\r\n\r\n\r\n" + RECORD="" + LINES="" + INRECORD=0 + USETHISRECORD=0 + REQUEST_RESULTS=`echo -e "$REQUEST" | openssl s_client -quiet -connect $CPANEL_SERVER:2083 2>/dev/null` + + _check_results_for_error "$REQUEST_RESULTS" "$REQUEST" + for LINE in $REQUEST_RESULTS + do + #_debug "$LINE" + if [ "$LINE" == "" ]; then + INRECORD=1 + continue + fi + if [ "$LINE" == "" ]; then + INRECORD=0 + if [ "$USETHISRECORD" == "2" ]; then + LINENUM=`echo -e "$RECORD" | grep '' | awk -F'<' '{print \$2}' | awk -F'>' '{print \$2}'` + TXT=`echo -e "$RECORD" | grep -i '' | awk -F'<' '{print \$2}' | awk -F'>' '{print \$2}'` + LINES="$LINES\n$LINENUM=$TXT" + fi + USETHISRECORD=0 + RECORD="" + continue + fi + if [ "$LINE" == "TXT" ]; then + _debug "Match TXT" + USETHISRECORD=`expr $USETHISRECORD + 1` + fi + if [ "$LINE" == "$_sub_domain.$_domain." ]; then + _debug "Match Domain" + USETHISRECORD=`expr $USETHISRECORD + 1` + fi + if [ "$INRECORD" == "1" ]; then + RECORD="$RECORD\n$LINE" + fi + done +} + +_parse_zone_lines(){ + #_info "In _parse_zone_lines" + #_debug "$LINES" + for LINE in `echo -e $LINES` + do + _debug "Removing Validation TXT" + LINENUM=`echo $LINE | awk -F= '{print $1}'` + REQUEST="GET /xml-api/cpanel?cpanel_xmlapi_module=ZoneEdit&cpanel_xmlapi_func=remove_zone_record&cpanel_xmlapi_apiversion=2&domain=$DOMAIN&line=$LINENUM HTTP/1.0\r\nConnection: close\r\nAuthorization: Basic $AUTH_STRING\r\nUser-Agent: cpanel-dynamic-dns.sh $VERSION\r\n\r\n\r\n" + RESULT=`echo -e "$REQUEST" | openssl s_client -quiet -connect $CPANEL_SERVER:2083 2>&1` + _check_results_for_error "$RESULT" "$REQUEST" + done +} + +_check_results_for_error () +{ + REQUEST_RESULTS="$1" + REQUEST="$2" + if [ "`echo $REQUEST_RESULTS | grep '1'`" ]; then + if [ "$QUIET" != "1" ]; then + echo -n "success..." + fi + else + INREASON=0 + INSTATUSMSG=0 + MSG="" + STATUSMSG="" + + for LINE in $REQUEST_RESULTS + do + if [ "`echo $LINE | grep ''`" != "" ]; then + INREASON=1 + INSTATUSMSG=0 + MSG=`echo $LINE | awk -F'>' '{print \$2}'` + continue + fi + if [ "`echo $LINE | grep ''`" != "" ]; then + INREASON=0 + MSGADD=`echo $LINE | awk -F'<' '{print \$1}'` + MSG="$MSG $MSGADD" + continue + fi + if [ "`echo $LINE | grep ''`" != "" ]; then + INSTATUSMSG=1 + INREASON=0 + STATUSMSG=`echo $LINE | awk -F'>' '{print \$2}'` + continue + fi + if [ "`echo $LINE | grep ''`" != "" ]; then + INSTATUSMSG=0 + MSGADD=`echo $LINE | awk -F'<' '{print \$1}'` + STATUSMSG="$STATUSMSG $MSGADD" + continue + fi + if [ "$INREASON" -eq "1" ]; then + MSG="$MSG $LINE" + fi + if [ "$INSTATUSMSG" -eq "1" ]; then + STATUSMSG="$STATUSMSG $LINE" + fi + + done + + if [ -z "$MSG" ]; then + MSG="Unknown Error" + if [ -z "$STATUSMSG" ]; then + STATUSMSG="Please make sure you have the zoneedit, or simplezone edit permission on your account." + fi + fi + + _err "Request failed with error: $MSG ($STATUSMSG)" + + _terminate + fi +}