#!/bin/bash
#< Installs the newly transferred slave named.conf file after consistency checks
# This script is part of my DNS named.conf transfer scripts. The scripts
# are designed to be used for the transferral of a frequently changing
# named.conf file from a primary nameserver to a secondary nameserver.
# The scripts assume a lot. The zone files used in my test environment
# were very "bare-bones" and simple. Example:
# Any additional configuration options present in the file would
# require script modifications (sync_to_slave.sh) to accomodate parsing
# the other configuration options. This script is designed to be easily
# modified, and should be considered a template for named.conf transfers.
# The scripts revolve around a central lock file mechanism, which aims to
# ensure that no incomplete named.conf files are transferred whilst they are
# being edited - although it will not parse a file for syntactical correctness.
# Also, this assumes that all edits to the named.conf file are made using the
# vinamed wrapper, or a custom tool that uses the lockfile mechanism used in
# these scripts.
# The scripts provided are:
# vinamed Lock file protected named.conf editing wrapper
# sync_to_slave.sh Intended to be scheduled via cron on the
# primary nameserver, BEFORE the script below
# runs on the slave nameserver.
# install_slave_config.sh Schedule this to run a few minutes after
# the sync_to_slave.sh script runs on the
# primary nameserver. This script contains
# various checks (lockfile, confirms whether
# the newly transferred named.conf file is
# modified, etc.) This script also creates
# backups of named.conf prior to, and after
# modification. Then it restarts BIND.
# Example cron jobs (depending on the frequency of your named.conf file
# edits).
# master# crontab -l | grep sync_to_slave
# 0,30 * * * * /usr/local/dns/bin/sync_to_slave.sh
# slave# crontab -l | grep install_slave_config
# 15,45 * * * * /usr/local/dns/bin/install_slave_config.sh
# You will also need to check the paths presented in the scripts, and ensure
# that the appropriate backup directories are created, or the paths
# referenced in the scripts are changed.
# For the key-based file transfer to work, I'm using a "namesync" user on each
# server.
# Executable paths
CAT="/bin/cat"
CHOWN="/bin/chown"
CHMOD="/bin/chmod"
CP="/bin/cp"
DATE="/bin/date"
DIFF="/usr/bin/diff"
ECHO="/bin/echo"
ID="/usr/bin/id"
LS="/bin/ls"
MV="/bin/mv"
RM="/bin/rm"
TAIL="/usr/bin/tail"
OUTPUT_SUFFIX="$( ${DATE} +%Y%m%d ).$$"
# Files
SYSCONFDIR="/etc"
NAMED_INIT="${SYSCONFDIR}/init.d/named"
NAMED_CONF="${SYSCONFDIR}/named.conf"
NAMED_BACKUP_DIR="${SYSCONFDIR}/named.bak"
NAMED_BAK="${NAMED_BACKUP_DIR}/${NAMED_CONF##*/}.${OUTPUT_SUFFIX}"
LOCKFILE="/var/lock/vinamed.lock"
NAMESYNC_HOME="/home/namesync"
INCOMING_DIR="${NAMESYNC_HOME}/named.xfer"
UPDATE_FILE="${NAMESYNC_HOME}/named.conf.latest"
BACKUP_DIR="${NAMESYNC_HOME}/named.bak"
# Errors
SUCCESS="0"
ENOTROOT="1"
ELOCKFILE="1"
# ToDo: Check that the appropriate backup directories exist
# Function: print_error
# Arguments: $@
# Returns: Nothing
# Description: echo wrapper to output to STDERR
function print_error {
${ECHO} "Error: $@" >&2
}
# Function: print_msg
# Arguments: $@
# Returns: Nothing
# Description: echo wrapper to output to STDOUT with prefix
function print_msg {
${ECHO} "--> $@"
}
# Function: check_root_user
# Arguments: None
# Returns: Nothing - will exit on failure
# Description: Checks whether the script is being executed as root user
function check_root_user {
WHOAMI=$( ${ID} -u )
if [ "${WHOAMI}" -ne "0" ]; then
print_error "You must be root to run this program"
exit ${ENOTROOT}
fi
}
# Function: check_lockfile
# Arguments: None
# Returns: Nothing - exits on failure
# Description: Exits if the lockfile exists
function check_lockfile {
if [ -e "${LOCKFILE}" ]; then
print_msg "Lockfile exists - is ${NAMED_CONF} being edited?"
exit ${ELOCKFILE}
fi
}
# Function: create_backup
# Arguments: None
# Returns: Nothing
# Description: Creates backup of current named.conf file
# ToDo: Check if backup directory exists
function create_backup {
${CP} -p ${NAMED_CONF} ${NAMED_BAK}
print_msg "Backup named.conf at ${NAMED_BAK}"
}
# Function: diff_conf_file
# Arguments: None
# Returns: Nothing
# Description: Performs a diff of the newly transferred file against the
# currently installed file, and exits if the file hasn't been
# changed. Nethertheless, a backup of the incoming file is
# created.
# ToDo: Optionally backup the file - allow a command-line argument
# to optionally disable the backup functionality
function diff_conf_file {
# Here we are just checking whether or not the incoming
# file is different from the current installed file
${DIFF} ${NAMED_CONF} ${INCOMING_DIR}/${LATEST} >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
print_msg "No differences in new config file"
# Yes, this is also good, but still backup incoming file
# discarding /etc/named.conf backup as the file hasn't
# been modified
print_msg "Removing ${NAMED_BAK}"
${RM} ${NAMED_BAK}
${MV} ${INCOMING_DIR}/${LATEST} ${BACKUP_DIR}
exit ${SUCCESS}
fi
}
# Function: install_config
# Arguments: None
# Returns: Nothing
# Description: Installs the new named.conf file, creating a backup of
# the current "good" configuration.
function install_config {
print_msg "Last modified file version will be installed"
LATEST="$( ${LS} -1rt ${INCOMING_DIR} | ${TAIL} -n 1 )"
if [ ! -f "${INCOMING_DIR}/${LATEST}" ]; then
print_msg "No new config file to install"
# Again, this is a good exit
exit "${SUCCESS}"
fi
print_msg " ${LATEST}"
diff_conf_file
${CP} ${INCOMING_DIR}/${LATEST} ${BACKUP_DIR}
${MV} ${INCOMING_DIR}/${LATEST} ${SYSCONFDIR}
${MV} ${SYSCONFDIR}/${LATEST} ${NAMED_CONF}
${CHOWN} -R namesync:users ${BACKUP_DIR}
${CHOWN} root:root ${NAMED_CONF}
${CHMOD} 644 ${NAMED_CONF}
print_msg "New /etc/named.conf installed"
${CAT} ${NAMED_CONF}
}
# Function: named_restart
# Arguments: None
# Returns: Nothing
# Description: Restarts BIND to ensure the changes take effect
# ToDo: Check for errors when restarting BIND that would indicate
# possible configuration file corruption
function named_restart {
print_msg "Issuing ${NAMED_INIT} restart"
${NAMED_INIT} restart
}
check_root_user
check_lockfile
create_backup
install_config
named_restart
exit ${SUCCESS}