-- Leo's gemini proxy
-- Connecting to rawtext.club:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
===================================================================== ___ __ _ __ ___ _____/ (_)___ / /_(_) /__ / _ \/ ___/ / / __ \/ __/ / //_/ / __/ /__/ / / /_/ / /_/ / ,< \___/\___/_/_/ .___/\__/_/_/|_| /_/ =====================================================================
Over the holidays we got our two younger children HP laptops for them to do their school work on and to have a proper computer. While the schools Google login effectively adds restrictions to Chrome, I still wanted to have some guardrails on their Internet access as well as ad-blocking.
The first thing I did was replace the Windows S install that came on the laptops with Linux Mint[1] and I've always enjoyed the Cinnamon desktop environment[2] and it has a low enough learning curve that the kids could easily pick it up. After installing a few apps and games (OpenRCT2[3]) from the Software Center and setting their own passwords, they were up and running and surfing the world-wide-web.
Finally I added Tailscale[4] to both laptops to get them onto my tailnet[5]. This has benefits of accessing tailnet-only services, easier remote access, and leveraging the dnscrypt-proxy on OpenBSD[6] I setup a few years ago for DNS.
My original DNS config worked well, but I wanted to add some guardrails kids,
1. Cloudflare for Families[7]
2. Ad Blocking[8]
3. YouTube Restricted Mode[9] via Cloaking[10]
4. Accessible only from the Tailscale
First I tried using the existing dnscrypt-proxy[11] to provide a different set of DNS resolvers depending on the source IP, but this wasn't possible. Eventually I came up with a seperate DNS infrastructure in a container for the laptops to use with,
`Dockerfile` is used for building a container including `dnscrypt-proxy`,
FROM debian:trixie-slim ENV DEBIAN_FRONTEND noninteractive RUN apt update && \ apt install -y dnscrypt-proxy \ ca-certificates \ && apt clean WORKDIR /tmp ENTRYPOINT [ "/usr/sbin/dnscrypt-proxy" ] CMD [ "-config", "/etc/dnscrypt-proxy/dnscrypt-proxy.toml" ]
docker compose[12] is used to bring up the stack, which includes a `tailscale` container to provide network and access to other devices on the tailnet. Configuration files are monted read-only from the current directly and some volumes to maintain state across restarts.
`docker-compose.yml`
version: '3.9' services: tailscale: container_name: tailscale-dnscrypt hostname: dnscrypt-proxy image: ghcr.io/tailscale/tailscale stdin_open: true environment: - TS_AUTH_KEY=${TS_AUTH_KEY} - TS_USERSPACE=true - TS_STATE_DIR=/var/lib/tailscale - TS_SOCKET=/var/run/tailscale/tailscaled.sock volumes: - dnscryptvarlib:/var/lib restart: unless-stopped dnscrypt-proxy: build: . stdin_open: true volumes: - ./dnscrypt-proxy.toml:/etc/dnscrypt-proxy/dnscrypt-proxy.toml:ro - ./blocklist.txt:/etc/dnscrypt-proxy/blocklist.txt:ro - ./cloaking-rules.txt:/etc/dnscrypt-proxy/cloaking-rules.txt:ro - ./domains-allowlist.txt:/etc/dnscrypt-proxy/domains-allowlist.txt:ro - keys:/etc/dnscrypt-proxy/keys restart: unless-stopped network_mode: 'service:tailscale' volumes: dnscryptvarlib: keys:
The dnscrypt-proxy configuration uses the `cloudflare-family` source and ad-blocking using a `blocklist.txt` generated with generate-domains-blocklist.py[13]. All logs go to `/dev/stdout` so they appear in `docker compose logs`.
`dnscrypt-proxy.toml`
#Use cloudflare DNS server_names = ['cloudflare-family'] #Listen on local and LAN addresses for DNS listen_addresses = ['127.0.0.1:53'] max_clients = 250 user_name = '_dnscrypt-proxy' #Enable ipv4 and ipv6 ipv4_servers = true ipv6_servers = false #Allow TCP and UDP force_tcp = false timeout = 2500 keepalive = 30 #Logging log_level = 2 use_syslog = true #Certs cert_refresh_delay = 240 dnscrypt_ephemeral_keys = true tls_disable_session_tickets = true #Cache cache = true #Cloaking cloaking_rules = '/etc/dnscrypt-proxy/cloaking-rules.txt' #Query logging, commented out unless for troubleshooting [query_log] file = '/dev/stdout' format = 'tsv' #Sources for resolvers and relays [sources] [sources.'public-resolvers'] urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3 /public-resolvers.md'] minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3' cache_file = '/var/tmp/public-resolvers.md' refresh_delay = 72 #Blocking configuration [blocked_names] ## Path to the file of blocking rules (absolute, or relative to the same directory as the executable file) blocked_names_file = '/etc/dnscrypt-proxy/blocklist.txt' log_file = '/dev/stdout' log_format = 'tsv' #Allow configuration [allowed_names] allowed_names_file = '/etc/dnscrypt-proxy/domains-allowlist.txt
Cloaking[14] is used to set all requests to YouTube to go `restrict.youtube.com`.
`cloaking-rules.txt`
www.youtube.com restrict.youtube.com m.youtube.com restrict.youtube.com youtubei.googleapis.com restrict.youtube.com youtube.googleapis.com restrict.youtube.com www.youtube-nocookie.com restrict.youtube.com
Since I only want DNS available to devices on my tailnet, there's a `tailscale` container in the `docker-compose.yml` that provides networking to the `dnscrypt-proxy` container using `network_mode`.
Set this up by creating an auth key[15] for your tailnet and then putting it into a `.env` file that docker compose will source in and set as the `TS_AUTH_KEY` variable.
`.env`
TS_AUTH_KEY=tskey-auth-xxxxxxxxxxx
My tailnet uses Magic DNS[16] which sets the nameserver for all devices on a tailnet to `100.100.100.100`, but since this is a DNS server specific to a subset of systems we want to use the IP of the `dnscrypt-proxy` device instead.
After bringing up the stack with `docker compose up`, the `tailscale` container will authenticate to the tailnet and have an Tailscale IP (eg `100.112.129.40`). This IP is then added to the laptops `/etc/resolv.conf`,
nameserver 100.112.129.40 search tailnet-3831.ts.net
Tailscale will try and revert this however, so to keep the settings permanent set `/etc/resolv.conf` to immutable with `chattr +i /etc/resolv.conf`.
Test that DNS is working by looking for more "adult" content on youtube and it should give a message similar to "your Google workspace administrator has restricted some content".
Verify this in the container logs by doing a `dig m.youtube.com @100.76.233.91` (where the IP is your Tailscale container IP) and check the logs for messages similar to `127.0.0.1 m.youtube.com A CLOAK 0ms -`.
____________________________________________________________________
-- Response ended
-- Page fetched on Sun Apr 28 13:42:39 2024