dn42regsrv/contrib/sync_rootzone.sh
2019-10-05 22:24:28 +01:00

295 lines
7.3 KiB
Bash
Executable File

#!/bin/bash
##########################################################################
#
# This script is intended to be run via cron job to sync a local,
# authoritative DNS server's root zone with the registry information
# provided by dn42regsrv.
#
# As is, the script is specific to updating PDNS within the burble.dn42
# network, however it is also intended to be easily adaptable to other
# DNS servers and networks
#
##########################################################################
##########################################################################
# This array is used to define additional, local networks that the
# DNS server may be authoritative for. The array is used to prevent
# the related resource records from being removed automatically,
# as any records not listed here or in the registry will get deleted.
# Make sure to include '.' here or the local NS and SOA records
# will get removed
IGNORE_RECORDS=(
'.'
'$ORIGIN'
'ns1.burble.dn42'
'burble.dn42'
'collector.dn42'
'1.0.6.2.2.4.2.4.2.4.d.f.ip6.arpa'
'160/27.129.20.172.in-addr.arpa'
'0/27.129.20.172.in-addr.arpa'
)
##########################################################################
# The functions here are used to actually update the DNS server
# Change these to use a different server than PDNS
PDNSUTIL='/usr/bin/pdnsutil'
PDNSCTRL='/usr/bin/pdns_control'
# replace_rr <name> <type> <content>
function replace_rr {
local rr_name=$1; shift
local rr_type=$1; shift
local rr_content=$*
echo "Replace: ${rr_name} ${rr_type} '${rr_content}'"
if [ ${DEBUG} -eq 0 ]
then
${PDNSUTIL} replace-rrset . ${rr_name} ${rr_type} "${rr_content}"
fi
}
# delete_rr <name> <type>
function delete_rr {
local rr_name=$1
local rr_type=$2
echo "Delete: ${rr_name} ${rr_type}"
if [ ${DEBUG} -eq 0 ]
then
${PDNSUTIL} delete-rrset . ${rr_name} ${rr_type}
fi
}
# list the current contents of the root zone
function get_current_root_zone {
local rra rr_name rr_type rr_content
while read -r -a rra
do
rr_name=${rra[0]}
rr_type=${rra[3]}
rr_content=${rra[@]:4}
current_rz["${rr_name} ${rr_type}"]=${rr_content}
done < <(${PDNSUTIL} list-zone .)
}
# update the . SOA record after a change
function update_soa {
echo "Incrementing SOA serial"
if [ ${DEBUG} -eq 0 ]
then
${PDNSUTIL} increase-serial .
fi
}
# used to trigger a notify to any slaves of this server
function notify_slaves {
echo "Notfying slaves"
if [ ${DEBUG} -eq 0 ]
then
${PDNSCTRL} notify .
fi
}
##########################################################################
# No further local customisation should be needed from here
##########################################################################
# initialise script parameters and global vars
function usage {
echo "Usage: $0 [-h] [-d] [-a URL] [-c FILE]"
echo " -h: this help"
echo " -d: enable debugging and don't action changes"
echo " -a: URL to dn42regsrv API"
echo " -c: file in which to store previous commit number"
}
# default options
DEBUG=0
APIURL="http://explorer.burble.dn42/api"
COMMITFILE="/tmp/.sync_rz_commit"
# parse any arguments passed to the script
while getopts ":hda:" opt
do
case ${opt} in
d)
DEBUG=1
;;
a)
APIURL=${OPTARG}
;;
*)
usage
exit 0
;;
esac
done
# global vars
declare -A current_rz
declare -A new_rz
current_commit=''
new_commit=''
deleted_records=0
updated_records=0
##########################################################################
# fetch and parse the root zone data from the API
function fetch_new_root_zone {
local line fields rr_name rr_type rr_content
while read -r line
do
if [[ ${line} == ';; Commit Reference:'* ]]
then
new_commit=${line#;; Commit Reference: }
else
# strip out comments and create array
fields=( ${line%%;*} )
# if the line is a valid record
if [ ${#fields[@]} -ge 4 ]
then
rr_name=${fields[0]}
rr_type=${fields[2]}
rr_content=${fields[@]:3}
new_rz["${rr_name} ${rr_type}"]=${rr_content}
fi
fi
done < <(/usr/bin/wget -O - -q "${APIURL}/dns/root-zone?format=bind")
if [ ${DEBUG} -eq 1 ]; then echo "New Commit: ${new_commit}"; fi
}
##########################################################################
# load and store the previous commit
function load_current_commit {
read -r current_commit < "${COMMITFILE}"
if [ ${DEBUG} -eq 1 ]; then echo "Current Commit: ${current_commit}"; fi
}
function store_current_commit {
if [ ${DEBUG} -eq 0 ]
then
echo "${1}" > "${COMMITFILE}"
fi
}
##########################################################################
# remove records that have been deleted
function is_ignored {
local rr_name=$1
for i in "${IGNORE_RECORDS[@]}"
do
[ "${i}" == "${rr_name}" ] && echo "ignored"
done
echo ""
}
function remove_deleted_records {
local key rr_name ignored
deleted_records=0
# check each record in the old root zone
for key in "${!current_rz[@]}"
do
ignored=$(is_ignored ${key% })
# if record is not ignored, and no new record exists
if [ "${ignored}" == '' -a "${new_rz[${key}]}" == '' ]
then
# then get rid of it
delete_rr ${key}
deleted_records=$((deleted_records + 1))
fi
done
echo "Deleted ${deleted_records} records"
}
##########################################################################
# update records that have been added or changed
function update_new_records {
local key content
updated_records=0
# check each new record
for key in "${!new_rz[@]}"
do
content="${new_rz[${key}]}"
# if old record didn't exist, or content differs
if [ "${current_rz[${key}]}" != "${content}" ]
then
# update the record
replace_rr $key ${content}
updated_records=$((updated_records + 1))
fi
done
echo "Updated ${updated_records} records"
}
##########################################################################
# main flow of the script starts here
echo "DN42 Root Zone Sync"
date
echo
fetch_new_root_zone
# check that the commit was populated
if [ "${new_commit}" == '' ]
then
echo "Unable to fetch new root zone, aborting"
exit 1
fi
load_current_commit
# now check if anything actually needs to be done
if [ "${new_commit}" == "${current_commit}" ]
then
echo "Commits are equal, nothing to do"
exit 0
fi
get_current_root_zone
# apply changes
remove_deleted_records
update_new_records
# bail out if there were no actual differences
if [ $((deleted_records + updated_records)) -eq 0 ]
then
echo "No records were updated, exiting"
else
# update the SOA and send out a notification to slaves
update_soa
notify_slaves
fi
# finally store the new commit to show it's been updated
store_current_commit "${new_commit}"
echo "All done"
##########################################################################
# end of code