#!/usr/bin/env bash ##################################################### # This script require the following # # * A path structure like this: # [..path..]// # * This path contains a zone file named `db` # # Your named.conf.local file must link to a # filed named `db.signed`, e.g # # zone "domain.tld" { # type master; # file "[..path..]/domain.tld/db.signed"; # }; # # USAGE: # # 1. Make `dnssec-signer` executable: # chmod +x /path/to/dnssec-signer # # 2. Run: # ./path/to/dnssec-signer # 3. Or, for every domain in path # ./path/to/dnssec-signer # ###################################################### SELF="$(basename ${0})" ###################################################### # START OF CONFIG # ###################################################### # SET YOU SYSTEM SPECIFIC DATA (which systemctl e.g) # NAMEDZONES="/etc/bind/named.conf.local" ZONESDIR="/etc/bind/zones" CHECKZONE=/usr/sbin/named-checkzone CHECKCONF=/usr/sbin/named-checkconf KEYGEN=/usr/sbin/dnssec-keygen SIGNZONE=/usr/sbin/dnssec-signzone SYSCTL=/bin/systemctl JOURCTL=/bin/journalctl DNSSERVICE="bind9.service" LOGGER=/usr/bin/logger LOGGERFLAGN="-t $(whoami) -p daemon.info" LOGGERFLAGE="-t $(whoami) -p daemon.err" ###################################################### # END OF CONFIG # ###################################################### function error_msg { FOR=${1} ERRMSG="${2}" echo -e "\e[31m[${SELF}:\e[1m${FOR}\e[21m]:\e[0m ${ERRMSG}" ${LOGGER} ${LOGGERFLAGE} "[${SELF}:${FOR}]: ${ERRMSG}" } function note_msg { FOR=${1} NOTEMSG="${2}" echo -e "\e[32m[${SELF}:\e[1m${FOR}\e[21m]:\e[0m ${NOTEMSG}" ${LOGGER} ${LOGGERFLAGN} "[${SELF}:${FOR}]: ${NOTEMSG}" } # Generating keys for zone # Note, this doesnt check if they exists! function genkeys { ZONE=${1} note_msg "${ZONE}" "Generating missing ZSK and KSK" ${KEYGEN} -a NSEC3RSASHA1 -b 2048 -n ZONE "${ZONE}" if [ $? -ne 0 ]; then error_msg "${ZONE}" "Failed to generate Zone Signing Key (ZSK)" return 1 fi ${KEYGEN} -f KSK -a NSEC3RSASHA1 -b 4096 -n ZONE "${ZONE}" if [ $? -ne 0 ]; then error_msg "${ZONE}" "Failed to generate Key Signing Key (KSK)" return 1 fi note_msg "${ZONE}" "ZSK and KSK generated" return 0 } # Sign zone # The actual signing process. function sign { ZONE="${1}" note_msg "${ZONE}" "Signing ZONE" if ! [ -d "${2}" ]; then error_msg "${ZONE}" "Path ${2} doesnt exist" return 1 fi cd "${2}" echo `pwd` F="db" if ! [ -w "${F}" ]; then error_msg "${ZONE}" "Missing $(pwd)/${F}" return 1 fi ZONEF="db.zone" ZONEINCF="${ZONEF}.include" note_msg "${ZONE}" "Validating unsigned zone file" ${CHECKZONE} "${ZONE}" "${F}" if [ $? -ne 0 ]; then error_msg "${ZONE}" "Error(s) in zone file" if [ -r "${ZONEF}" ]; then note_msg "${ZONE}" "Existing signed zone, allowing restart" return 1 else note_msg "${ZONE}" "No existing signed zone, disallowing restart" return 2 fi fi KEYS=() for KEY in $(ls "./"); do if [[ "${KEY}" == "K${ZONE}"* ]] && [[ "${KEY}" == *key ]]; then KEYS[${#KEYS[@]}]="${KEY}" fi done if [ "${#KEYS[@]}" -ne 2 ]; then if [ "${#KEYS[@]}" -ne 0 ]; then rm "${KEYS[@]}" fi genkeys "${ZONE}" if [ $? -ne 0 ]; then return 1 fi sign "${ZONE}" "${2}" return $? fi cat "${F}" > "${ZONEINCF}" for KEY in "${KEYS[@]}"; do echo "\$INCLUDE ${KEY}" >> "${ZONEINCF}" done SALT=$(head -c 1000 /dev/random | sha1sum | cut -b 1-16) ${SIGNZONE} -A -3 "${SALT}" -N increment -f "${ZONEF}" -o "${ZONE}" -t "${ZONEINCF}" if [ $? -ne 0 ]; then error_msg "${ZONE}" "Error signing zone" return 2 fi note_msg "${ZONE}" "Checking zone-file" ${CHECKZONE} "${ZONE}" "${ZONEF}" if [ $? -ne 0 ]; then error_msg "${ZONE}" "Error zone-file'${SONEF}'" return 2 fi note_msg "${ZONE}" "Signing successul!" return 0 } # Sign zone; # Checks that dir exists first... # and ensures we're entering and leaving correctly function signzone { SIGN="${1}" CWDIR=$(pwd) sign "${1}" "${ZONESDIR}/${SIGN}" RET=$? cd "${CWDIR}" return ${RET} } note_msg "date" "$(date)" if [[ ${EUID} -ne 0 ]]; then error_msg "${USER}" "Must execute file as root." exit 1 fi # Collect arguments (zones) ZONES="${*}" IFS=' ', read -r -a ZONES <<< "${ZONES}" if [ ${#ZONES[@]} -ne 0 ]; then note_msg "${#ZONES[@]}" "Start signing zones" ERR=0 for ZONE in "${ZONES[@]}"; do signzone "${ZONE}" if [ $? -eq 2 ]; then # To prevent restarting dns if failure ERR=1 fi done; if [ ${ERR} -eq 0 ]; then ${CHECKCONF} if [ $? -ne 0 ]; then error_msg "${CHECKCONF}" "Error in configruation, not reloading" else note_msg "$(basename ${SYSCTL})" "Restarting ${DNSSERVICE}" ${SYSCTL} restart "${DNSSERVICE}" if [ $? -ne 0 ]; then ${JOURCTL} -n 10 --unit "${DNSSERVICE}" | xargs -0 fi fi else error_msg "${DNSSERVICE}" "Errors in configuration(s) for zone(s), not restarting." fi else note_msg "${ZONESDIR}" "Finding zones to sign corresponding to ${NAMEDZONES}" CWD=$(pwd) cd "${ZONESDIR}" NAMED=$(cat $NAMEDZONES) for ZONE in $(ls -d */); do ACTIVE=$(cat $NAMEDZONES | grep "\"$(basename ${ZONE})\"" | tr -d '[:space:]') if [[ "${ACTIVE}" == zone* ]]; then ZONES[${#ZONES[@]}]=$(basename ${ZONE}) fi done cd ${CWD} note_msg "" "Found ${#ZONES[@]} zone(s) active" if [ "${#ZONES[@]}" -ne 0 ]; then ${SELF} "${ZONES[@]}" fi fi