TL;DR I like using golinks, which are used at a lot of tech companies. Basically you can alias go/<my custom alias> to point to any website or URL.

I decided to jerry rig such a system for my home network. Here’s how I did it.

  1. Get a raspberry pi. I had an old Raspberry Pi 3B+ lying around, so yay.
  2. Install a DNS server. sudo apt install dnsmasq
  3. Configure DNS server. Here’s my /etc/dnsmasq.conf:
    # To look at the log, try `tail -f /var/log/daemon.log`
    log-queries
       
    # Listen at these addresses.
    listen-address=127.0.0.1
    listen-address=0.0.0.0
    listen-address=192.168.2.123  # Replace with your server's local IP.
       
    # DNS servers run on port 53. If you're using ubuntu to do your DNS server,
    # you have to remove a different process that is run by default.
    # On Raspberry Pi there is no such issue.
    port=53
       
    # Don't constantly poll the config file. 
    no-poll
       
    # Stop DNS server from forwarding reverse-lookup queries that
    # have a local IP range to the upstream DNS servers.
    bogus-priv
       
    # Cache settings.
    neg-ttl=3600
    cache-size=10000
    dns-forward-max=150
       
    # Does not forward names that do not contain a dot (.) or domain name (.com) to upstream nameservers.
    domain-needed
       
    # Tell dnsmasq not to read the `/etc/resolv.conf` file
    # for its upstream nameservers. Instead, just rely on the ones specified in this conf.
    no-resolv
       
    # Use Google's DNS servers upstream.
    server=8.8.8.8
    server=8.8.4.4
    
  4. Configure the dns server’s hosts file /etc/hosts
    # Add this line to make `go` an alias for your server.
    192.168.2.123        go  # Replace  192.168.2.123 with your golink server IP.
    
  5. Make a flask server that catches go/<path> and does whatever (mostly redirect based on a hash table or you can use a SQLite db or something). Here’s mine.
  6. Deploy the flask server. Ports under 1024 require root, so I use nginx to reverse proxy port 80 to port 8080, where my golink flask server actually runs.

    In /etc/nginx/sites-available/default modify the location / block to read as follows:

     location / {
             # Proxy to flask server running on 8080.
             proxy_set_header    Host $host;
             proxy_set_header    X-Real-IP   $remote_addr;
             proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_pass  http://127.0.0.1:8080;
     }
    
  7. Daemonize it / start a service. I used systemd to start a gunicorn instance on boot. Here’s how:

    Add /usr/lib/systemd/system/myservice.service and configure it like this:

    [Unit]
    Description=Golink service
    After=multi-user.target
       
    [Service]
    User=samliu
    WorkingDirectory=/home/samliu/code/dns_redirect
    ExectStart=/home/samliu/.virtualenvs/default/bin/python /home/samliu/.local/bin/gunicorn server:app -b 0.0.0.0:8080
       
    [Install]
    WantedBy=multi-user.target
    

    Then run commands to load the service. Try it out.

    sudo systemctl daemon-reload
    sudo systemctl start myservice
    sudo systemctl status myservice  # Print out the status to see that it's running.
    
  8. Set your router to use the raspberry pi as the DNS server. I made the raspberry pi’s local address a static local IP, then set the DNS config to use my raspberry pi as the primary DNS server. You can set google’s as the secondary, in case your raspberry pi goes offline or dies.

You’ll also want to purge your DNS settings on the local machines so that it takes effect.

Additional step for linux users

This was super annoying to debug. My DNS server worked great for MacOS, but didn’t seem to be respected by Ubuntu.

The culprit was systemd-resolved, which by default uses some other local DNS resolution mechanism. Fortunately you just have to change a symlink to make it respect the router-provided DNS settings:

sudo rm -f /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemd-resolve --flush-caches
	
# Then double check that your network card is picking the correct DNS server
systemd-resolve --status

EDIT, 2022-09-01: In ubuntu 20.04, systemd-resolve was renamed to resolvectl. Try resolvectl flush-caches.

Router setup details

I gave my raspberry pi a static LAN IP, and set the router’s default WAN DNS server to this internal IP. You can additionally or alternatively set the LAN DNS server to the internal IP as well.

Debugging tips

If you want to check if the DNS server is running, try

telnet <my_dns_server_ip> 53

It should output something like this:

Trying <my_dns_server_ip>..
Connected to <my_dns_server_ip>.
Escape character is '^]'.

Blocking productivity killing sites

Now you can block productivity-killing sites across your whole LAN! Simply add an entry to /etc/hosts on the DNS server that redirects your big timewasting sites to your golinks server.