Build a Linux Router from an Old Laptop

Recently, I found myself in a situation where I needed to quickly replace a broken router. In times like these, you must improvise and think on your feet…

The device in question was an old, rusty but trusty IBM ThinkPad T60. This particular machine has a special history for me. I salvaged it from a pile of e-waste at my old job as a PC tech many years ago, and that summer it was my playground for learning Linux ultimately starting the career which I currently enjoy. Over the years it’s run Mint, Ubuntu, OpenSUSE, Fedora, Arch, even Gentoo, as painful as compiling a kernel on a Celeron CPU was.

But tonight, it becomes a router.

Now, hear me out, laptops have a few very nice features. Even older devices like my T60 use very little power, and create very litle noise. Second is that it essentially has a built in power backup. Even with a well aged battery, there is a power backup that will keep the device online well after my UPS takes down my KVM servers.

Network Configuration

Since my device only has one enternet interface, I needed to use VLANs to trunk in both my internal and external networks.

Luckily, I do have a layer-2 switch that can handle VLAN ingress and egress tagging. I have configured the ports like this:

Port Untagged VLAN Tagged VLAN Label
1 - 22 10 Internal Network
23 999 10, 666 Trunk to Router
24 666 CPE Device

In this configuration, I am using VLAN 666 to carry WAN traffic from the CPE cable modem through to the firewall, where it is NAT’ed and routed to the internal network, which is then accessed through VLAN 10. In reality, I have many more VLANs in my actual network but for simplicity I am only including one here.

Once this is set up and tested on the physical and link layer, it’s time to set up the router itself.

Install Debian

The device should be very reliable and stable, making Debian Stable a great fit. At the time of this writing Buster is on 10.1, so stable enough that I feel confident running my network through it.

Storage requirements for a router are obviously quite minimal, so when installing I opted for LVM with seperate volumes for /home, /var, /tmp, and /var/log. The advantage of this is that since logs are stored on a seperate ‘device’, if there is a burst of logs it won’t take down the entire system the way a system with a single disk would. Again, good for stabillity since this device may not get as much maintenance as one of my application servers.

Requirements

To achieve the basic functions of a typical home router, we need to accomodate a few needs that network devices will have:

  1. NAT (PAT) translation from inside->outside to allow internet access
  2. Basic stateful firewall functionality
  3. DHCP addressing on the local network
  4. DNS nameserver
  5. OpenVPN remote access server

There are a few additional features I would like to add on for more security and advanced network functions, but this is a good start.

Setting it up

To set up the router there are a few tasks:

  • Configure the network stack
  • Configure the firewall
  • Install a isc DHCP server
  • Install a bind9 DNS server
  • Create server/client certificates with EasyRSA
  • Configure OpenVPN server & client profiles

Network Stack

First off, we have to set up the linux server with VLANs and IP addressing.

VLAN IP address Label
10 10.99.10.1 Inside
666 dhcp Outside

Since I’m using Debian, this info goes into the /etc/network/interfaces file:

auto eth0 

auto eth0.666
iface eth0.666 inet dhcp 
    vlan_raw_device eth0 

auto eth0.10
iface eth0.10 inet static 
    address 10.99.10.1/24
    vlan_raw_device eth0 

This will ensure that we will get an IPv4 address on the WAN subinterface, and we use a static interface on the local network to provide the gateway to intranet devices.

Next, reload the network service. This will trigger the interface scripts to set up the vlans and request a public address from the ISP.

sudo systemctl restart networking 

The other key configuration to make on the linux network stack is enabling IP forwarding, allowing the kernel to process packets destined for hosts on other networks. On most distributions, this is in the /etc/sysctl.conf file:

net.ipv4.ip_forward=1

Once this file has been modified, reload the sysctl config:

sudo sysctl --system

Firewall & NAT Configuration

At this point, we should be able to ping out to various well known public IPs (1.1.1.1, 8.8.8.8, 4.2.2.2, etc). If not, double check the configuration of the interfaces and network stack.

A base Debian install already comes with iptables out of the box, but adding some helper scripts will make it simpler to maintain:

sudo apt install iptables-persistent 

Then the config can be added to the /etc/iptables/rules.v4 file:

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A INPUT -j LOG  --log-prefix "INPUT:DROP "
-A INPUT -j REJECT --reject-with icmp-host-unreachable
-A FORWARD -m state --state RELATED,ESTABLISHED  -j ACCEPT
-A FORWARD -o eth0.666 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -j LOG  --log-prefix "FORWARD:DROP "
-A FORWARD -j REJECT --reject-with icmp-host-unreachable
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A POSTROUTING -o eth0.666 -j MASQUERADE
COMMIT

This ruleset implements a basic stateful firewall, where all existing connections will be permitted back in the direction where they came. In this example, packets in the forward chain will be allowed to exit the network through the eth0.666 interface, then since they are tracked by conntrack allowed to come back into the network.

Finally, there is a rule added to the NAT table to perform port address translation on the outside interface eth0.666. This allows all the devices in the network to share a single public address, a requirement for a typical home router use case.

Once configured, the rules can be loaded to take effect:

sudo iptables-restore /etc/iptables/rules.v4

DHCP Server

The next requirement for the router is to supply all internal devices with IPv4 address.

First, install the required packages:

sudo apt install -y isc-dhcp-server

Note that this service ships without a default config, and will immediately fail to start. This is okay, since we will add our own config file.

/etc/dhcp/dhcpd.conf

option domain-name "example.com";
option domain-name-servers 10.99.10.1;

default-lease-time 600;
max-lease-time 7200;

ddns-update-style none;

subnet 10.99.10.0 netmask 255.255.255.0 {
  authoritative;
  range 10.99.10.50 10.204.9.250;
  option routers 10.99.10.1;
}

Now that it is configured, the service can start:

sudo systemctl enable --now isc-dhcp-server 

This command will also set the service to start on next boot.

DNS Server

Next is a simple DNS resolver for the network devices to use. I personally like to use bind9 for this, but you may prefer to use dnsmasq for something more lightweight.

First, install the server package:

sudo apt install bind9 bind9

Next, configure the server:

/etc/bind/named.conf.options

acl "trusted" {
	10.99.10.0/24;
	localhost;
};

options {
	directory "/var/cache/bind";
	forwarders {
		1.1.1.1;
		1.0.0.1;
	};
	dnssec-validation auto;
	auth-nxdomain no; 
	listen-on-v6 { any; };
	listen-on { any; };
	allow-recursion { trusted; };
	allow-query-cache { trusted; };
	allow-transfer { none; };
};

If you prefer to use Google or another public resolver, replace the forwarders lines.

We could also configure DNS zones or dynamic DNS, but this minimal config will be enough to get the network up and running.

You can test the DNS server by querying localhost:

$ dig @localhost +short one.one.one.one
 1.1.1.1
 1.0.0.1

If this command outputs something else, double check the config.

Finally, to allow other devices to access the DNS server you must add these lines to the INPUT chain in the IPtables config:

-A INPUT -s 10.99.10.0/24 -p udp --dport 53 -j ACCEPT
-A INPUT -s 10.99.10.0/24 -p tcp --dport 53 -j ACCEPT

At this point, there should be the basic services available to get the network up and running.


This is, of course, an ongoing project. I will update and revise this document as I continue to work on and improve my own router.

comments powered by Disqus