• We value your experience with Plesk during 2024
    Plesk strives to perform even better in 2025. To help us improve further, please answer a few questions about your experience with Plesk Obsidian 2024.
    Please take this short survey:

    https://pt-research.typeform.com/to/AmZvSXkx
  • The Horde webmail has been deprecated. Its complete removal is scheduled for April 2025. For details and recommended actions, see the Feature and Deprecation Plan.
  • We’re working on enhancing the Monitoring feature in Plesk, and we could really use your expertise! If you’re open to sharing your experiences with server and website monitoring or providing feedback, we’d love to have a one-hour online meeting with you.

mr-wolf

Silver Pleskian
Plesk Guru
Recently I got an unusual request to create 2 CNAMES for a client for which we're doing a lot of stuff and also DNS-hosting.
They will be moving their site to some clever load-balancing webserver.

The CNAME needed to be created on the www.client.com and on the client.com
The latter is not possible in Plesk nor in Bind.

Here's some more info (which I didn't read all):

Introducing CNAME Flattening: RFC-Compliant CNAMEs at a Domain's Root
Why it's a bad idea to put a CNAME record on your root domain
CNAME Flattening: RFC-Compliant CNAMEs at a Domain's Root | Hacker News

I gave it all kind of thoughts and after trying some other workarounds I decided to simulate their cname flattening trick using a normal Plesk.

I have it as an hourly cronjob. The TTL on their CNAME is 60 seconds and they have a pool of 12 IP's of which 4 IP's are taken out on a regular basis. The IP's that are taken out keep responding to the http-requests (I tested this by putting 1 of those IP's in my /etc/hosts). It also keeps some tracking, so you know what that other DNS-provider is doing. Use it if you think it fits your scenario.

The script can run in 2 modes
With "USE_PLESK_CLI=true" it will use Plesk's dns command to add/remove records.
With "USE_PLESK_CLI=" it will manipulate the zonefiles directly
If you want a high update frequency (beneath an hour) it might be better to let it manipulate the zonefiles directly.

[Edit] I wouldn't use the mode that directly edits the zonefile as of yet. I have trouble getting it to sync with the slave server.
I'm updating the serial, but it doesn't come over.
Maybe I will replace the published code with the USE_PLESK_CLI mode only.

I'm open for suggestions or questions.

BTW: I still advised the webmaster to redirect immediately from https://clientdomain.com to https://www.clientdomain.com. This way, the amount of users actually going to the bare domain should remain minimal.

cat /usr/local/sbin/flattencnames.conf
Code:
client.com client.com.herokudns.com.

# cat /usr/local/sbin/flattencnames
Code:
#!/bin/bash

# Booleans
USE_PLESK_CLI=
USE_PLESK_CLI=true
MUTATED=
HEADLESS=
TAB="`echo -en '\t'`"
tty >/dev/null || HEADLESS=true

# Constants
THISSCRIPT="`readlink -f $0`"
CONFIG="${THISSCRIPT}.conf"
SCRIPTNAME=${THISSCRIPT##*/}
TMPDIR=`mktemp -t -d ${SCRIPTNAME}.XXXXXXXXXX`
TRACKERPREFIX="/var/log/${SCRIPTNAME}"
ZONEDIR=/var/named/run-root/var

# check some prerequisites
if [ ! -e "${CONFIG}" ] ; then
  echo "I'm expecting a config file with CNAMES to be flattened: ${CONFIG}" >&2
  exit 1
fi
if [ ! ${USE_PLESK_CLI} ] ; then
  if [ ! -d "${ZONEDIR}" ] ; then
    echo "The folder ${ZONEDIR} where I expect the zonefiles doesn't exist!" >&2
    exit 1
  fi
fi


mutrecords()
{
  MUTATED=true
  ACTION=$1
  echo ${ACTION} | egrep -q "^(add|del)$" || exit 1

  [ ${HEADLESS} ] || echo -e "${ACTION} record: ${DOMAIN}\tIN A\t${IP}" >&2
  if [ ${USE_PLESK_CLI} ] ; then
    /usr/local/psa/bin/dns --${ACTION} ${DOMAIN} -a "" -ip ${IP} 2>&1 >${TMPDIR}/dnsoutput
    [ ${HEADLESS} ] || cat ${TMPDIR}/dnsoutput >&2
  else
    # Modify zonefile directly (outside Plesk)
    # This means that the Plesk Panel doesn't represent the actual status of the bare A-records
    # On the other hand the database entries can't be modified while you're busy editing
    # Maybe use this method if you want to run this script more often than once an hour.

    if [ ! -f "${ZONEDIR}/${DOMAIN}" ] ; then
      echo "The Zonefile ${ZONEDIR}/${DOMAIN} doesn't exist, I can't do anything" >&2
    else
      if [ "${ACTION}" = "del" ] ; then
        sed -i -e "/^${DOMAIN}\..*IN A.*${IP}$/d" ${ZONEDIR}/${DOMAIN}
      else
        sed -i -e "1,/^${DOMAIN}\..*IN A/ {/^${DOMAIN}\..*IN A.*/i\
${DOMAIN}.${TAB}${TAB} IN A${TAB} ${IP}
}" ${ZONEDIR}/${DOMAIN}
      fi
    fi
  fi
}

update_tracker()
{
  [ -f "${TRACKER_FILE}" ] || touch "${TRACKER_FILE}"

  IP_AND_TIME="`echo $IP | awk '{ printf("%-15s",$0)}'`\t${TIME}"

  if grep -q "^${GREP_IP}" "${TRACKER_FILE}" ; then
    OLDCOUNTER=`grep "^${GREP_IP}" "${TRACKER_FILE}" | awk '{print $6}' | tr -cd '0-9'`
    [ -z "${OLDCOUNTER}" ] && OLDCOUNTER=0
    COUNTER=$((${OLDCOUNTER} + 1))

    REPLACEMENT="`grep "^${GREP_IP}" "${TRACKER_FILE}" | cut -b1-31`\t${TIME}\t${COUNTER}"
    sed -i -e "s/^${IP}.*/${REPLACEMENT}/g" "${TRACKER_FILE}"
  else
    echo -e "${IP_AND_TIME}\tNEW" >>"${TRACKER_FILE}"
  fi

}

# Remove commented and empty lines
sed -e '/^\s*#/d;/^\s*$/d' ${CONFIG} >${TMPDIR}/config

while read LINE ; do


  DOMAIN="`echo ${LINE} | awk '{print $1}' | tr 'A-Z' 'a-z' | egrep -o '[a-z0-9-]+\.[a-z0-9-]+'`"
  CNAME="`echo  ${LINE} | awk '{print $2}' | tr 'A-Z' 'a-z' | egrep -o '[a-z0-9.-]+' | sed 's/\.$//'`"

  ARECORDS=${TMPDIR}/${DOMAIN}
  CNAMEIPS=${TMPDIR}/${DOMAIN}.cnameips

  TIME="`date +%d-%b-%y\ %H:%M`"
  TRACKER_FILE="${TRACKERPREFIX}-${DOMAIN}.log"

  if [ ! -z "${DOMAIN}" ] ; then

    host ${DOMAIN} localhost | grep -o 'has address .*' | egrep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort -n >${ARECORDS}
    host ${CNAME}            | grep -o 'has address .*' | egrep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort -n >${CNAMEIPS}

    if [ ! -s ${CNAMEIPS} ] ; then
      [ ${HEADLESS} ] || echo "${CNAME} doesn't resolve to ipv4 addresses, I'm skipping this domain (${DOMAIN})" >&2
    else
      if diff ${ARECORDS} ${CNAMEIPS} >${TMPDIR}/difference ; then
        [ ${HEADLESS} ] || echo "${DOMAIN}: nothing changed"
      else

        grep '^<' ${TMPDIR}/difference | egrep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' >${TMPDIR}/delete
        grep '^>' ${TMPDIR}/difference | egrep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' >${TMPDIR}/add

        DELETED=`grep -c . ${TMPDIR}/delete`
          ADDED=`grep -c . ${TMPDIR}/add`

        echo "${DOMAIN}: ${DELETED} record(s) deleted and ${ADDED} record(s) added"

        while read IP ; do
          GREP_IP="`echo ${IP} | sed 's/\./\\./g'`"   # change dots to backslash dots for more precise searching
          mutrecords add
          update_tracker
        done<${TMPDIR}/add

        while read IP ; do
          GREP_IP="`echo ${IP} | sed 's/\./\\./g'`"   # change dots to backslash dots for more precise searching
          mutrecords del
        done<${TMPDIR}/delete
        if [ ! ${USE_PLESK_CLI} ] ; then
          OLDSERIAL=`grep '; Serial' ${ZONEDIR}/${DOMAIN} | awk '{print $1}'`
          SERIAL=$(( ${OLDSERIAL} + 1))
          sed -i "s/${OLDSERIAL}/${SERIAL}/" ${ZONEDIR}/${DOMAIN}

          rndc reload ${DOMAIN} 2>&1 >/dev/null
        fi
      fi
    fi
  fi
done<${TMPDIR}/config

rm -r ${TMPDIR}

As a test it's now running as a 5-minute cronjob (I would suggest using 1 hour):
Code:
ln -s /usr/local/sbin/flattencnames /etc/cron.5min/

The first datestamp is the initial registration, the 2nd one is the last registration.
Then at the end there's a number representing the times that IP had to be re-added.

# cat /var/log/flattencnames-client.com.log
Code:
46.137.179.247  12-Apr-17 12:59 12-Apr-17 17:00 16
46.51.179.164   12-Apr-17 12:59 12-Apr-17 17:00 15
54.217.215.80   12-Apr-17 12:59 12-Apr-17 16:55 14
54.228.204.220  12-Apr-17 12:59 12-Apr-17 16:55 13
54.228.233.224  12-Apr-17 12:59 12-Apr-17 17:05 13
54.228.244.215  12-Apr-17 12:59 12-Apr-17 17:05 13
176.34.254.113  12-Apr-17 12:59 12-Apr-17 17:00 11
54.246.108.43   12-Apr-17 12:59 12-Apr-17 17:05 11
54.247.71.175   12-Apr-17 13:05 12-Apr-17 17:05 10
54.247.88.150   12-Apr-17 13:05 12-Apr-17 16:20 7
54.247.97.70    12-Apr-17 13:05 12-Apr-17 16:50 7
176.34.180.226  12-Apr-17 13:05 12-Apr-17 17:00 11
 
Last edited:
Hi mr-wolf,

How did this work go, it's been a while?
Have you found a way to effectively and safely do ANAME on Plesk?

Cheers
 
OK, so is it a way to implement ANAME records in Plesk and can it be used on a multi-domain shared server, in your view and experience?
 
mmm, not really.
It's a dirty hack and if you understand what it's doing you can implement it as well.
I did it to keep that customer in our DNS-system. In the long run they will be leaving the service that forces them to use this system.
I think those services are unethical by implying that conventional DNS (what we have in Plesk) is lacking features and therefore bad, when in fact they work according to RFC's.

The effect is that you have multiple A-records pointing to IP-addresses. This then becomes RFC-compliant.

[Edit]
I never had complaints, but I can imagine that it may sometimes point to an IP which in the mean time doesn't point to a service servicing that particular hostname.
With a low TTL the chances of that should be little.

I just read my original post again and it seems I tested that scenario and it seems that, at least with Heruko, it's safe. I forgot I tested that because I was worried about that, then
 
Last edited:
Back
Top