John Terenzio

Ad Blocking with DNS

JT on 20160302

For a long time, I used the Chrome plugin Adblock Plus. But more recently I've wanted to tighten things up from a security standpoint, and one of the things I felt good about was removing all third-party Chrome plugins, including Adblock Plus. That left me with a problem though- how to continue to block ads and tracking scripts during my everyday browsing.

The immediate answer that came to mind was perhaps blocking unwanted domains in my /etc/hosts file. When I thought of this, I felt like it was an awesome and original idea. But of course when I Googled "ad blocking hosts file" I found that I was certainly not the first person to think of this. There is a great page at where the author maintains a robust hosts file for just this purpose. So problem solved for the time being. I installed this hosts file on a few of my machines and called it a day.

Enter the iPad. I got my first iPad this past Black Friday and I've been loving it. It's the iPad Mini 4, which I think is a perfect size. Anyhow since I can't control the hosts file on this device without jailbreaking it, I initially started accepting that ads and trackings scripts would load during my browsing sessions. I use Chrome on my iPad also, because I like Google sync and I am sticking with one cloud provider for the time being to hold all my sensitive data (Google, that is). So using some other browser with built-in ad blocking, even Safari, was not an option. One day when reading Hacker News, I saw a post entitled Adblock via /etc/hosts . I of course wanted to read about this since it is a topic I am familiar with. But what actually jumped out at me was a post in the comments section talking about how this was similar to blocking ads with a DNS server (specifically dnsmasq). My interest was piqued. So I brew installed dnsmasq and went to work figuring out how to configure it.

HN comment

I have a Mac Mini desktop at my house that I keep on all the time so I can serve media from it, ssh into it when I'm away, etc. I also have a box in Digital Ocean that I serve this site from, as well as some other stuff. At first my instinct was to configure a DNS server on my Digital Ocean box and point my home router at it. This posed a few problems though. Firstly, there would be no redundancy if my DO box went down. Secondly, I didn't want to have a public DNS server because it could be used for DNS Reflection Attacks. So I would, at a minimum, need to whitelist my home IP. IP whitelisting is known not to be very hard to bypass, plus if my Comcast IP changed under me, I would be hosed.

After thinking a bit, I decided that a better solution would probably be to make my home desktop that is always on into a DNS server for my local network. This would cut down on latency, and not introduce security issues. Futhermore, I decided that instead of pointing my router at my desktop for DNS, I could just opt any device into using it (including iDevices, which allow you to configure a custom DNS server for each wi-fi network. Problem solved. I have a new DNS server running at now, and I opted itself, plus my iPhone and iPad into using it to resolve.

DNS on iPhone

Now, how to actually block ads. There are definitely people out there using dnsmasq for this very thing. One interesting project is which is basically what I set up, but using a dedicated Raspberry Pi. I wondered how this project got a list of hosts together, so I dug into the source code and found that they are basically using a collection of sources similar to the MVPS hosts file mentioned before. I went ahead and did some research of my own and constructed my own script and collection of sources to make a dnsmasq configuration file to my liking. It maps about 30,000 hosts to Here is the Ruby script (not aiming for code quality):

require 'open-uri' require 'set' HOST_FILES = %w{ } hosts = output ='dnsmasq.blacklist.conf', 'w') HOST_FILES.each do |file| open(file) do |f| while (line = f.gets) do line = line.strip.gsub(/#.*/, '') next if line == '' _, host = line.split(/\s+/) host = _ unless host next unless host.include?('.') next if host.include?('localhost') unless hosts.include?(host) hosts.add(host) output.puts("address=/#{host}/") end end end end

And here is my final dnsmasq.conf. I did some research here. Mostly by reading the incredibly informative default configuration file that dnsmasq ships with. I decided to hard-code Google's DNS servers as the upstream servers rather than my router, which allows me to enable DNSSEC validation. I also disabled reading my hosts file (it's pretty much empty now), increased the default cache size to be quite large, and enabled the server on my local interface as well as my wireless interface so other devices on my local network can use it. Note that you would also want to assign a static IP for this local machine in your router (DHCP) settings so that it doesn't change over time. Also note that the actual file generated by my script lives separately the conf-dir.

domain-needed bogus-priv dnssec trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5 dnssec-check-unsigned no-resolv interface=en1 interface=lo0 no-dhcp-interface=en1 no-dhcp-interface=lo0 no-hosts cache-size=10000 conf-dir=/usr/local/etc/dnsmasq.d/,*.conf # Google Public DNS server= server= server=2001:4860:4860::8888 server=2001:4860:4860::8844

So that is basically it. I won't bore you with the many phases of configuring dnsmasq or searching for ad blocking blacklists. This is a journey you should undertake on your own. I've been running this new setup now for a few weeks and the results have been supurb. I'm especially pleased with how quickly sites load on my iPad and how responsive they are without all the ad bloat. And most importantly, I can feel slightly better that I'm at least making it more of a pain to build a file somewhere about my needs and wants as an American consumer. If you want to chat more about this, contact me!