Block Ads With pfSense 2013-01-20
The Trouble
For the last little while I have been using pfSense as my firewall and I have been generally liking it. Nice balance between having features and being minimal. However, one of the things that has been bugging me about it is the fact that it can't block ad-sites using wildcards. You can add an individual host name to its DNS Forwarder Service, but you can't tell it to resolve all hostnames below a given domain. You can provide an authoritative DNS server for a given domain but that is not quite what I am looking for. So for example, if you want to block
server1.intellitxt.com
server2.intellitxt.com
server3.intellitxt.com

... you have to create individual entries for each one. And frankly, with a bunch of chumps like IntelliTXT you can be sure they'll have plenty of "hostnames" on their list. What's more is that I want to be able to have my list of "BAD sites" replicated to other firewalls that I control so that even if I go elsewhere I'll still be sheltered from that crap.
I haven't had shitloads of free time lately and as a result have not had time go searching through the pfSense forums for answers... until yesterday. I found a post by some guy looking for generally the same thing but for different reasons. Then I found another post explaining that if you simply create a /usr/local/etc/dnsmasq.conf file you can throw in configuration directives as described at the dnsmasq webite. So here is what to do...
The Steps
- Login to the pfSense web interface and enable sshd (under System -> Advanced)
- SSH into the firewall
I don't like to leave sshd open by default, there are just too many douchebags constantly pounding on port 22 all the time. If you are managing your firewall from a Windows based machine you can SSH to it by using something like KiTTY or PuTTY. If you don't know how to SSH into your firewall you may want to consider buying a "hardware router" at your local computer shop.
- pkg_add -r wget
The wget package will be used to download the list of crappy sites from a central distribution point. That way you just update the copy on my webserver and all routers you control will be updated.
- mkdir /usr/local/etc/dnsmasq.d
Later on we will be telling dnsmasq that ALL files in this directory are to be considered config files. That way you can simply add another file if you want to configure dnsmasq for some other purpose (and keep your config files segregated by function).
- vi /usr/local/bin/noads_update.sh
Use vi to create a small script that will update the "bad sites" list and then restart dnsmasq so that it will reload the new list.
#!/bin/sh
cd /usr/local/etc/dnsmasq.d
/bin/rm ad_hosts.txt
/usr/local/bin/wget https://www.your-domain.com/ad_hosts.txt
/usr/bin/killall dnsmasq
/usr/local/sbin/dnsmasqNOTE: that you will of course have to change the URL above to YOUR list of bad sites... you will want to make your ad_hosts.txt easily modifiable by you and readily downloadable by your firewalls.
- chmod 755 noads_update.sh (to make the script executable)
- Logout of your SSH session
- Return to the web interface and disable sshd
Seriously, people suck, just turn it off.
- Add the cron package
Go to "System -> Packages" and add in cron, which will tell pfense to run your little script every now and then.
- Add a cron entry for /usr/local/bin/noads_update.sh to run as admin
You can run it as frequently as you wish though I would think that once a day is enough.
- Create a new file: /usr/local/etc/dnsmasq.conf
Go to "Diagnostics -> Edit File" and first try to LOAD /usr/local/etc/dnsmasq.conf... it should not find anything, then add the following line and save it:
conf-dir=/usr/local/etc/dnsmasq.d
- Restart the cron service from "Status -> Services" so it will reread its configs and will know that it is supposed to run your script.
A Little More Explanation
Now to some that might look a little scattered. So here it essentially what it all does. The /usr/local/etc/dnsmasq.conf file you added at the end is a dnsmasq config file, by adding conf-dir=/usr/local/etc/dnsmasq.d to it you are telling dnsmasq to look in the dnsmasq.d directory and treat [almost] all the files in there as further config files. The cron job you created is for automatically (once daily) running the script you made, and the script just downloads a new copy of the list.
The List To Create
Okay, so what about the list? Well, you would create something like this (notice how wildcard entries start with a "dot"):
# This is a sample of my ad_hosts.txt file
# Start with wildcard entries
address=/.ad-flow.com/127.0.0.1
address=/.adbrite.com/127.0.0.1
address=/.addthis.com/127.0.0.1
address=/.addtoany.com/127.0.0.1
address=/.adsonar.com/127.0.0.1
address=/.adtechus.com/127.0.0.1
# And then individual host names
address=/adcounter.theglobeandmail.com/127.0.0.1
address=/adlog.com.com/127.0.0.1
address=/ads1.msn.com/127.0.0.1
Now you COULD add these entries for "bad servers" directly into the /usr/local/etc/dnsmasq.conf file but by using the /usr/local/etc/dnsmasq.d directory to store your config files, you could easily add another function to dnsmasq later on very easily. Say for example, that you had local network devices that are not very aware of things like DHCP and DNS. You could create another config file (and another script for cron to run) that has your
address=/printer.my-cool-domain.com/192.168.1.100
address=/ipod.my-cool-domain.com/192.168.1.101
address=/whatever.my-cool-domain.com/192.168.1.102
Now you would have one config file for blocking ad servers and another for resolving names of local "DNS unaware" devices.
Additional Notes
I have chosen to resolve these names to 127.0.0.1 which is localhost, you could really use any IP address you wish (but of course you would want a fast webserver that shows nothing). Since most of my "surfing" is done from a Windows box, I like to use Homer Webserver on my Windows boxes to serve up a single transparent GIF file to all requests. That way the blocked sites are about as invisible as I can get them.
Personally I would like to find a better automated way to restart dnsmasq than using "killall dnsmasq" but I seem to be struggling on this one.
Even if you control some networks on which you do NOT use pfSense as your firewall, you can still use the same ad_hosts.txt file (assuming you are bright enough to be using dnsmasq for DNS on those networks).