Ad blocking with Raspberry Pi and Pi-hole
cri.dev
cri.dev
https://github.com/StevenBlack/hosts
That is about 60,000 hosts, so I added MaraDNS support to have up to 500,000 blacklisted names. Since it’s a speed-optimized (not size-optimized) cache, each element takes about a kilobyte of memory, so a blacklist this size takes about 60 megabytes of memory for MaraDNS to store (on a modern Core i7 7600U processor, it only takes about two seconds to load all 60,000 elements in to memory), but it’s very rapid to use.
The script to take that blacklist and convert it in to a MaraDNS compatible format is here:
https://github.com/samboy/MaraDNS/blob/master/deadwood-githu...
There are ways to make the memory footprint of the blacklist smaller, but this was a quick and simple way to implement a medium sized blacklist. Finding ways to have, say, 10 million blacklist elements with a small memory footprint is left as an exercise for the reader.
My current project is to make a proper Docker container for MaraDNS.
The strings are special structures which allow binary data to be stored in them and to protect against buffer overflow protection, so they take up more room than a normal string.
Since the cache is a dynamic LRU cache (blacklist entries are not in the circular LRU, of course, but the cache was designed that way) for temporarily storing correct DNS answers (with overhead because, yes, each entry has among other things a 64-bit expire timestamp), each entry is created or removed with multiple malloc() and free() calls, so there’s some page size overhead there.
Like I said, there are ways to make a blacklist have a smaller memory footprint, by using a special data structure which trades speed for size, [2] but for a two-day coding project, it made more sense to just add them to the speed optimized LRU cache the code is built around.
[1] As an aside, the hash compression algorithm came out before SIP Hash, but, like SIP Hash, has protection against an attacker finding hash bucket collisions. If I were to write the code today, I would use SIP Hash.
[2] Here is how I would do it: I would have two blocks of data. One block is an array of offsets (pointers, but not C memory pointers) to data in the second block, and the second block is a bunch of NULL-terminated strings compactly stored. The array of offsets is a sorted list, where the sort key is the string each element points to. I would then do a binary search every time I wanted to see if an element is blacklisted. Memory overhead is, for 10 million entries with an average length of 15 bytes per string (including the NULL) 30 million bytes for the offsets and 150 million bytes for the strings, so we could store 10 million entries in under 200 megs. Speed is O(log2(N)) instead of O(1) but we save a lot of memory. We would need more memory to sort that list, but we could store this block as a binary file which is made on a host with a lot of memory, but read on our Raspberry Pi or old Netbook. Again, doing this is left as an exercise for the reader, and I’m sure there’s a lot of public domain or otherwise open licensed libraries which have already solved this problem. If I were to go down that path; I would probably use sqlite, but am a bit reluctant to add sqlite to a tiny program which can compile to be under 70k in size.
The problem is that it works really great for static data, but doesn’t work for dynamic data as cleanly as using a linked list to handle collisions.
The one thing I would do differently is that Deadwood (MaraDNS’s recursive/blacklist DNS server) adds individual elements with malloc() and removes them with free(); doing things that way is OK for relatively small data sets on a desktop class computer, but some embedded systems don’t handle the stress of a lot of malloc() and free()s very well, and doing it that way uses a significant amount of memory per element.
Yes, there are dynamic perfect hashes, but they are too slow when we update the hash with each lookup, a common use case with DNS.
For example, given a block list of 60,000 how many hosts on the list did your computer actually try to access. Have you ever read through one of those massive blocklists.
I use a whitelist rather than a blocklist. Similar to a firewall, I block everything by default. Then carefully choose what I allow.
This way I can see exactly what hosts I actually need to access. Makes it easy to see attempts to access ad servers, trackers, phone home, etc.
upstream_servers = {}
upstream_servers["."] = "192.168.253.253" # Never answers
upstream_servers["good-domain.example.com."] = "9.9.9.9"
upstream_servers["whitelist-entry.foo."] = "9.9.9.9"
# And so on
The downside is that the code currently only supports 20,000 elements added this way.What I am curious about are these massive blocklists. Does anyone actually read through them. Does it make sense to block 60,000 hosts when only, say 100 or even 1000, ever stand a chance of being accessed by a user's computer -- due to the user's particular usage habits.
It might make sense though to do a periodic run through them to see if they still exist.
NSD/Unbound are good DNS servers, as is the newer Knot DNS suite.
djbdns was really good in the early 2000s, but hasn’t been updated since 2001 and its age is starting to show. Yes, there are third party patches, but forks of the djbdns codebase have this annoying way of getting abandoned after a couple of years. I’ll probably end up doing basic maintenance of a djbdns fork just so there’s something getting minimal maintenance available to download.
In case the reader is interested (and this may not be what OP had in mind): In trading memory gains with computation, this is how I squeezed ~4M entries in less than 30 MiB:
1. Compress (reversed) individual entries using a dictionary or entropy encoder. I experimented with Shoco [0], Femto [1], and a modified LZ78 [2].
2. Create a bloom filter for membership checks, [3] or any probabilistic structure for that matter, like the xor-filters [4]. This prevents expensive but unnecessary lookups.
3. Insert the compressed (reversed: google.com -> com.google) entries into a radix-tree [5]. You could use a directed-acyclic graph (DAG), if you don't need to store key-value pairs.
4. Compact [6][7] the radix-tree [8] (compacting a DAG might be super complicated).
5. On every search:
5a. Compress (reversed) incoming query (i.e. domain-name).
5b. Check the bloom filter.
5c. Traverse the radix-tree, if necessary.
5d. Cache an eligible entry in a LFU [9] or possibly, using any other suitable algorithm inspired from Linux's page replacement implementations [10].
6. Strive for zero-allocation and zero-gc.
---
[0] https://news.ycombinator.com/item?id=10060018
[1] https://github.com/gtoubassi/femtozip/wiki/How-femtozip-work...
[2] https://en.wikipedia.org/wiki/LZ78
[3] https://news.ycombinator.com/item?id=12231623
[4] https://news.ycombinator.com/item?id=21840821
[5] https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-lin...
[6] https://news.ycombinator.com/item?id=22544718
[7] https://www.youtube-nocookie.com/embed/3Y2weLDiUWw
[8] https://news.ycombinator.com/item?id=2348619
[9] https://github.com/ronomon/hash-table/blob/master/README.md
[10] https://en.wikipedia.org/wiki/Page_replacement_algorithm
Unplugged it faster than I could swear.
Since you can't buy dumb TVs anymore, you best leave the "smart" ones offline.
But the bottom line is that the number of attempts to resolve the DNS doesn't say anything about how egregious the activity is. You could have a device try every second because it wants to send an "I'm alive" message home, or you could have a device attempt DNS resolution once per day and send all the traffic it captured from your network. Don't use that number as an indication of how evil the device is, I'm sure the most egregious malware or crappy IoT will be careful not to flood DNS requests.
Android TV is a massive improvement upon any Smart TV's UX though, so I'm winning on that count.
Though rare to find a 4k/OLED TV that's "dumb", I managed to grab one.
If I do want certain smart features e.g. chromecast I can always just buy it individually for like $30.
But for my phone, I set up a PiHole running on an EC2 instance and VPN into it from my phone. Blocks ads in everything, not just my web browser. The VPN is configured to only tunnel DNS lookups, not traffic, so the EC2 bandwidth bill is minimal.
But I have an instance that I already use for other things (IRC bouncer, Mumble server, Taiga task/issue tracker, and test server for a game I was writing) that has plenty of CPU time to spare, so it's not costing me anything.
That said, the massive performance gains of blocking ads and intrusive 3PC probably more than makes up for it, but something to consider.
But for the most part, I imagine it won't make much of a difference. I live within 300 miles of the data center containing my EC2 instance.
I have no idea if the Pi-hole is configured with this extension.
Firefox can have this feature disabled, but more malicious applications will simply not give you the choice.
The only other way would be MITM all https traffic and sign with your own certificate, but many iot devices won’t allow you to install a new certificate store.
Will a canary domain on an edge device prevent my smart TV from using DoH to get the IP of a tracking server?
If your smart tv checks to see if it should disable DOH then it would work.
I wouldn’t bank on it doing so though. Best to block all iot devices.
> The use of this domain is specified by Mozilla, as a limited-time measure until a method for signaling the presence of DNS-based content filtering is defined and adopted by an Internet standards body.
More than anything I am disappointed in Mozilla for shipping such a half-baked anti-feature. I can't even trust Firefox to do the right thing anymore.
Intimately DoH isn’t needed to make IoT transparent, an IoT device can establish a TLS tunnel to a CDN, and unless that’s blocked you have no way of knowing what is being sent where.
An in-line https proxy is the only solution, if iot devices fail to check certificates (which wouldn’t surprise me)
If they do check, and you can’t provide your our root, then you can’t trust them. Read GNU manifesto and realise why it’s important.
I am conflicted over DoH. As an end user I want to be protected against rogue networks - especially in hotels. As a network admin I want to be protected against rogue end devices.
As both I want to be able to deploy split dns servers due to Nat, not to force users (including me), but to don’t to them where to go.
DoH should have been implemented at the OS level and driven by DHCP unless overridden, as with normal DNS.
Also, moving DNS to the application instead of the OS just makes managing my well behaved applications harder. It used to be possible to set this automatically through DHCP, now I have to audit every application and make sure it is using my preferred DNS and also manually configure those applications.
DNS-over-HTTPS in Firefox doesn't really benefit me. DNS-over-HTTPS with a DHCP configured server in my OS would be a beneficial option.
Really I would prefer something like DNS over TLS because that solves everything. DNS keeps working, intermediate parties can't read my DNS requests and I can reason about my network traffic, at least on devices I trust.
It's much harder (or impossible) to block only some of the IoT device traffic (like ads). If I want it to function at all I have to let it spy on me and do other rude things.
My solution is to just not have IoT things but in the long run I think that will become impossible. Consumers seem to have made their choice and everything needs an IP address now.
I don't like how DNS-over-HTTPS smells and I wish there was less momentum behind it. This should be solved by OS vendors, not application developers. DNS doesn't belong at this part of the stack.
OTOH I expect non-malicious code to just use the platform/language/framework-standard mechanism to resolve hostnames, and I expect such mechanisms to provide all the necessary customization points, just as they did for DNS.
The solutions are different but a device like a Pi-hole probably has a part to play in both.
I avoid phone banking as it is...
1) https://support.mozilla.org/en-US/kb/canary-domain-use-appli...
My main concern with putting PiHole on my home network is that for example my mother in law might not understand that she can't get to some web page because it's being ad-blocked, nor would she be able to go to the web admin page and temporarily unblock it.
Even as a technical person sometimes it takes a while to figure out that a page isn't working because of adblock or pi-hole.
How do people deal with this?
They would still have to know that the site isn't working because of blocking, but I suppose developing the habit of "website doesn't work, press the magic button and try again" isn't so bad.
If I do PiHole just for myself I think I'll add this button. Thanks!
It points to ..../admin/api.php?disable=300&auth=authkey
300: Time in seconds to disable it.
Auth key: You need to have a password set for the Pi-hole installation, and the API key is the salted version of that password. You can find it as the value for the key WEBPASSWORD in your /etc/pihole/setupVars.conf file.
Works great, even if I dont invoke Siri to trigger it.
[Edit] Grammar.
I keep reading about people having to disable the Pi-Hole so much that it becomes annoying, or constantly butting heads with websites that don't work with Pi-Holes, but I can honestly say that I, and the people I've set them up for, BARELY EVER need to use the Disable button. Not for online banking, not for online shopping, not for any regular content consumed. I can't personally think of the last time I needed to disable the Pi-Hole for anything at all. It just sits there blocking ads invisibly, plugged into the back of my modem/router, and is one of my absolute favourite parts of my tech stack.
What websites are people visiting that the Pi-Hole doesn't work with? I'm genuinely curious, because I've been running mine for a couple years now and can't think of anything off the top of my head, and no one in my family group have ever complained about it, with most telling me it's been flawless and they barely every touch the button.
I've been meaning to make a blog post about it but haven't got around to it yet. I'll try write something up later today/tonight.
[0] https://raspberrypihq.com/use-a-push-button-with-raspberry-p...
Edit: I found this which is a much bigger/fancier version of what I did, but has the associated code you can pull out for a simple button: https://github.com/nickearl/piHoleButtons
Usually sites where the dev has based functionality on a JS module loaded from an advertise/tracker site, and that is being blocked, resulting in missing functionality.
Two prominent examples that I've personally had to deal with: CVS, and Taco Bell's iOS app.
Edit: Oh, and google's inserted redirects in shopping results lists are blocked by default.
That comes up now and again, not as much as you'd expect but enough that I needed to give instruction on it. Funny thing is, most of those cases are _me_ doing things like signing into Nvidia's software instead of anyone else here.
I think I managed to have the blacklist/whitelist easier though by creating an activity log page that shows exactly what was blocked and allowed and on what device - in near real-time. Fun project at first but I didn't know what I was going into at first.
It is a bit frustrating when starting but, once you start whitelisting what was blocked by mistake, you will see that it gets easier, almost unnoticeable by a week or so.
I always had uBlock and disconnect.me and it still blocks, somewhat consistent, about 16% of the traffic which is a big number considering.
It doesn't really do this as well as a browser adblocker, YouTube ads for example can't effectively be blocked with pihole
It should never be considered an "or" situation; you want "both" as much as possible.
But yes self-hosted ads, etc. won't be blocked. For that, there are other solutions.
Personally, I use both.
Google tries to force users to turn on JavaScript by needlessly making the YouTube website "require" JS to be turned on to play videos.
However it is easy to play the videos without using JavaScript using just basic HTML.
Example
It needs to stop.
I know it's a lame solution but I use ff and containers and have a "shopping" container for sites that I don't block trackers on. I also have various other containers for other sites I don't mind knowing about each other. But mostly I block them.
I would love to see real data on this! Most of my peers don't want to be marketed to, at least when it comes to internet advertising. Definitely when it comes to traditional advertising (ie billboards are an eyesore, TV ads are offensive or irrelevant or just plain annoying).
I understand that advertisers pay for the sites I use and I would turn off the ad-blocker if I was promised I wasn't being tracked. But we have no control over those decisions so we do what we can.
Learning how to do this is not "hard" per se, but if you haven't had your own computer growing up and been born into an environment that encourages learning, you aren't going to be able to do this.
https://github.com/rajannpatel/Pi-Hole-PiVPN-on-Google-Compu...
It's in stark contrast with my server, which admittedly has more demanding tasks. For $35 it's the most trustable computer I bought, I now have a lot of respect for the Pi foundation.
For my Pi I regularly update without a reboot, but that might not be enough for kernel updates. I will look more into it.
As I read your comment all I could visualize was an IE6 browser with 8 toolbars. The network stack of the future.
Is there some trickery to getting AdGuard to run stable? I have it running as an Add-On to Home Assitant, so I'm not sure if that's causing the issues, but I get lots of failed DNS queries, slowdowns, etc when going through AdGuard.
VPNs typically tunnel your packets thru an encrypted connection to a gateway somewhere else on the internet.
Ad blockers point your DNS to a resolver that blacklists ad domains. You can use a VPN and still set your DNS to whatever you'd like. What works best for you will depend on your threat model (or just privacy concern, as that other term sounds loaded).
I tried to make this setup as robust as possible, using OverlayFS [1] so root is read-only unless I remount it (to change configuration or upgrades) and with a watchdog so it can auto-reboot in case my Raspberry Pi is unresponsive. Pretty happy with this setup and it my internet seems more responsible than ever.
[1]: https://yagrebu.net/unix/rpi-overlay.md/wiki/Setting_up_over...
I block ads at the application layer, DNS based ad blocking doesn’t really do it anymore and can create another troubleshooting surface to consider when it breaks something.
-- mostly: out of about 7 videos, an ad was shown to me at the start of one of them, but that is much better than mobile Safari, on which and ad is shown to me at the start of every video I start.
Thanks for the tip.
The extra benefits are - play video with the screen off to listen to lectures - youtube music - specifically good for letting the toddler hear the wiggles without having to watch a wiggles video on youtube
I also pay hulu for no ads. I'm happy for the opportunity to pay directly for entertainment instead of with my eyeballs. No way to opt out of tracking, though, hence the pihole.
I like how you phrased this as a solved engineering problem.
https://www.reddit.com/r/oisd_blocklist/comments/dwxgld/dblo...
I'm actually satisfied, but I'm trying to setup pi-hole+unbound on a cloudatcost host I own to reduce the load on nextdns, so far without success.
https://sfxpt.wordpress.com/2011/02/21/the-best-ad-blocking-...
Set up a VPN to an EC2 with pihole on it, no more ads on Spotify. Even family members wanted the VPN config, they really appreciate it.
curl -sSL https://install.pi-hole.net | bash
SighMy observations so far have been that Android tends to ignore any DNS set by either the network via DHCP or statically set. Android instead probes the gateway for 8.8.8.8, and happily uses that instead.
The only way I have been able to solve this has been to setup a VPN (I prefer wireguard) on the pihole. Android seems to accept this.
The above in combination with say a DDNS hostname means that I now have a permanent adblocked VPN on my android phone which isn't too bad.
Although with DoH these days, I'm not confident my firewall rule is still doing a good job :(
At this point every device on my network is hostile, default deny outbound is starting to feel like the reasonable starting point.
I don't want to add an extra router because that would add unnecessary latency. The above is not an unusual setup at all.
- in windows, if you use primary dns as pihole and secondary dns as another cloud option (cloudflare, google), some ads will go through. secondary dns is not failover dns. try it and you will see.