295 lines
7.3 KiB
Bash
Executable File
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
|