#!/usr/bin/env bash # - Replaces a TLSA Record # - # - Return (Exit) Codes: # - success: # - 0: TLSA record is up to date # - 1: TLSA record replaced # - 2: New TLSA record written # - error: # - 10: Invalid TLSA record given # - 11: No zonefile for TLSA record found # - 15: Hostname/Domain not supported # - 20: Replacing record failed # 21: Adding Record failed # - 99: Fatal error # - # - example: # - ./replace_dns_tlsa.sh _25._tcp.mail.initiativenserver.de. IN TLSA 3 1 1 aab3a46b387dd543ed8d... ## -- Variable definitions ## -- # - Bind configuration file containing zone definitions # - ZONE_CONF_FILE=/etc/bind/named.conf.local BIND_USER=bind BIND_GROUP=bind ## -- ## -- End: Variable definitions ## *** ## *** Don't make changes after this line *** ## *** ## --- some functions ## --- echononl(){ echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ } warn (){ echo "" echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" echo "" } info (){ echo "" echo -e "\t[ \033[33m\033[1mInfo\033[m ]: $*" echo "" } error(){ echo "" echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*" echo "" } echo_ok() { echo -e "\033[75G[ \033[32mok\033[m ]" } echo_failed(){ echo -e "\033[75G[ \033[1;31mfailed\033[m ]" } echo_skipped() { echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" } backup_dir () { dir_to_backup=$1 echononl "\tBackup existing directory \"$dir_to_backup\" .." if [[ -d "$dir_to_backup" ]] ; then cp -a $dir_to_backup ${dir_to_backup}.BAK.`date +%Y-%m-%d-%H%M` if [[ $? -eq 0 ]]; then echo_ok else echo_failed echo "" exit 99 fi else echo_failed error "Directory \"$dir_to_backup\" not found. No Backup written!" exit 99 fi } ## --- ## --- END: functions # - Parameter "check" can be used, to test whether this script # - is accessable (e.g. from a script on a remote host) # - if [[ "$1" = "check" ]]; then echo "\$1: $1" exit 0 fi # - Split given Record into an array # - declare -a record_arr=($@); if [[ ${#record_arr[@]} -eq 7 ]]; then record_name=${record_arr[0]} record_ttl="" record_type="${record_arr[1]} ${record_arr[2]} ${record_arr[3]} ${record_arr[4]} ${record_arr[5]}" record_hash=${record_arr[6]} elif [[ ${#record_arr[@]} -eq 8 ]]; then record_name=${record_arr[0]} record_ttl=${record_arr[1]} record_type="${record_arr[2]} ${record_arr[3]} ${record_arr[4]} ${record_arr[5]} ${record_arr[6]}" record_hash=${record_arr[7]} else error "Invalid TLSA record given!" exit 10 fi # - Split record_name, to get port,protocol,hostnaem,domain # - CUR_IFS=$IFS IFS='\.' declare -a split_record_name_arr=($record_name) IFS=$CUR_IFS _port=${split_record_name_arr[0]} port=${_port##*_} _protocol=${split_record_name_arr[1]} protocol=${_protocol##*_} hostname="${split_record_name_arr[2]}" declare -i _index=3 while [[ $_index -lt ${#split_record_name_arr[@]} ]] ; do hostname="${hostname}.${split_record_name_arr[$_index]}" let _index++ done # - Determin zone (domain) # - _failed=false _hostname=$hostname _tmp_hostname=$(echo ${_hostname//\./\\.}) while ! grep -e "$_tmp_hostname" $ZONE_CONF_FILE > /dev/null 2>&1 ; do _hostname=${_hostname#*.} _tmp_hostname=$(echo ${_hostname//\./\\.}) if [[ ! $_tmp_hostname =~ \. ]]; then _failed=true break fi done if $_failed ; then error "Given hostname/domain \"$hostname\" not supported by this nameserver!" exit 15 else domain=$_hostname fi # - Determine zonefile (by reading bind configuration) # - _found=false declare -i _number=0 regex_zone="^[[:space:]]*zone[[:space:]]+\"$_tmp_hostname\"" regex_file="^[[:space:]]*file" while IFS='' read -r line || [[ -n "$line" ]] ; do if [[ $line =~ $regex_zone ]]; then _found=true fi if $_found ; then if [[ $line =~ $regex_file ]]; then zone_file=`echo $line | awk '{print$2}'` shopt -s extglob if [[ $zone_file =~ \; ]]; then zone_file=${zone_file%%*(\;)} fi if [[ $zone_file =~ ^\" ]]; then zone_file=${zone_file##*(\")} zone_file=${zone_file%%*(\")} fi shopt -u extglob let number++ break fi fi done < $ZONE_CONF_FILE if [[ $number -eq 0 ]] ; then error "No Zonefile (master) found for domain \"$domain\" ." exit 11 fi zone_file_dir=`dirname $zone_file` # - Backup existing zone file directory # - # - Update/Add TLSA recotd if needed # - if grep -E "^$record_name.+$record_type" $zone_file > /dev/null 2>&1 ; then if [[ -n "$record_ttl" ]]; then search_string="^$record_name\\s+$record_ttl\\s+$record_type" else search_string="^$record_name\\s+$record_type" fi if grep -E "$search_string" $zone_file | grep $record_hash > /dev/null 2>&1 ; then info "TLSA record is already up to date.." echo "" exit 0 else _replac_string=${record_arr[@]} # - Backup Zone directory backup_dir $zone_file_dir # - Replace TLSA Record echononl "\tGoing to replace TLSA Record.." perl -i -n -p -e "s#^${record_name}.+${record_type}.*#$_replac_string#" $zone_file if [[ $? -eq 0 ]] ; then echo_ok echo "" exit 1 else echo_failed echo "" exit 20 fi fi else warn "No Record for replacing fount in zonefile \"`basename $zone_file`\"!" declare -i _count search_string="^_${port}\._(tcp|udp)\.$hostname" _count=`grep -Eo "$search_string" $zone_file | wc -l` _tlsa_record_found=true if [[ $_count -eq 0 ]]; then search_string="^_[0-9]{1,4}\._(tcp|udp)\.$hostname" _count=`grep -Eo "$search_string" $zone_file | wc -l` if [[ $_count -eq 0 ]]; then search_string="^_[0-9]{1,4}\._(tcp|udp).*TLSA" _count=`grep -Eo "$search_string" $zone_file | wc -l` if [[ $_count -eq 0 ]]; then _tlsa_record_found=false search_string="^[^;].+\s+IN\s+MX" _count=`grep -Eo "$search_string" $zone_file | wc -l` if [[ $_count -eq 0 ]]; then search_string="^[^;].+\s+IN\s+NS" _count=`grep -Eo "$search_string" $zone_file | wc -l` if [[ $_count -eq 0 ]]; then error "No place for adding a new TLSA record found. Check manually!" exit 99 fi fi fi fi fi CUR_IFS=$IFS IFS='' _tmpfile=`mktemp` > $_tmpfile # - backup zone directory backup_dir $zone_file_dir # - Add new TLSA record echononl "\tAdd new TLSA record to zonefile \"\".." while read -r line || [[ -n "$line" ]]; do echo $line >> $_tmpfile if echo "$line" | grep -E "$search_string" > /dev/null 2>&1 ; then let _count-- fi if [[ $_count -eq 0 ]]; then echo "" >> $_tmpfile if ! $_tlsa_record_found ; then echo ";" >> $_tmpfile echo "; DANE" >> $_tmpfile echo ";" >> $_tmpfile fi echo "${record_arr[@]}" >> $_tmpfile echo "" >> $_tmpfile _count=-1 fi done < "$zone_file" if [[ $? -eq 0 ]] ; then echo_ok else echo_failed rm $_tmpfile exit 21 fi IFS=$CUR_IFS mv $_tmpfile $zone_file # - Set Coorect Owner/Permission echo "" echononl "\tCorrect Owner for $zone_file .." chown $BIND_USER:$BIND_GROUP $zone_file if [[ $? -eq 0 ]] ; then echo_ok else echo_failed exit 99 fi echononl "\tCorrect permissions on $zone_file .." chmod 644 $zone_file if [[ $? -eq 0 ]] ; then echo_ok else echo_failed exit 99 fi echo "" exit 2 fi echo exit 99