My kids lie about the internet when they argue

Warning! This is techy mumbo jumbo linux-howto-article!

To get a translation of this text, hide the swedish version and click on the english version.

Svensk Version

Det enda som verkligen tycks betyda något i barnens liv, bortsett från de gånger de faktiskt uppskattar att träffa vänner och gå ut, är internet. Därför har jag under lång tid använt internet som ett tydligt styrmedel: sköter man sina åtaganden är internet öppet. Sköter man inget alls är internet helt stängt.

Det fungerar för det mesta. Problemet uppstår när det blir bråk.

I de lägena dyker det ofta upp påståenden om att internet absolut behövs för skolarbete. Det är inte särskilt troligt mitt under ett lov, men det händer faktiskt att skolarbeten måste göras även då. Ibland handlar det inte ens om skola, utan om något så basalt som att någon behöver kunna somna med ljud i hörlurarna.

Så hur gör man då?

Den enkla lösningen är inte att antingen stänga allt eller släppa allt. Lösningen är att begränsa internet på personnivå.

Problemet är att de flesta färdiga brandväggslösningar och föräldrakontroller som klarar detta på riktigt ofta är dyra, inlåsta eller alldeles för grova. DNS-baserade lösningar räcker inte heller i ett hushåll där flera personer delar samma uppkoppling. Då måste man kunna begränsa per individ, inte per nätverk.

Det var här behovet uppstod på riktigt. Inte minst eftersom Emily aktivt försökte hitta kryphål till internet.

Vad jag faktiskt har byggt

Jag har byggt ett system där varje person i hushållet behandlas individuellt på nätverksnivå. Varje enhet har ett fast internt IP och knyts till en person. Utifrån det kan internet styras i tre tydliga lägen:

  • Full tillgång: allt är öppet
  • Avstängt: all trafik stoppas helt
  • Begränsat: internet är på, men utvalda tjänster blockeras

Den begränsade nivån är den intressanta. I stället för att försöka filtrera innehåll via DNS eller appar används brandväggsregler som blockerar hela nätblock (CIDR-ranges) för specifika tjänster som spelplattformar och streaming. Det gör det betydligt svårare att kringgå, eftersom det inte räcker att byta DNS, app eller domän.

För att detta ska vara hanterbart i praktiken har jag byggt ett eget styrscript. Med ett enda kommando kan jag slå på, stänga av eller begränsa internet för en specifik person eller till och med en specifik enhet. Status går alltid att se, och systemet överlever både omstarter och nätverksändringar.

Resultatet är att argumenten om “jag behöver internet till skolan” inte längre automatiskt innebär fritt spelrum. Internet kan vara öppet där det faktiskt behövs, samtidigt som det som orsakar konflikterna hålls borta.

Det här är ingen kommersiell produkt och inget universallösning. Det är ett tekniskt svar på ett väldigt vardagligt problem i ett hushåll där internet blivit en central del av allt.

Hur ser det ut?

Här är en förenklad bild av hur det fungerar i praktiken. Inget är magiskt: allt bygger på fasta interna IP per enhet, en liten state-fil per person och en apply-slinga som lägger iptables-regler.

1. Fasta IP per enhet (DHCP)

Varje relevant enhet får en fast adress i DHCP (MAC -> fixed-address). Exempel:

host Emily {
  hardware ethernet <mac>;
  fixed-address 10.1.1.53;
}

host SkolEmily {
  hardware ethernet <mac>;
  fixed-address 10.1.1.51;
}

host SkolEmily2 {
  hardware ethernet <mac>;
  fixed-address 10.1.1.52;
}

2. En konfigurationsfil med domäner som ska kunna blockas

Filen /var/tornevall/system/etc/resolver/iplist.conf innehåller bara hostnames/domäner, en per rad:

roblox.com
www.roblox.com
youtube.com
www.youtube.com
steamcommunity.com
store.steampowered.com

3. Resolver som gör om domäner till CIDR-ranges

Resolvern är ett separat hjälpscript som heter resolvecidr. Dess enda uppgift är att ta en lista med domäner och översätta dem till hela nätblock (CIDR-ranges).

Detta är nödvändigt eftersom stora tjänster använder många IP-adresser för redundans och lastbalansering. Att blockera en enskild IP-adress är i praktiken meningslöst; man måste blockera hela det nät som tjänsten är tilldelad.

Resolvern arbetar i tre steg:

  1. Slår upp en eller flera IPv4-adresser för varje domän
  2. Kör whois på varje IP-adress
  3. Plockar ut hela CIDR-rangen från registry-datat

En förenklad version av scriptet ser ut så här:

#!/bin/bash
set -e

CONF="/var/tornevall/system/etc/resolver/iplist.conf"
OUT="/var/tornevall/system/etc/resolver/iplist.resolved"

tmp=$(mktemp)

while read -r host; do
    [ -z "$host" ] && continue
    [[ "$host" =~ ^# ]] && continue

    for ip in $(getent ahostsv4 "$host" | awk '{print $1}'); do
        whois "$ip" | awk '/^CIDR:/ {print $2}'
    done
done < "$CONF" | tr ',' '
' | sort -u > "$tmp"

mv "$tmp" "$OUT"

Resultatet skrivs till /var/tornevall/system/etc/resolver/iplist.resolved och innehåller enbart CIDR-ranges:

128.116.0.0/17
142.250.0.0/15
172.217.0.0/16
216.58.192.0/19
23.0.0.0/12

Det är den här listan som kopieras in i personens state-fil när man aktiverar strict.

4. State per person

Varje person har en state-fil:

  • /var/tornevall/system/etc/resolver/state-emily
  • /var/tornevall/system/etc/resolver/state-max
  • /var/tornevall/system/etc/resolver/state-thomas

Tom fil = full tillgång. Thomas = Test-state. Man bör inte blocka sig själv dock.

När man kör strict kopieras iplist.resolved in i personens state-fil.

5. Styrning med ett kommando

Scriptet kan slå av/på per person eller per enhet:

# Status
nethandle

# Stäng allt internet för Emily
nethandle emily off

# Släpp på internet men blocka valda tjänster (CIDR-listan)
nethandle emily on strict

# Bara en enhet (Antilopen) får strict, övriga kan få andra lägen
nethandle emily-antilopen on strict

# Full tillgång igen (tömmer state)
nethandle emily on

6. Apply-fasen (iptables)

En separat apply-komponent körs efter varje ändring och vid boot. Den läser state-filerna och skapar:

  • en kedja per person, t.ex. NH_EMILY
  • en JUMP-regel i FORWARD per intern IP som ska styras
  • DROP-regler i kedjan för varje CIDR i state-filen

Förenklat ser det ut så här:

Chain FORWARD
NH_EMILY all -- 10.1.1.23 0.0.0.0/0
NH_EMILY all -- 10.1.1.52 0.0.0.0/0
NH_EMILY all -- 10.1.1.51 0.0.0.0/0
NH_EMILY all -- 10.1.1.53 0.0.0.0/0

Chain NH_EMILY
DROP all -- 0.0.0.0/0 128.116.0.0/17
DROP all -- 0.0.0.0/0 142.250.0.0/15
...

Poängen är att blocken blir “per person” eftersom bara just de interna IP-adresserna hoppar in i kedjan.

English version

The only thing that really seems to matter in my children’s lives, apart from the times when they actually appreciate meeting friends and going out, is the internet. Because of that, for a long time I have used internet access as a clear tool for control: if you take care of your responsibilities, the internet is open. If you take care of nothing at all, the internet is completely shut off.

This works most of the time. The problem arises when arguments happen.

In those situations, claims often appear that the internet is absolutely necessary for schoolwork. That is not particularly likely in the middle of a school break, but it does in fact happen that school assignments need to be done even then. Sometimes it is not even about school, but about something as basic as someone needing to be able to fall asleep with audio in their headphones.

So how do you deal with it?

The simple solution is not to either shut everything down or let everything through. The solution is to limit internet access on a per-person basis.

The problem is that most off-the-shelf firewall solutions and parental control systems that can actually do this properly are often expensive, locked down, or far too coarse. DNS-based solutions are not sufficient either in a household where several people share the same connection. In that case, you need to be able to limit per individual, not per network.

That was where the need truly emerged. Not least because Emily actively tried to find loopholes to get internet access.

What I have actually built

I have built a system where each person in the household is handled individually at the network level. Each device has a fixed internal IP address and is tied to a person. Based on that, internet access can be controlled in three clear modes:

  • Full access: everything is open
  • Off: all traffic is completely blocked
  • Limited: the internet is on, but selected services are blocked

The limited mode is the interesting one. Instead of trying to filter content via DNS or apps, firewall rules are used to block entire network blocks (CIDR ranges) for specific services such as gaming platforms and streaming. This makes it significantly harder to circumvent, because it is not enough to change DNS, an app, or a domain.

To make this manageable in practice, I have built my own control script. With a single command, I can turn internet access on, off, or limit it for a specific person or even a specific device. Status is always visible, and the system survives both reboots and network changes.

The result is that arguments like “I need the internet for school” no longer automatically mean free rein. The internet can be open where it is actually needed, while what causes the conflicts is kept out.

This is not a commercial product and not a universal solution. It is a technical response to a very everyday problem in a household where the internet has become a central part of everything.

What does it look like?

Here is a simplified picture of how it works in practice. Nothing is magical: everything is based on fixed internal IPs per device, a small state file per person, and an apply loop that installs iptables rules.

1. Fixed IPs per device (DHCP)

Each relevant device gets a fixed address in DHCP (MAC -> fixed-address). Example:

host Emily {
  hardware ethernet <mac>;
  fixed-address 10.1.1.53;
}

host SchoolEmily {
  hardware ethernet <mac>;
  fixed-address 10.1.1.51;
}

host SchoolEmily2 {
  hardware ethernet <mac>;
  fixed-address 10.1.1.52;
}

2. A configuration file with domains that can be blocked

The file /var/tornevall/system/etc/resolver/iplist.conf contains only hostnames/domains, one per line:

roblox.com
www.roblox.com
youtube.com
www.youtube.com
steamcommunity.com
store.steampowered.com

3. Resolver that turns domains into CIDR ranges

The resolver is a separate helper script called resolvecidr. Its sole purpose is to take a list of domains and translate them into entire network blocks (CIDR ranges).

This is necessary because large services use many IP addresses for redundancy and load balancing. Blocking a single IP address is practically meaningless; you have to block the entire network that the service is assigned.

The resolver works in three steps:

  1. Looks up one or more IPv4 addresses for each domain
  2. Runs whois on each IP address
  3. Extracts the full CIDR range from the registry data

A simplified version of the script looks like this:

#!/bin/bash
set -e

CONF="/var/tornevall/system/etc/resolver/iplist.conf"
OUT="/var/tornevall/system/etc/resolver/iplist.resolved"

tmp=$(mktemp)

while read -r host; do
    [ -z "$host" ] && continue
    [[ "$host" =~ ^# ]] && continue

    for ip in $(getent ahostsv4 "$host" | awk '{print $1}'); do
        whois "$ip" | awk '/^CIDR:/ {print $2}'
    done
done < "$CONF" | tr ',' '\n' | sort -u > "$tmp"

mv "$tmp" "$OUT"

The result is written to /var/tornevall/system/etc/resolver/iplist.resolved and contains only CIDR ranges:

128.116.0.0/17
142.250.0.0/15
172.217.0.0/16
216.58.192.0/19
23.0.0.0/12

This is the list that is copied into a person’s state file when strict is activated.

4. State per person

Each person has a state file:

  • /var/tornevall/system/etc/resolver/state-emily
  • /var/tornevall/system/etc/resolver/state-max
  • /var/tornevall/system/etc/resolver/state-thomas

An empty file means full access. Thomas = test state. You should not block yourself.

When strict is used, iplist.resolved is copied into the person’s state file.

5. Control with a single command

The script can turn access on or off per person or per device:

# Status
nethandle

# Shut down all internet for Emily
nethandle emily off

# Allow internet but block selected services (CIDR list)
nethandle emily on strict

# Only one device (Antilopen) gets strict, others can have different modes
nethandle emily-antilopen on strict

# Full access again (clears state)
nethandle emily on

6. Apply phase (iptables)

A separate apply component runs after every change and at boot. It reads the state files and creates:

  • one chain per person, for example NH_EMILY
  • a JUMP rule in FORWARD per internal IP that should be controlled
  • DROP rules in the chain for each CIDR in the state file

Simplified, it looks like this:

Chain FORWARD
NH_EMILY all -- 10.1.1.23 0.0.0.0/0
NH_EMILY all -- 10.1.1.52 0.0.0.0/0
NH_EMILY all -- 10.1.1.51 0.0.0.0/0
NH_EMILY all -- 10.1.1.53 0.0.0.0/0

Chain NH_EMILY
DROP all -- 0.0.0.0/0 128.116.0.0/17
DROP all -- 0.0.0.0/0 142.250.0.0/15
...

The point is that the blocks become “per person” because only those specific internal IP addresses jump into the chain.


Discover more from Tornevalls

Subscribe to get the latest posts sent to your email.