Logo Computer scientist,
engineer, and educator
• Articles • Articles about computing • Articles about Linux

Using a Linux box as an Internet gateway, to enforce parental control over Internet access

This is an issue that I've been discussing a lot lately — most of my friends and acquaintances have teenage children, as I do. Like many kids of that age, mine have smartphones, tablets, and laptops; the days seem to be long gone when we all shared a desktop PC in a family room. A lot of teenagers and, I suspect, many adults have a deep fascination with social networking services like Facebook and Instagram, although I find it hard to see the appeal myself. For my part, I wouldn't want to prevent my kids having access to these things completely, although that's easy enough to enforce; but I do worry about the number of hours that can easily be wasted on such activities, when the awful spectre of GCSE examinations is lurking just up ahead.

Consequently, I'm sharing my approach to time-based filtering of Internet access, in the hope that it will be of interest to other parents with a similarly geeky interest in computers (if there are any). In summary, my method involves using a Linux box to filter all network traffic in and out of the house. Any Linux box will do: I'm using an old laptop which also works as a local file/Web server. In principle, this would be an ideal application for a Raspberry Pi, although I haven't tried it.

My approach has the following advantages:

  • It is device-independent — it works on any kind of computer, smartphone (with some limitations), or tablet, regardless of operating system.
  • With one slight exception for smartphones, it requires no set-up or software on the individual devices.
  • It is extremely difficult to circumvent, even by the most technologically-adept teenagers.
  • It is highly flexible — there's no problem with enabling Internet access for different lengths of time on different days for different people, for example.
  • It can be Web-enabled, allowing non-technical parents to control access.
  • It won't interfere with any Web-based or network services that are hosted inside the home.
The disadvantages are:
  • It requires a relatively high level of Linux knowledge to set up, and it's very difficult to reduce the process to a set of step-by-step instructions.
  • You need a Linux box running all the time or, at least, all the time for which Internet access is required in the household.
  • Smartphones present a specific problem, because they might have alternative ways than wifi to get to the Internet.
  • It irritates the heck out of your kids (but probably not as much as taking all their toys away would)
Note that I'm only discussing time-based control over general Internet access here. Content filtering — restricting access to specific sites is a different matter. I use the free service from OpenDNS for this. The set-up I use for time-based filtering automatically assigns OpenDNS as the DNS server for all devices on the home network — more on this later.

Some modern, top-of-the-range commercial modem/routers already have some built-in capabilities for time-based filtering; it's worth checking this out before embarking on a complex technical set-up that might not actually be necessary.

This article assumes a reasonable knowledge of Linux in general, and Linux networking in particular. Nothing in this article is arcane or novel — it's all well-documented, but figuring out all the different places you need to look for information can be time-consuming.

Overview

Home networks are all different, so it's impossible to give specific, step-by-step instructions on how to set up time-based filtering. Any implementation will need to address each of the following points; the remainder of this article describes my approach to each of these. I can't promise that any of this will work in a different environment, but most likely some will, with a bit of tweaking.
  • Ensure that all Internet-bound network traffic is routed to the Linux box (or, at least, all traffic from devices that need to be filtered).
  • Arrange for such traffic to be routed to the 'real' Internet router or modem.
  • Create firewall rules on the Linux box to ensure that devices which have to be restricted can be restricted — and corresponding rules to relax the restrictions when appropriate.
  • Encapsulate the setting of the rules into shell scripts, for ease of use.
  • Use cron or some other scheduler to run the scripts at appropriate times of day.
  • Optionally, provide some user-friendly alternative to shell scripts to set or relax the restrictions outside the set times. For example, arrange for the scripts to be invoked by a CGI invocation from a password-protected page on a local Web server (Apache, perhaps).
  • Take such steps are practicable to prevent the system being bypassed, either deliberately or accidentally.

Routing Internet traffic to the Linux box

The first step is to find a way to ensure that all Internet-bound traffic goes to the Linux box; we can't filter traffic that we don't see. The Linux box will then forward traffic to the real Internet modem/router if it is allowed.

One easy way to get all traffic to the Linux box is to set up all the devices manually with a new gateway IP. This process is usually associated with setting a static IP number of the device. In practice we need static IP numbers for all devices whose traffic is to be filtered — management is just too fiddly otherwise. So this very simple approach might be appropriate in some circumstances.

There are a number of problems, however. The most obvious is that this set-up has to be done on every device; it can't be centrally managed. Worse, some devices simply don't allow a static IP number to be set (It's a problem in Android 2.3 devices, for example, of which there are still a number). On smartphones, it might not be possible to set a static IP for wifi use without setting a static IP for external, service-provider networking, and that would break things.

Most Internet-enabled devices get their connection properties (IP, gateway, etc) via DHCP. Commercial routers usually offer a DHCP server, which will assign IP numbers in a specifi range. However, in most cases, commercial routers do not allow for any configuration of the properties that are set for a specific device. A better approach is simply to turn off the DHCP server in your router, if it has one, and implement it on the Linux box using dhcpd.

The dhcpd DHCP server is available for all common Linux distributions, and is easy to set up. In general, it requires one configuration file, usually located at /etc/dhcp/dhcpd.conf. A little-documented feature of dhcpd is that it is able to assign fixed IP numbers to known hardware MAC addresses, and this is a crucial part of this system. Here is a sample dhcpd.conf file.

default-lease-time 1000000;
max-lease-time 1000000;
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option routers 192.168.1.2;
option domain-name-servers 208.67.222.222, 208.67.220.220;
option domain-name "homenetwork";

subnet 192.168.1.0 netmask 255.255.255.0 {
   range 192.168.1.100 192.168.1.150;
}

#Static IP definitions

host fred-laptop {
  hardware ethernet C4:88:E5:FE:2A:48;
  fixed-address 192.168.1.60;
}

host mary-phone {
  hardware ethernet BC:47:60:9C:74:BB;
  fixed-address 192.168.1.61;
}

#More IP definitions here...
There are several things to note about this configuration.
  • The routers statement ensures that any device that gets its IP settings from this DHCP server will be told to use the router at (in this example) 192.168.1.2. This IP is not the IP of the main Internet router/modem — it is the IP of a virtual port that will be set up (in the next step) on the Linux box to accept IP traffic for filtering.
  • domain-name-servers points, in this case, to the OpenDNS servers. This ensures that any device that gets its settings from this DHCP server will use OpenDNS for name resolution, with the content filtering that implies. No other set-up is necessary to enforce the use of OpenDNS on the network.
  • The range setting indicates the range of IP numbers that will be made available for devices that are not specifically listed as a host entry. It is important that the IP numbers used here do not overlap with the IP numbers in any host entry.
  • Each host entry assigns a (different) IP number to each device on the network. For this to work, you'll need the hardware MAC address. On a Windows PC, run the command ipconfig /all in a DOS box, and hunt through the output for the relevant value (there might be more than one, if the PC uses both wired and wireless Ethernet.) For Android smartphones, the value can usually be found using the Settings applet, in the "About..." section. For Apple products, you have my sympathy ;)
It's advisable to test this set-up before proceeding with the next step. set the router setting (for now) to point at the main Internet router/modem, and check that your devices get the appropriate static IP number when making a new network connection. One thing to watch out for — if you're using iptables or some other software firewall: you'll need to ensure that the multicast packets that DHCP uses are not blocked from reaching the dhcpd server. More on firewall configuration later.

Arrange for Internet traffic to be routed to the 'real' Internet router or modem

Once we've got devices to route their Internet traffic to the Linux box, we then need to ensure that the Linux box can forward it to the real modem/router. It's best to check this with no restrictions in place first, because the restrictions add another level of complexity.

I'm assuming that your Linux box already has working access to the Internet via a modem/router; it doesn't matter whether this access is wired or wireless, so long as it works. I'm assuming that the real modem/router is at IP 192.168.1.1, as this is the default for most commercial products. I'm assuming that the Linux machine has a default route to this IP.

So what we need to do is to assign another IP to this Linux machine — I've assumed above that it is 192.168.1.2 — and arrange for packets sent to 192.168.1.2 to be forwarded to 192.168.1.1 (eventually with restrictions but, for now, all packets).

To enable forwarding of packets at all, we need to configure it at the kernel level. In most systems, the appropriate setting is in /etc/sysctl.conf:

net.ipv4.ip_forward = 1
Alternatively, arrange for the following command to be executed at boot time:
echo 1 > /proc/sys/net/ipv4/ip_forward
You'll also need to provide the additional IP number 192.168.1.2; in an industrial-grade set-up you'd probably want to install a separate network adapter but, for home use, a virtual IP should be sufficient. If the main network adapter is eth0, then you need:
ifconfig eth0:1 192.168.1.2 up
This command, like most of those that follow, need to be executed at every boot — they are not persistent. My approach is simply to wrap them up in a shell script and call the shell script from /etc/rc.local; of course, more sophisticated approaches exist.

Simply forwarding packets between two network interfaces, however, is not enough — we need a system in place to apply restrictions, and we need to do Network Address Translation (NAT) if the network consists of more than one device. Both these requirements are taken care of using iptables. This is a software firewall, but it also has routing capabilities.

Describing how to set up iptables is a bit tricky, because most common Linux distributions provide their own fancy ways of doing it. All the operations with which we are interested here affect the default FORWARD and POSTROUTING chains, which are not usually used for anything but routing. I'm assuming, therefore, that it's safe to manipulate these chain using command-line tools, and that you already have a basic iptables set-up in place, which is taking care of the routine firewall needs. In fact, none of the following depends on iptables being set up, so long as the relevant kernel modules are loaded.

To enable forwarding of (all) packets, with NAT, we need to set up the FORWARD and POSTROUTING chains as follows:

# Remove all rules from the FORWARD chain
iptables -F FORWARD
# Enable NAT for IP 192.168.1.2
iptables -t nat -A POSTROUTING -o eth0:1 -j MASQUERADE
# Enable forwarding between 192.168.1.1 and 192.168.1.2
iptables -A FORWARD -i eth0:1 -o eth0 -j ACCEPT
Again, it's worth testing this configuration before applying any restrictions. With these settings, all traffic sent to 192.168.1.2 should be forwarded with NAT applied.

On a side note: doing this stuff on a Linux box does make any existing Internet router at least partially redundant; that is, a modem/router ends up behaving more-or-less like a simple modem.

Create firewall rules

To apply restrictions to the flow of traffic through the Linux box, we can create additional firewall rules. To be honest, I find the detailed interpretation of iptables rules rather opaque, and I've used a fair amount of trial-and-error to get the behaviour I need. In general, we need to install rules to drop packets from disallowed devices before the rules that accept all packets for forwarding; but we probably want to ensure that existing connections are not brutally terminated when the allowed time period expires (otherwise you might cut off downloads in the middle, which is a bit harsh).
iptables -F FORWARD
iptables -t nat -A POSTROUTING -o eth0:1 -j MASQUERADE

# Retain established connections
/sbin/iptables -A FORWARD -i p255p1 -o p255p1:1 -m state --state RELATED,ESTABLISHED -j ACCEPT

# Drop all packets from disallowed devices, other than established connections
/sbin/iptables -A FORWARD -s 192.168.1.60 -j DROP
/sbin/iptables -A FORWARD -s 192.168.1.61 -j DROP
# And more...

# Accept everything else
iptables -A FORWARD -i eth0:1 -o eth0 -j ACCEPT
-j DROP does what it sounds like — affected packets are simply ignored. For the end user, the result is the same as unplugging the network cable, or switching off the wifi radio.

Wrap the rules in scripts

Depending on the complexity of your setup, this step might require some ingenuity. It's fiddly to insert and remove iptables rules dynamically, because you have to figure out how the existing rules are numbered. It's easy enough for a human to do, but it's hard (in my experience) to write a reliable script to do it without human intervention. In practice, it's easier to build the whole setup of the FORWARD chain from scratch, inserting rules to block access to the disallowed devices according to need. A way to do this might be to maintain the list of disallowed devices in (for example) a text file, and then use a variant of the script above to iterate the text file and execute an iptables -j DROP command for each one. Then, to allow or disallow a specific devices, you'd update the list (using scripts, or whatever), and then run the script that builds the FORWARD chain from that list.

My needs are (at present) simpler — I just want to allow and disallow access to a whole set of devices all at once; I don't need fine control over individual devices. So I just use variants of the two scripts shown above: kiddie_internet_on.sh to allow access to everything, and kiddie_internet_off.sh to disallow it to the kid's devices.

Hint: the iptables command is probably in the /sbin/ directory, which probably won't be in the PATH for all user environments. If you plan to automate the invocation of your scripts, using the full path /sbin/iptables for the command.

Schedule scripts to run automatically at certain times of day

Once you've got shell scripts to switch Internet access of and on for the relevant devices, the next step is likely to be to automate that switching. I'm using the ordinary cron daemon for that; cron is controlled using yet another rather opaque text file and, as for iptables, some Linux distributions provide nice tools for simplifying that task. At a pinch, you can just execute crontab -e (as root, probably), and use the default text editor to manipulate the file. Editing the file directly is not recommended, as cron will not necessarily update itself from the file.

Here are a couple of cron rules to turn internet access on between 2pm and 4pm Saturday:

0 14 * * 6 kiddie_internet_on.sh
0 16 * * 6 kiddie_internet_off.sh
The time specifications are minute,hour,day,month,day-of-week; the '*' character matches any month or day in this example.

Using cron you can easily allow or disallow Internet access for specific periods each day, and these can be different on different days if desired.

Implementing user-friendly controls

To be a Linux geek and a parent is unusual enough; having more than one adult in a family who is a Linux geek must be exceedingly rare. So a need might well arise to provide a user-friendly way to turn Internet access on and off outside the scheduled times. It might even be interesting to provide a way for a non-technical person to edit the time rules, but such a thing is completely beyond the scope of this article. Happily, using a Linux Web server like Apache, at least running the scripts to turn Internet access on and off from a Web page is relatively straightforward.

Assuming that you have Apache installed and running, you need first to ensure that CGI script support is enabled, and that you know which directories to insall scripts in, and apply the appropriate password controls — see here for details on these points. Some sort of password protection is necessary, otherwise your kids will very quickly work out how to use a Web browser to overcome your Internet restrictions.

To run a script from a Web browser typically involves invoking a URL of the form

http://mylocalwebserver/cgi-bin/myscript.sh
These invocations can be placed on a (password protected) Web page on the server, attached to ordinary hyperlinks. The scripts you invoke this way should not be the ones I referred to above, that manipulate the iptables rules, for two reasons. First, manipulating iptables requires root privileges, and Apache is probably not running as root (at least, it should not be). Second, the script invoked using the CGI mechanism must produce output in a very specific format, or Apache will generate an error response to the browser (although the script might still run). Instead, the script to be invoked using Apache should write the appropriate output, and then use sudo to run the 'real' script (and, of course, you'll need to have configured sudo to allow Apache to run the relevant script, and without supplying a password, which is whole other session with the man pages). The output expected by Apache from the script is described in the link referred to above, in the section entitled "Writing a CGI program".

Preventing bypass of the system

The main weakness in the system I've described here applies to smartphones — it's a weakness that is inherent in the very nature of smartphones. The problem is that if you block wifi-based Internet access on a smartphone, it will revert to using cellular internet access; this will not only bypass your controls, but will possibly increase phone charges.

Some mobile networks (few, in the UK) allow parental restriction on the times at which kids can use their phones for Internet access. Without such a service, the only way to prevent smartphones bypassing your access controls is to switch off cellular networking on the handset. Even if you can fool the handset into thinking that the wifi-based Internet connection is actually valid when it is not — and this requires elaborate work with iptables, not to mention providing local proxies for DNS, etc — you still can't prevent the user simply switching off wifi on the handset. In such cases, the handset will use cellular Internet access via G3/GPRS/whatever, unless you've specifically disabled this.

Disabling celluar Internet access completely works for my situation — my kids don't need Internet access on their phones outside the home. If they find a way to re-enable it, they'll find pretty quickly that a couple of hours' fiddling with YouTube exhausts their contract's cellular bandwidth allowance in a matter of days, and the problem will naturally resolve itself, albeit it in a harsh way. Nevertheless, the system I'm using will probably be unsuitable for any smartphone that runs on a contract that does not cap cellular Internet charges.

If your kids are very technically knowledgeable, they might be able to bypass the system I describe by setting static IP addresses in their toys. Usually, such a setting allows a specific gateway to be configured as well, and there's nothing to stop smart kids bypassing your filter by entering the IP of the real modem/router.

Happily, such a problem can usually be resolved in the modem/router itself. Normally, you can specify that only certain client IPs, or certain MAC addresses, are allowed access to the Internet, so you can specify that your Linux box that is acting as the router is allowed access, and nothing else is.

Summary

Like many parents, I'm concerned about the amount of time my children spend hunched over their computers. However, I'm in no position to claim that all use of computers is bad, or should be restricted, since I spend much of every day hunched over mine — it's how I make a living. Restricting Internet access to certain times of day — and further restricting what Web sites may be viewed — may be a workable compromise. It's certainly a less stressful approach than constantly struggling to keep track of where all the Internet-enabled gadgets are.

Restricting Internet access doesn't completely stop kids using their toys for time-wasting purposes, but it certainly limits it. It's serendipitous that many free computer and tablet games are ad-supported, and therefore don't run for long when there is no Internet connection to suck ads from. So restricting Internet access has a happy side-effect of limiting access to other pointless computer-based activities.

Yes, it would be nice in a way if we could go back to a time where we all shared a single computer in the family room; but that really isn't the world we live in any more.

Copyright © 1994-2013 Kevin Boone. Updated Nov 18 2013