Dnsmasq on CentOS 6 for Filtering

DNS filtering is a quick and easy method to block access to certain websites. It can be used to block third party advertisements within websites, block known malware infected sites, and can even be used as a content filter (pornography, etc.). In this post, we’ll walk through setting up Dnsmasq on CentOS 6. Dnsmasq is a lightweight DNS forwarding and caching server and DHCP server. In our setup, we will only be using the DNS services of the package.

First, we get a minimal installation of CentOS going. I am using Centos 6 x86_64 on a Dell Poweredge 1850 with 4GB of RAM. The minimal install is extremely minimal, even so far as not having networking set up. The first order of business then is to get our network configuration going. We’ll edit by hand (I prefer the vi editor) the /etc/sysconfig/network-scripts/ifcfg-eth0 file:

[sourcecode language=”plain”]
vi /etc/sysconfig/network-scripts/ifcfg-eth0
[/sourcecode]

In my case, I ended up with:

I masked out the MAC address in the HWADDR field, yours should be pre-filled. You will need to replace the  IP addresses with whatever you environment requires. The USERCTL=yes line is optional: it lets non-root users control the interface. Once the file has the appropriate entries, either reboot or run ‘service network restart’. Once this is done, we can access the machine via PuTTY (SSH client), or continue by logging in locally at the machine.

Next, we get the wget utility installed via yum, download (via wget) the epel repository rpm (in tmp directory), and set it up :

[sourcecode language=”plain”]
cd /tmp
yum install wget
[/sourcecode]
[sourcecode language=”plain”]
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-7.noarch.rpm
sudo rpm -Uvh epel-release*.rpm
[/sourcecode]

Next, we install the Dnsmasq package via yum:

[sourcecode language=”plain”]
yum install dnsmasq
[/sourcecode]

We’ll need to modify the Dnsmasq config file, which is located at /etc/dnsmasq.conf. For this, you can go manual (vi, etc.) or use WinSCP. Here’s the rundown on edits within the conf file:

[sourcecode language=”plain”]

domain-needed
bogus-priv
resolv-file=/etc/resolv.dnsmasq
strict-order
user=dnsmasq
group=dnsmasq
interface=eth0
no-dhcp-interface=eth0
log-facility=/var/log/dnsmasq.log
log-queries
dns-forward-max=150
cache-size=10000
conf-file=/etc/dnsmasq.d/blocklist1
[/sourcecode]

As we have specified a file for parent nameservers (/etc/resolv.dnsmasq), we need to create that file and populate it with upstream DNS IP addresses (I’ll use Google DNS IP’s). We can do this from a CLI with:
[sourcecode language=”plain”]
cd /etc
touch resolv.dnsmasq
echo “nameserver 8.8.8.8“ >> resolv.dnsmasq
echo “nameserver 8.8.4.4“ >> resolv.dnsmasq
cat resolv.dnsmasq
[/sourcecode]

We also need to add a group and user “dnsmasq” as specified in the conf file for the service to run under:
[sourcecode language=”plain”]
groupadd -r dnsmasq
useradd -r -g dnsmasq dnsmasq
[/sourcecode]

We’ve called for a block list in the config as well, located in the /etc/dnsmasq.d folder. The entries are basically an IP address and a domain, such as:
[sourcecode language=”plain”]
address=/facebook.com/127.0.0.1
[/sourcecode]
This would give out the local loopback address of 127.0.0.1 for facebook.com, which would effectively block access. We can specify the IP address of a webserver with a pre-configured page as well that explains that the site in question has been blocked. This webserver could be on another machine, or running on the same machine (via Apache, etc.) as Dnsmasq.

We can create the list manually for testing:
[sourcecode language=”plain”]
cd /etc/dnsmasq.d
touch blocklist1
echo “address=/facebook.com/127.0.0.1“ >> blocklist1
cat blocklist1
[/sourcecode]

We will also need to allow access to UDP port 53 in our firewall (iptables):
[sourcecode language=”plain”]
iptables -I INPUT -p udp –dport 53 -j ACCEPT
service iptables save
service iptables restart
[/sourcecode]

We should have enough of a configuration at this point to test. So, we want to start Dnsmasq and then set it as a service to start up at boot time:
[sourcecode language=”plain”]
/etc/init.d/dnsmasq start
chkconfig dnsmasq on
[/sourcecode]

For testing, we can set the DNS server IP on another machine to point to the Dnsmasq server. It may be a good idea to flush the dns cache on the machine with ipconfig /flushdns assuming a Windows computer. Then, we can use nslookup to test Dnsmasq:

As expected, doing a lookup for facebook.com returns the loopback address (127.0.0.1), which should also return a 404 Not Found if we try to access the site with a web browser. We can also confirm that it is working by viewing the log file found at /var/log/dnsmasq.log. While we are on the subject of logs, lets create a new log rotation file for our Dnsmasq logs. We first create a new file in /etc/logrotate.d and then populate the new file with the following:
[sourcecode language=”plain”]
/var/log/dnsmasq.log {
daily
rotate 32
missingok
notifempty
delaycompress
sharedscripts
postrotate
[ ! -f /var/run/dnsmasq.pid ] || kill -USR2 `cat /var/run/dnsmasq.pid`
endscript
create 0640 dnsmasq dnsmasq
[/sourcecode]
This should rotate the log daily and keep only the 32 most current logs in archive. You can drop the ‘rotate 32’ or increase the number if you wish.

We can improve things a tad by dishing up a custom block page rather than sending people to a 404 page, but we need to install Apache first:
[sourcecode language=”plain”]
yum install httpd
[/sourcecode]
We also need to set the appropriate iptables rule for access:
[sourcecode language=”plain”]
iptables -I INPUT -p tcp –dport 80 -j ACCEPT
service iptables save
service iptables restart
service httpd start
chkconfig –levels 235 httpd on
[/sourcecode]
We then create a simple index page:
[sourcecode language=”plain”]
cd /var/www/html
touch index.html
echo "Website Blocked On This Network" >> index.html
[/sourcecode]
We can test that our block page is working by browsing to the IP address of the server, in my case 192.168.1.97. We’ll also use that IP for future list modifications rather than 127.0.0.1.

Now, we have a working DNS forwarder and caching server, but it isn’t blocking very much (although it could be argued that blocking Facebook is quite alot on it’s own). Let’s see about populating a larger list. We’ll download the black list package from Shalla.de for this example, extract the spyware list, and format it for Dnsmasq. First, let’s edit our /etc/dnsmasq.conf file to include another blocklist:
[sourcecode language=”plain”]
cd /etc
echo “conf-file=/etc/dnsmasq.d/spyware“ >> dnsmasq.conf
[/sourcecode]
Next, we’ll move into the dnsmasq.d folder download the blacklist tarball, and decompress / untar it:
[sourcecode language=”plain”]
wget http://www.shallalist.de/Downloads/shallalist.tar.gz
tar -zxvf shallalist.tar.gz
[/sourcecode]
We should now have a new ‘BL’ (BlackList) directory with many sub-directories and files within. We’ll navigate to the /BL/spyware directory and do some sed surgery on the ‘domains’ list to get them into Dnsmasq format (remember to change the IP address to your machine if you installed Apache earlier, or continue using 127.0.0.1):
[sourcecode language=”plain”]
cd /BL/spyware
sed -e ‘s%^%address=/%’ domains > spyware
sed -i ‘s%$%/192.168.1.97%’ spyware
cp spyware /etc/dnsmasq.d/
cd /etc/dnsmasq.d
ls
[/sourcecode]
We should see our new ‘spyware’ file in the folder, and can now issue a service restart to Dnsmasq:
[sourcecode language=”plain”]
service dnsmasq restart
[/sourcecode]
For testing, use the client machine configured earlier and perform an nslookup on any domain name within the ‘spyware’ file. We can add new blocklists in our dnsmasq.conf file for other blacklists as well. Simply add a new line in the dnsmasq.conf file for a new list (ex: conf-file=/etc/dnsmasq.d/porn), create / format the list and ensure it is in the right folder (/etc/dnsmasq.d in our case), and restart the dnsmasq service.

We can of course automate the process of downloading and formatting our blacklist(s). CentOS 6 uses Anachron for for maintenance cron jobs, which is not really to my liking. Let’s make a few changes:
[sourcecode language=”plain”]
yum remove cronie-anacron
yum install cronie-noanacron sysstat
service crond start
chkconfig crond on
[/sourcecode]

Continuing with Shalla Blacklists as an example, we’re going to format three lists from the total download this time: spyware, redirector, and porn. We also need to be sure that we have all three lists referenced in our dnsmasq.conf file:
[sourcecode language=”plain”]
conf-file=/etc/dnsmasq.d/spyware
conf-file=/etc/dnsmasq.d/redirector
conf-file=/etc/dnsmasq.d/porn
[/sourcecode]

Next, we’ll write up a script to fetch and format our blacklists. We create a new file in the /etc/dnsmasq.d folder named blacklistprep.sh containing the following:
[sourcecode language=”plain”]
cd /etc/dnsmasq.d
rm shallalist.tar.gz
wget http://www.shallalist.de/Downloads/shallalist.tar.gz
tar -zxvf shallalist.tar.gz
cd /etc/dnsmasq.d/BL/porn
# edit porn list for dnsmasq formatting
sed -e ‘s%^%address=/%’ domains > porn
sed -i ‘s%$%/192.168.1.97%’ porn
cp porn /etc/dnsmasq.d/
# edit redirect list for dnsmasq formatting
cd /etc/dnsmasq.d/BL/redirector
sed -e ‘s%^%address=/%’ domains > redirector
sed -i ‘s%$%/192.168.1.97%’ redirector
cp redirector /etc/dnsmasq.d/
# edit malware list for dnsmasq formatting
cd /etc/dnsmasq.d/BL/spyware
sed -e ‘s%^%address=/%’ domains > spyware
sed -i ‘s%$%/192.168.1.97%’ spyware
service dnsmasq restart
[/sourcecode]

Now, we can schedule the script with crontab to run weekly at midnight. On our command line, we input
[sourcecode language=”plain”]
crontab -e
[/sourcecode]
This will take us into the /var/spool/cron/root file where we can add an entry. This will use the default editor, vi.:
[sourcecode language=”plain”]
00 0 * * 0 sh /etc/dnsmasq.d/blacklistprep.sh
[/sourcecode]

(Note: If crontab fails to run this entry properly, you can add a new file under /etc/cron.d/ with the same info. Be sure to restart the crond service afterwards.)

To run the script manually, we can do the following:
[sourcecode language=”plain”]
cd /etc/dnsmasq.d
sh ./blacklistprep.sh
[/sourcecode]

The three lists contained about 873,000 entries as of this post. What I have observed is that some entries (i.e. domains), at least in the Shalla lists, contain an odd character or two, such as the vertical bar character, that causes Dnsmasq to choke on restart. It may be better to drop the service restart line at the end of the blacklistprep.sh script (service dnsmasq restart) and manually restart the service to catch these occurrences. Other lists may not be an issue when using an auto-restart line in terms of errors in formatting.

    Sources for domain lists

Comprehensive:
http://www.shallalist.de/
http://www.urlblacklist.com/
http://dsi.ut-capitole.fr/blacklists/

Ad-Blocking:
http://pgl.yoyo.org/as/