In an attempt to be a little more friendly in terms of bandwidth to the strapped folk over at Malware Domains, I’ve retooled the script I wrote about in the post To Be Protecting Against the Malware.
I’ve added some lines to take advantage of remote zipped files (.zip), which will help them by reducing the number of bits we’re pulling from them.
I’ve added some lines to copy the downloaded malware zones file to other servers behind my firewall, which will help them by not making individual connections from each server to pull the files. I just set up a cron job on each internal “slave” server to bounce named every morning timed for after this process is complete.
Here’s the updated code. It is, as is my wont, rather verbose. It is considerably more verbose than other examples out there that take care of this same problem, but as I said, such is my wont.
The URLS array is filled with fake hosts right now b/c the zipped format is still in testing. When the folk at malwaredomains.com think it’s ready for public consumption, I’ll put the real hosts back in.
Also, it’s relatively untested, and I expect to be tweaking it. Use at your own risk.
On the “master” server, I’m using this…
#!/usr/local/bin/bash
# To know where script is running
HOSTNAME=$( hostname )
# To put file where named can see it
BLACKHOLEDIR=/var/named/etc/namedb/blackhole
# To name file so we know what named seeing
TMPZONEFILE=tmp.malwaredomains.zones
ZONEFILE=malwaredomains.zones
ZONEFILEBACKUP=malwaredomains.zones.bak
# To get updated file from remote server
URLGRABBER=/usr/local/bin/curl
USERAGENT="Malware Domain Grabber ( ${HOSTNAME}; unix; BASH )/0.1"
# To keep quiet while am getting file
URLGRABBEROPTS="-s -f"
# To know where file is hosted
URLS=( host1 host2 host3 host4 )
TIMESTAMPFILE=timestamp
# To know how to decompress the file
UNZIPCMD=/usr/local/bin/unzip
UNZIPOPTS="-o -qq"
# To copy files to other servers so that we are only
# pulling the files once, though we have multiple
# DNS servers in house
HOSTS=( host1 host2 host3 host4 )
# MOUNTCMD: The mount command
MOUNTCMD=/sbin/mount
UMOUNTCMD=/sbin/umount
# FSTYPE: The filesystem type of the mounted partition
FSTYPE=nfs
# MOUNTDIR: The directory that the dumps will be written to
MOUNTDIR=/mnt
# To control bind
NAMEDCMD="/usr/sbin/rndc reload"
#==============================================================
# Get start time so we can know how long this thing runs
START=$( date +%s )
# Make our working directory the location of the blackhole files
cd ${BLACKHOLEDIR}
# Copy the current timestamp file to ${TIMESTAMPFILE}.old so we can
# make a comparison between what we have and what's out there now.
if [ -f ${BLACKHOLEDIR}/${TIMESTAMPFILE} ]; then
cp ${BLACKHOLEDIR}/${TIMESTAMPFILE} ${BLACKHOLEDIR}/${TIMESTAMPFILE}.old
fi
# Attempt to download the timestamp file and zone file from each mirror.
# Break out of the loop at the first successful download of a zone file,
# otherwise, try each one in turn
# Assume there are no updates available
NEW=0
for URL in "${URLS[@]}"; do
echo "Attempting to download from ${URL}"
echo " Checking timestamps..."
${URLGRABBER} ${URLGRABBEROPTS} -A '${USERAGENT}' -o ${BLACKHOLEDIR}/${TIMESTAMPFILE}.zip ${URL}/${TIMESTAMPFILE}.zip
if [ $? -ne 0 ]; then
echo " ... timestamp download from ${URL} failed! Code: $?"
# Move on to next URL so we keep the timestamp/zonefile pair intact
continue
else
if [ -f ${BLACKHOLEDIR}/${TIMESTAMPFILE} ]; then
# Unzip the new timestamp file over the old old one
${UNZIPCMD} ${UNZIPOPTS} ${BLACKHOLEDIR}/${TIMESTAMPFILE}.zip
# Do a little cleanup
rm -f ${BLACKHOLEDIR}/${TIMESTAMPFILE}.zip
OLDTIMESTAMP=$( cat ${BLACKHOLEDIR}/${TIMESTAMPFILE}.old )
NEWTIMESTAMP=$( cat ${BLACKHOLEDIR}/${TIMESTAMPFILE} )
if [ ${OLDTIMESTAMP} -ge ${NEWTIMESTAMP} ]; then
echo " ... no new updates."
# No new updates on this server... but how well are the various mirrors
# kept in sync? Let's try the others. This is a tiny transfer, and it's
# only once a day, so it's pretty cheap.
continue
fi
else
# Timestamp file does not exist. Create it.
${UNZIPCMD} ${UNZIPOPTS} ${BLACKHOLEDIR}/${TIMESTAMPFILE}.zip
rm ${BLACKHOLEDIR}/${TIMESTAMPFILE}.zip
fi
fi
# Backup and copy file to final location for named to find
# (via "include" directory in named.conf)
echo "Backing up zone file"
cp ${BLACKHOLEDIR}/${ZONEFILE} ${BLACKHOLEDIR}/${ZONEFILEBACKUP}
echo "Retrieving new zone file from ${URL}..."
${URLGRABBER} ${URLGRABBEROPTS} -o ${BLACKHOLEDIR}/${ZONEFILE}.zip ${URL}/${ZONEFILE}.zip
if [ $? -ne 0 ]; then
echo " ... zonefile download from ${URL} failed! Code: $?"
# Oops. Try the next server. If this is the last, then ${NEW} is still
# set to 0, and we'll be done. Better luck tomorrow...
continue
else
# We have a new timestamp, and were able to download the zone file from
# the same server we downloaded the timestamp from. Set ${NEW} to 1 and
# get out of the loop. No need to check further.
echo "Unzipping new zone file..."
if [ -f ${ZONEFILE}.zip ]; then
${UNZIPCMD} ${UNZIPOPTS} ${BLACKHOLEDIR}/${ZONEFILE}.zip
rm ${ZONEFILE}.zip
# Rename the zone file temporarily to allow sed to work on it later, and
# and in that process, rename it back to the name that named knows.
mv ${ZONEFILE} ${TMPZONEFILE}
else
echo "No new zone file..."
exit
fi
NEW=1
break
fi
done
# If ${NEW} hasn't been set, then we either error'd out of all servers, or there are no
# new files. Either way, we're done.
if [ ${NEW} == 0 ]; then
exit 1
else
# Disable name checking for only those domains with underscores,
# so we don't have to turn off name checking globally.
SEARCH='_'
FIND='blockeddomain.hosts";};'
REPLACE='blockeddomain.hosts"; check-names ignore;};'
# Get a count of the zones from the last update
OLDZONECOUNT=$( cat ${BLACKHOLEDIR}/${ZONEFILEBACKUP}|grep "^zone"|wc -l )
echo "Disabling checking on domains with underscores"
sed "/${SEARCH}/ s/${FIND}/${REPLACE}/g" ${BLACKHOLEDIR}/${TMPZONEFILE} > ${BLACKHOLEDIR}/${ZONEFILE}
rm -f ${BLACKHOLEDIR}/${TMPZONEFILE}
# Get a count of the zones from the current update
NEWZONECOUNT=$( cat ${BLACKHOLEDIR}/${ZONEFILE}|grep "^zone"|wc -l )
echo "${OLDZONECOUNT} Previous Zones"
echo "${NEWZONECOUNT} Current Zones"
echo "Reloading named"
${NAMEDCMD}
if [ $? -ne 0 ]; then
echo " ... failed! Restoring zone file"
cp ${BLACKHOLEDIR}/${ZONEFILEBACKUP} ${BLACKHOLEDIR}/${ZONEFILE}
echo "Reloading old zones in named"
${NAMEDCMD}
if [ $? -ne 0 ]; then
echo " ... failed again!! You'll want to see to that."
fi
fi
echo "Copying files to other internal network servers..."
for HOST in "${HOSTS[@]}"; do
DUMPDEVICE=${HOST}:${BLACKHOLEDIR}
MOUNTRESULTS=$( ${MOUNTCMD} | grep "${DUMPDEVICE} on ${MOUNTDIR}" )
if [ "${MOUNTRESULTS}" == "" ]; then
echo ""
echo "Mounting ${DUMPDEVICE} on ${MOUNTDIR}"
${MOUNTCMD} -t ${FSTYPE} ${DUMPDEVICE} ${MOUNTDIR}
if [ $? = 1 ]; then
echo " ... failed. Files will not be copied."
continue
else
echo " ... succeeded"
fi
else
echo "${HOSTNAME}:${DUMPDEVICE} already mounted on ${MOUNTDIR}"
fi
# Copy the files to ${MOUNTDIR} as a temporary file. On the remote server,
# we'll manage bouncing named if necessary.
echo ""
echo "Copying ${BLACKHOLEDIR}/${ZONEFILE} to ${TMPZONEFILE}"
cp ${BLACKHOLEDIR}/${ZONEFILE} ${MOUNTDIR}/${TMPZONEFILE}
if [ $? = 1 ]; then
echo "... Failed to copy ${ZONEFILE}! You might want to see to that."
fi
# Umount the backup filesystem
echo ""
echo "Unmounting ${MOUNTDIR}"
${UMOUNTCMD} ${MOUNTDIR}
if [ $? = 1 ]; then
echo " ... failed. You might want to see to that."
else
echo " ... succeeded"
fi
done
END=$( date +%s )
RUNTIME=$(( ${END} - ${START} ))
H=$(( ${RUNTIME}/3600 ))
M=$(( ( ${RUNTIME}/60 ) % 60 ))
S=$(( ${RUNTIME} % 60 ))
echo "Malware zonefile download on ${HOSTNAME} complete in"
echo "${H} hrs, ${M} mins and ${S} secs (${RUNTIME} secs)"
exit
fi
On the “slave” servers, I’m using this…
#!/usr/local/bin/bash
# To put file where named can see it
BLACKHOLEDIR=/var/named/etc/namedb/blackhole
ZONEFILE=malwaredomains.zones
TMPZONEFILE=tmp.malwaredomains.zones
# To control bind
NAMEDCMD="/usr/sbin/rndc reload"
if [ -f ${BLACKHOLEDIR}/${TMPZONEFILE} ]; then
echo "New zone file exists..."
# Rename the zone file to back it up
echo "Backing up current zone file."
mv ${BLACKHOLEDIR}/${ZONEFILE} ${BLACKHOLEDIR}/${ZONEFILEBACKUP}
# Rename the tmp file to the name the daemon can find
echo "Replacing it with the new zone file and removing the temp file."
mv ${BLACKHOLEDIR}/${TMPZONEFILE} ${BLACKHOLEDIR}/${ZONEFILE}
# Reload named.
${NAMEDCMD}
if [ $? -ne 0 ]; then
echo " ... failed! Restoring zone file"
cp ${BLACKHOLEDIR}/${ZONEFILEBACKUP} ${BLACKHOLEDIR}/${ZONEFILE}
echo "Reloading old zones in named"
${NAMEDCMD}
if [ $? -ne 0 ]; then
echo " ... failed again!! You'll want to see to that."
fi
fi
else
echo "No update. Quitting..."
fi