(Project Journal) - Pomodoro Timer that Blocks Domains

Hi all! In the spirit of spinning up a journal from the discussions here How do you all come up with ideas / things to tinker with? - #23 by Biky (thanks again everyone by the way), I wanted to start writing up findings about a personal project I’d find very useful: a pomodoro timer, that during the ‘work’ periods, will automatically block certain domains.

Reason / Motivation

I struggle immensely with aimlessly browsing YouTube, Reddit, Twitter, and some random news sites when I want to be focusing on something else. I also have found that using a pomodoro timer helps my brain do a mental shift, even if it is placebo, into doing some useful work. So why not combine the two ideas?

MVP: A CLI tool (that works on Linux) that acts as a pomodoro timer, that during the ‘work’ periods, blocks a given list of domains.

Limitations

  1. I have roommates that visit some of the sites I want to block during work periods, and so I cannot perform this blocking on the home network level. Thus, this should be a tool that runs on and only affects the device I am working on.

  2. I would like this to work across platforms, not just Linux (though for my first draft, just a Linux solution is fine). Sometimes I’m working on my Windows partition, and my team at work is a Mac shop, so a cross-platform solution would be ideal. However this could limit the approach.

First Steps

  1. I’m currently messing with adding a few domains manually to /etc/hosts like so:
...
0.0.0.0 	www.youtube.com
...

However, this has not worked so far. I’m on Pop OS, so idk if there’s something that is superseding /etc/hosts, but all I know is, I’m either doing something wrong or it isn’t being used as the ‘last step’ for DNS resolution.

Getting this working would work for Linux, and help get a functional PoC working.

  1. Explore whether the /etc/hosts approach would work on Mac / Windows (doubtful?). The idea, is that the script would automatically edit /etc/hosts and restart the local network service to make the changes, and then revert the old state of /etc/hosts back either during a short break in the timer, or when the app closes.

  2. This all may lead me to needing to write a simple proxy program to block DNS requests on the list of blocked domains, I’d probably do this in Go as that is what I’m familiar with, but I’ve done very little network programming in Go (or at all, really).

Skills I hope to / probably will learn

  1. Solidify some basic DNS networking knowledge
  2. Network Programming (in Go, if I have to write a simple proxy as explained above)
  3. How different OS’s treat /etc/hosts and DNS requests in general.

Final Thoughts

I am open to all feedback, including some potential “This is a bad idea”, as maybe there’s an approach I’m missing out on. I do want to write my own solution though (as opposed to using existing software), as a way of both fixing a problem I have, and learning some new skills.

5 Likes

That is a great idea, i just don’t like to create my own tools to solve a vice, because i really like to break things.

My approach to this issue i would just fork an browser extension because im lazy… or i would monitor my browser ram usage and if goes up i would just kill it, i would make it so annoyinly terrible to procastinate that i would actually not do it. After thinking about it for longer them i want to admit, you can do some really evil stuff not only blocking the sites tho.

2 Likes

Yep, this is undoubtedly overkill. Hence why a ton of browser extensions exist for blocking domains.

I like the idea of the browser ram usage… but I still would want to use the internet for looking at documentation, GitHub, etc even when I’m working.

So I discovered why my /etc/hosts wasn’t being respected, and it had nothing to do with my OS.

Firefox was not respecting /etc/hosts due to the default DNS Over HTTPS option being enabled. Turning that off now has it respecting /etc/hosts, so now my youtube / reddit blocking works.

As long as I can get the apps I use to respect /etc/hosts and across platforms, I think that’s the best approach, as writing an entire proxy / resolver in Golang could be time-consuming and be hard to work with any of the OS’s that’s not Linux.

Edit: of course now, any change in /etc/hosts requires Firefox’s cache being cleared to work…

1 Like

Why not setup a RPI as proxy machine with this and proxy your device into it when you want to make use of it?

Avoids the entire cross platform issue; also means avoiding manual setup on each client device.

Also, disabling security features for this would be best avoided, in my opinion, even if you only plan to visit safe sites your familiar with.

I was thinking about this option (though in a cloud VM such as Linode instead of RPI, though that makes more sense).

Doing the proxy approach could mean a bit less immediate feedback on where the pomodoro timer is at, as in whether you’re in a work or a break period. Having a separate GUI service running on the RPI with some kind of indicator could fix that.

2 Likes

I’d probably run a web service for interaction and easy feedback to the user; if you run it in a Linode and put a bit of work in the GUI, could ask others to try using it

1 Like

Yeah Linode isn’t the way to go, unless I was adamant about making it accessible anywhere (like if I take my laptop to a coffee shop, and not having to open up the home network) but I’m not.

I’m thinking as a quick PoC, setup a VM with a Squid proxy with the blocked domains setup on the VM’s /etc/hosts, see how that goes. I have a very basic Go program for handling the backing up / applying of the blocked domains to /etc/hosts, but needs more work before I start testing that.

Edit:

Proxy to a vm with squid + domain blocking with /etc/hosts is working. My browser can bypass this due to caching, however doing ctrl + shift + r gets past that. Happy to see that progress, thanks for the suggestion. There may be a solution to the caching “issue”, but that will be for another day

Interesting project! This probably needs a network-wide project implementation instead of something local but I understand and respect not wanting to bother roommates.

All in all, the issue may be best solved by disciple on the fleshware side instead of the software side.
——-
EDIT: Wow that sounded inappropriate and NSFW-like :joy::rofl:

1 Like

Hah you’re not wrong, but I’m kind of hoping that this will help me reach a more disciplined state on the fleshware side :wink: .

If not, well, I made a pomodoro / domain blocker using a proxy… so at least I would have learned something haha.

Luckily using the proxy server approach avoids the roommate problem, as they would have to be proxied in

1 Like

A proxy requires additional manual configuration and TBH, they’re kind of a PITA.

A DNS blocker would be the way to go. There is Pi-Hole and NoTrack for that, but neither of them have timers based on work hours or other arbitrary definition. There was however a proprietary product that I’ve seen on NetworkChuck’s channel, called “The Circle” (parental control - by the way, the link in the video to the circle is broken). But screw that, it’s $69 for the device and 1 year subscription and then $9.99 / month or $89.99 / year. Aside from the fact that it’s better to establish a trust relationship so the kids come to you to ask things, as opposed to trying to block their access that they will circumvent anyway, like using mobile traffic or a free VPN or proxy if they find how to get around network restrictions.

The Circle does ARP poisoning on your network. If it sounds bad, it is, because that’s what usually 3rd parties try to do to Man-in-the-Middle attack you, but this will ARP poison only devices that you tell it to (based on device names, it finds its MAC and IP and pretends to be the router, so all traffic from a device is filtered through it).

Anyway, why am I talking about this? Because it has some nifty features. Inside the mobile program, you can restrict some users access to, let’s say Disney+, but if they behave and you want to compensate them, you can allow access to Disney+ for 5 minutes or 2 hours or however much you decide and it’s not based on a timer, but on a connection time basis.

I believe you want to create a product that could easily work as a parental control.

In any case, these guys went the ARP poisoning route to make their product plug-and-play for normies. We can do better than that by doing a DNS server. Of course, a DNS can be bypassed just as easily as other techniques, with proxy being the hardest to circumvent (if not impossible if you want to stay on the same network, depending on the network rules).

You can set the DNS in your DHCP settings and all clients that get their IP addresses through DHCP (or at least just network info in case of stateless DHCPv6, O=1 flag) will start using your DNS server. You can just set a rule for everyone in your DNS server depending on the hour, but that wouldn’t be too challenging, would it? Since you are also in control of DHCP, you can set static DHCP mapping to clients, so they retain their IP addresses and based on that information, you can tell your DNS server project to have different rules for different IPs. Obviously, this requires a lot more work and you need to implement this feature in you DNS server project. If you go the easy route at first with just a single rule for everyone between certain hours, it’s probably going to be as simple as running a cron job to add entries to /etc/hosts on the DNS server, then remove them when the time is outside the set period time frame.

But if you decide to create a DNS server with custom domain resolution (or not resolution) for each IP address, I recommend you look at djbdns to see how that works and how to make it secure (by making it minimal), if you decide to write it from scratch in Go.

1 Like

And this is exactly the reason (or one of them) why DNS over HTTPS on a per application level is a bad idea™. It is not difficult to set this up for the entire machine. Better yet for the entire network.

I like the idea. You probably don’t need anything foolproof, just something that is annoying enough to circumvent.

1 Like

Got around to working on this a bit today.

The Go program now has a simple timer (hard coded time value for now) that does the appropriate /etc/hosts switching. Code is not of high quality, and I haven’t done any unit tests on it. Most of this so far is file I/O, which I actually don’t have much experience unit testing, so I’ll need to tackle that at some point.

In case anyone is curious I put up what I have so far GitHub - murnux/BlockerDoro: A pomodoro timer that is also a domain blocker

Current Issues

  • The program is very dumb, and does not gracefully exit (e.g. on keyboard interrupt, the original /etc/hosts is not brought back, if the program is stopped during a “work” period). Looks like I may be able to do some program wrapping work to make this happen.

  • Since the program directly interfaces with /etc/hosts, it has to be run as root. This is obviously gross and the whole program does not need root privileges, so I’ll need to think of some ways to work around that.

Next Steps

  • Add a config file to /home/user/.config/blockerdoro for some config options. Right now, this is how the user will have to provide their username for that path, as the program has to be run as root so running homeDir := os.GetConfigDir() just gives root’s local config dir.

  • Somewhat related to above, but add the list of domains to block to this file / timer values. Also support passing in the domains / timer settings as command line args.

  • I’m planning on making a docker-compose file to set up the squid proxy + the Go program for an easy startup / deployment

  • If I’m really feeling motivated, I’ll add a simple GUI (just a web app with the Go program exposing a REST API) for setting timer values / domains to block.

1 Like

I can only think in shell. I’d solve that by enabling passwordless sudo for 1 command (a script or program) that can be run in crontab.

1 Like