Setting up a basic Linux router with Ubuntu 18.04 and firewalld.

I’ve made a few choices that fit around my own preferences (firewalld) and problems I faced (bridgeutils and the classic network interfaces over bridging)

This is as much a cookbook as a guide

Picking hardware is easy. I got a system that was overkill – but basically there’s a ton of decent options. You’d want to take a look at the ars technica article that inspired this build for ideas.

You’ll be looking for at least 2 ports (I went with 4 – for future options of messing with multi-wan andor bonding). The typical, cheap celerons are probably good enough, and you don’t need all that much ram. AES-NI is recommended if you need it. 

My fully built up system takes up *about* 4gb of hard disk space (not counting swap, so 8-16gb would be a good size for a hard disk. You will *also* be managing this via command line. If this scares you, you might consider a less generic router distro like pfsense or openwrt.

I am covering *everything* in detail so its a little long

It helps a lot to have a second system (or a third ). I basically used a spare laptop to check if I was getting an IP address

When I was initially setting this up on 18.04, I got this working without bridging. There’s some super strange and frustrating race conditions with dnsmasq and netplan, so in this case I’ve chosen to disable netplan. Since I’m also using firewalld (the ubuntu default is UFW), we’re changing stuff up up anyway. Netplan will work fine unless you bridge.

You’ll want to edit /etc/apt.sources.list and add the restricted universe and multiverse repos – I’m not sure which one has firewalld, but its something that’s usual for my builds. I’d use repogen and pick everything there up to updates

For now I’m setting this up as a client in my existing network. This gives me a chance to get it up before knocking out the entire network. Plug the pc into the network, run ip addr and take down what the existing network ports are. You’ll be bridging all but the “external ports” You can probably skip everything up to the firewall bits if you don’t want/need to bridge.

Plug in your external interface to the internet. Run ip addr and note its adaptor name and that of all the other interfaces

sudo apt-get install ifupdown bridge-utils
apt purge netplan.io

Set up your /etc/network/interfaces – enp1s0 is connected to the internet and is picking up an IP over DHCP. In our case we’ve chosen to bridge the other 3 interfaces in the system as well as the wireless interface (so we can set up hostapd later)

# interfaces(5) file used by ifup(8) and ifdown(8)

auto lo
iface lo inet loopback

#primary interface, to internet
auto enp1s0
iface enp1s0 inet dhcp

#3x secondary interfaces and wifi. On bridge 

auto br0
iface br0 inet static
address 192.168.2.1
netmask 24
bridge_ports enp2s0 enp3s0 enp4s0 wlp5s0

You can restart the network with systemctl restart networking or
restart the PC and check if the settings have taken

Now comes the fun part
apt purge ufw (unless you want to do it with UFW)

apt install firewalld 
(If you can't find it, you didn't enable the additional repos or apt update)

systemctl enable firewalld
systemctl start firewalld

Now, we’ll want to enable ipv4 forwarding

sudo nano /etc/sysctl.conf 

Then find the line that reads net.ipv4.ip_forward=1 and uncomment it

sysctl -p to load the settings, though I rebooted cause I didn’t know any better

Now we can start setting up the firewall. One of the nice things with firewalld is that there’s zones with specific settings and we’ll be adding each of our interfaces to an appropriate zone, and opening a minimum number of services. I’ve used the internal zone for the internal facing interfaces (in theory you don’t have to bridge) and public for the internet facing interfaces. The first two commands add interfaces to zones, then we reload the firewall settings then check to see what the currently active zones are.

sudo firewall-cmd --permanent --add-interface=enp1s0 --zone=public
sudo firewall-cmd --permanent --add-interface=br0 --zone=internal
sudo firewall-cmd --reload 
sudo firewall-cmd --get-active-zones

Now the fun bits (though we can’t really test it yet) – these are the bits that let your little linux box at as a router. While you can add a –permanent to a command, firewalld rules can be set runtime only – which is useful if you need to do something temporarily or if you figure you need to undo it with a reboot.

sudo firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o  enp1s0 -j MASQUERADE

sudo firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i br0 -o  enp1s0 -j ACCEPT

sudo firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i enp1s0 -o br0 -m state --state RELATED,ESTABLISHED -j ACCEPT

sudo firewall-cmd --runtime-to-permanent

Install dnsmasq, configure, enable the dnsmasq service and start it

sudo apt install dnsmasq
systemctl enable dnsmasq
systemctl start dnsmasq

/etc/dnsmasq.conf is long, but very very well documented.

In our case we really only need to concern ourselves with a few parts of it, but its worth reading through as you go along so you can adjust it to taste. I’ve documented the bits that I’ve edited, and why

# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
server=8.8.8.8

I set up 8.8.8.8 as a default dns server. I think I was troubleshooting and this may be optional

We want dnsmasq listening on specific interfaces (in our case the bridge we set up earlier) but not others – we really shouldn’t be handing out ip addresses on the internet facing port

# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
interface=br0
# Or you can specify which interface _not_ to listen on
except-interface=enp1s0

We also really do want our router doing dhcp. The following option is the minimal you need for things to “work”.

# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
dhcp-range=192.168.2.50,192.168.2.150,12h

for initial setup, I’m using 192.168.2.x so I can plug in this router to an existing router, and test it in the meanwhile. This sets a range of about a hundred IPs you can use. Static IPs are also an option – but you’ll need to find the appropriate block.  Make sure the br0 IP and the range given out are in the same block. 

You will need to restart dnsmasq with systemctl restart dnsmasq
You’ll also need to open up dhcp and dns to the internal zone. 

sudo firewall-cmd --zone=internal --add-service=dhcp
sudo firewall-cmd --zone=internal --add-service=dns

At this point, since masquerading and bridging is set up you should be able to plug a machine into the router, get an IP and connect to the internet.

you can use “tail /var/lib/misc/dnsmasq.leases” to see which IP addresses are given out.

Its probably a good idea to consider disabling ssh for the external zone 

sudo firewall-cmd --permanent --zone=public --remove-service=sshd

It makes remote management harder but ssh servers tend to get hit by a ton of brute force attacks.  Of course, a properly secured ssh server should be fine