Why?
Raspberry Pi 4 with the 8GB variant is out, and I had to do something about my older Raspberry Pi 2 which was still lying around without much use. I needed an excuse to buy the new Raspberry Pi 8GB variant without feeling guilt about not utilizing the Pi 2 I already had.
Guilt aside, this is something I always wanted to try. The routers available in the market come with a proprietary firmware – the cheaper ones do not give you enough toggles to control and the expensive ones are…expensive. Plus who wants to trust a closed-source firmware with their internet usage data ? Certainly not me.
Well, just install OpenWRT then ?
Good suggestion. Unlike other opensource router firmware such as DD-WRT, Tomato etc – OpenWRT supports larger range of devices, so chances are your cheap wireless home router is already covered – but there is an issue here,
Even though you have now installed an open-source firmware for your router – the underlying hardware and CPU spec become an issue. OpenWRT, granted, is highly optimized for embedded devices that routers are – with memory as small as 32MB – but then you can not do a lot of tasks on such a limited hardware, like setting complex firewall rules, QoS and traffic shaping, traffic analysis, VPN client and much more! – sooner or later you are bound to run into issues because the router is not very powerful. The relatively more powerful Raspberry Pi costs around the same and there is a lot of room for your privacy paranoid self!
You can install OpenWRT on Raspberry Pi too (and RPi2, too), it also gives you a neat Luci web GUI to interact with, but I am not going to install OpenWRT on my RPi. You would realize that even after running a complex router on the RPi, you are still not hitting upper limits of the hardware and probably could use your RPi to host some other services, like you would do on a regular RPi.
Warning: Running anything on router other than router related services is usually discouraged because it exposes you to risk. This is why I would also be talking about configuring firewall on your raspberry pi, with QoS, so that your router is actually usable not a hobby project you forget about later!
So, what do we need ?
In terms of hardware, you would need,
- a Raspberry Pi – I will be using Raspberry Pi 2, you could use any other version of the RPi
Warning: Raspberry Pi 2 and Raspberry Pi 3 are limited by USB 2.0 bus speed – while theoretically you could still get maximum 300Mbps bandwidth – I would not recommend using Raspberry Pi 2 or Raspberry Pi 3 if you have more than 100Mbps plan with your ISP – you would be underutlizing the available bandwidth. The newer Raspberry Pi 4 comes with a true Gigabit LAN interface and that should be okay for this purpose. - a USB WiFi adapter – you do not need this if you are using Raspberry Pi zero, and RPi 3 or above, since RPi 2 does not come with on-board WiFi, I will be using a USB WiFi adapter. Configuring driver (if required) for your USB WiFi adapter is out of scope of this article.
- (optional) USB to Ethernet adapter – Raspberry Pi has only one ethernet interface, that will be connected to the internet. If you want connect a device to your RPi with physical cable, you would need an additional NIC – this is where a USB to Ethernet adapter comes handy – I am not using this here.
Here are the software elements of our router,
- Raspbian (now known as Raspberry Pi OS) – As mentioned earlier, I want to use my Raspberry Pi for non-router related things too, while you could do that in OpenWRT too, it’s just slightly convenient to have an regular desktop distro. I will be using “lite” version of Raspian/Raspberry Pi OS, but you could go with an actual desktop variant if you want.
- RaspAP – RaspAP is an open source project that enables you to quickly setup your Raspberry Pi as a WiFi access point with a clean web interface to administer your router – like any other typical router web interface would let you. In addition to that, you could optionally enable OpenVPN.
While we will be using most of the defaults of RaspAP – we will be overriding their iptables rule in favor of our firewall configuration. - FireHOL – FireHOL let’s you setup firewall in terms of config files which are easy to read and write. It then efficiently parses and converts them into equivalent iptable rules.
FireHOL also comes with FireQOS, which let’s you shape your traffic – if you have a Amazon FireTV stick connected to your RPi router and you have an active SSH session to a remote server, you really do not want the streaming traffic to consume all of your bandwidth, or when you are playing an online multiple player game and your latency fluctuates. FireQOS helps you address all these problems, I am pretty sure you would endup with a setup which is more efficient than what the cheaper home wifi comes with.
FireHOL community is friendly, and the documents are super easy to understand, and neatly structured. I learnt of FireHOL when I realized the sole maintainer of Shorewall, an another hugely popular firewall utility, is (well-deservingly) retiring and I wanted to explore other actively maintained open source firewall-cum-traffic-shapping alternatives.
Steps
Step 1: Download and install the Raspberry Pi OS. Make sure you change the default password as well as run sudo apt-get update
and sudo apt-get upgrade
before proceeding further.
Step 2: Install RaspAP using their quick install script – as a general advice, always inspect content of the remote script before running it. You could, alternatively, follow the manual installation steps to setup RaspAP.
Once you have installed RaspAP, reboot your Raspberry Pi – if all goes well, you would see a WiFi network with SSID raspi-webgui
– the default passoword is ChangeMe and the WiFi administration portal is available at 10.3.141.1 with username admin and password secret – needless to say you must change the WiFi admin as well as the WiFi password – the web interface is clean enough so finding the relevant option shouldn’t be a task.
Once connected to RaspAP – the internet may or may not work out of the box. That shouldn’t be a concern, because we will be anyway removing the default iptables rules populated by RaspAP in next step.
Step 3: SSH to your Raspberry Pi while connected to the rasp-webgui
network. The gateway IP – 10.3.141.1
is also the IP for the Raspberry Pi – ssh to your Pi using ssh [email protected]
Step 4: RaspAP writes the iptables rule to /etc/iptables/rules.v4
and /etc/iptables/rules.v6
. Run sudo nano /etc/iptables/rules.v4
and comment all the lines by placing “#” at beginning of the line, similarly run sudo nano /etc/iptables/rules.v6
and comment out all the lines.
Step 5: Install FireHOL by running sudo apt-get install firehol
and then run sudo nano /etc/default/firehol
and set START_FIREHOL
to YES
. In the end your /etc/default/firehol should look like this,
pi@raspberrypi:~ $ cat /etc/default/firehol # FireHOL application default file # sourced by the initscript `/etc/init.d/firehol'. # # See firehol-variables(5) manual page or FireHOL Manual # for the full list of exportable variables that control the # behaviour of FireHOL and their respective description. # # To enable firehol at startup set START_FIREHOL=YES (init script variable) START_FIREHOL=YES # If you want to have firehol wait for an iface to be up add it here WAIT_FOR_IFACE="" # Disallow pre-established traffic to continue whilst the firewall is activated FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT=0
Step 6: Now we need to identify interface name for the ethernet port, as well as the WiFi, this can be done by running ifconfig
and inspecting the output. You would usually get three items in the output, you could ignore the “lo:” interface, the remaining two were eth0 and wlan0 on my device. The eth0 representing the ethernet port which was connected to the ethernet cable coming from my ISP, this would be our WAN interface. The wlan0 represent the Wireless interface, which would be our LAN interface.
Step 7: Now it’s time to define our interface and allow traffic from LAN side to pass to WAN side and configure our Firewall, edit /etc/firehol/firehol.conf
so that it looks like this, I have added the comments for your explanation,
# Defining the version fireHOL config script, leave this unchanged version 6 # Replace "eth0" with your WAN interface from ifconfig output world="eth0" # Replace "wlan0" with LAN wireless interface from ifconfig output home="wlan0" # have outgoing traffic use the WAN IP ipv4 masquerade "${world}" # Enable all traffic on the LAN interface from the connected devices interface "${home}" home policy accept # Enable outgoing traffic on the internet interface "${world}" world protection bad-packets client all accept # Connect the LAN and WAN interfaces and allow outgoing traffic from LAN to pass through WAN router world2home inface "${world}" outface "${home}" protection bad-packets client all accept
In the config above, we are defining our “LAN” aka “home” interface and “WAN” aka “world” interface. We are then defining “client all accept” rule – on “world” interface – which basically means we can connect to any service on the internet, but nobody can connect to the router – your Raspberry Pi device – this is a very important rule – this let’s your surf internet as usual, while protects your network from external threats – for “internet” your device does not exist. This is possible because FireHOL is designed with “Deny all, Allow some” principle – everything is denied on the network except for what you have explicitly allowed in your configuration.
For better in-depth explanation I highly recommend you to go through FireHOL Welcome Guide
Once you have saved your changes to firehol.conf – you are ready to go. Even with a minimum config like this, you have converted your Raspberry Pi to a router that let’s you safely explore the web – while simultaneously protecting your network from what could be lurking in the darkness outside.
Step 8: While you could practically start using your Raspberry Pi router after completing Step 7 and rebooting your machine – chances are you have more than one device that is connected to your Router, and if use internet for anything like SSH, Online Gaming, using RDP to connect to office machine (it’s WFH afterall) – it is likely that traffic from your other connected devices on the network will interfere with these “interactive” tasks and make your experience awful. It is therefore required that you give priority to some kind of traffic over the rest – while slight latency during web surfing is not noticeable, the same will be irritating when you are using SSH or playing an online game.
To address this we need FireQOS – which is already installed with FireHOL as a dependency – all you need to do is define traffic classes and their priority in /etc/firehol/fireqos.conf
First, enable FIreQOS by editing /etc/default/fireqos
and setting the value of START_FIREQOS
to YES
.
Step 9: Edit /etc/firehol/fireqos.conf
and remove anything pre-existing and set it to look like this, again – I have added inline comments for your explanation,
# Name of the WAN interface from step 6 DEVICE=eth0 # The download speed - set it to 90% of the actual download speed INPUT_SPEED=90000kbit # The upload speed - set it 90% of the actual upload speed OUTPUT_SPEED=90000kbit # I have kept LINKTYPE empty, but if you have a DSL connection, you might want to refer to the FireQOS doc linked below to properly set the value LINKTYPE="" # Each traffic class is defined twice - once for incoming and once for outgoing, the class defined first gets highest priority # Defining classes for incoming traffic interface $DEVICE world-in input rate $INPUT_SPEED $LINKTYPE class interactive commit 20% # committed 20% traffic bandwidth to "interactive" traffic match udp sport 53 # Incoming DNS traffic match tcp sport 22 # Incoming SSH traffic match udp sport 3389 # Incoming RDP traffic over UDP match tcp sport 3389 # Incoming RDP traffic over TCP match icmp # Ping class surfing commit 30% # committed 30% traffic bandwidth for web serufing match tcp sports 0:1023 # Web surfing traffic is identified as incoming TCP traffic from source port 0-1023 class synacks # Default 1% is committed to TCP ACK and SYN related traffic match tcp syn match tcp ack class default # Anything which does not match above classes get 1% default reserved bandwidth # Defining classes for outgoing traffic interface $DEVICE world-out output rate $OUTPUT_SPEED $LINKTYPE class interactive commit 20% # committed 20% traffic bandwidth to interfactive traffic match udp dport 53 # Outgoing DNS queries match tcp dport 22 # Outgoing SSH connection match tcp dport 3389 # outgoing RDP connection over TCP match udp dport 3389 # Outgoing RDP connection over UDP match icmp # Ping class surfing commit 5% # committed 5% traffic bandwidtht o surfing upload match tcp dports 0:1023 # Outgoing traffic on TCP port 0-1023 class synacks # Commited default 1% for TCP SYN and TCP ACK packets match tcp syn match tcp ack class default # 1% is the default for anything which does not match above
In above configuration we have given highest priority to SSH, DNS, RDP and PING traffic – as part of defining “interactive” traffic class – because these are “interactive” in nature and any delay would be very noticeable. We have then given web-surfing the second priority and TCP ACK/SYN packets the third priority. We have also committed bandwidth for each of the traffic classes.
The commit clause defines how the bandwidth will be reserved for each of the classes, whereas priority defines how the spare bandwidth is allocated among the classes when needed. If all the classes have exhausted their committed bandwidth, the spare bandwidth, according to our config, will go to the class with the highest priority, if needed. You could change this to “balance” distribution where spare bandwidth is distributed according to the commit rate – I highly recommend reading FireQOS New User guide to understand each of the clause as well as customize the config according to your need – for example, we haven’t considered torrent and gaming traffic in our config – however you may want to do that. The FireHOL documents are very easy go through and neatly organized.
Once you have saved changes to fireqos.conf, apply the new rules by running sudo fireqos start
and entering commit
when prompted.
FireQOS has other useful utilities such as monitoring your live traffic according to the classes you have defined using fireqos status command. Read the linked doc for a much better explanation.
Conclusion
Turning your Raspberry Pi to a wireless router which is not only robust but safe is possible!
There are many other things that you could do, which you couldn’t with your usual router, for example you could use the RaspAP interface to connect to a VPN at router level. You could use FireHOL rules to deny all the DNS traffic – to enforce WiFi clients to use much safer DNS over HTTPS. You could do a traffic analysis, or make your internet experience better by defining efficient QoS rules.
I highly recommend going through FireHOL documents. Along with that, I highly recommend reading the official Raspberry Pi WiFi AP guide as well as the Router page from Arch Linux wiki to understand what’s happening under the hood.
You might also want to research to find out solutiont to improve the WiFi range – alternatively you can get the USB-to-Ethernet adapter and then connect the LAN interface with WAN interface of your spare router, or setup a repeater. Let me know how this goes.
Bruh nice one
Keep it up, proud of you
Greeetings from msg
Fuck yeah custom router!
Nice work thank you for the tutorial its a great help !
Something worth to consider to add in this tuto would be to install AdGuard Home (better/easier than Pi-Hole IMO) to run with RaspAP (integration is very easy – not like pi-hole-, by following these instructions : https://github.com/billz/raspap-webgui/wiki/FAQs#can-i-integrate-raspap-with-adguard-home ) so you can filter adds on the DNS level directly on your custom router.
Wow. Great suggestion. Pi-hole integration is really pain in neck given Pi-hole for some reason wants you to use their fork of DNSMasq, which means you endup disabling DHCP on RaspAP.
Hi & thanks for this tuto!
I am running into some issue :
With this configuration of fireHOL I cannot connect to my VPN (once I activate OpenVPN through RaspAP, I lose my internet connection) because when I follow the part where you said to comment the iptable created by RaspAP it also comment the line that allow the forwarding of tun0 (see my iptable below). How can I fix that ?
Once I have setup OpenVPN in RaspAP, here is how my iptable looks like (here I have commented the line as described in this tuto):
# Generated by xtables-save v1.8.2 on Sat Sep 19 07:03:07 2020
#*filter
#:INPUT ACCEPT [1361:172326]
#:FORWARD ACCEPT [4755:3023239]
#:OUTPUT ACCEPT [1132:478042]
#-A FORWARD -i tun0 -o wlan0 -m state –state RELATED,ESTABLISHED -j ACCEPT
#-A FORWARD -i wlan0 -o tun0 -j ACCEPT
#COMMIT
# Completed on Sat Sep 19 07:03:07 2020
# Generated by xtables-save v1.8.2 on Sat Sep 19 07:03:07 2020
#*nat
#:PREROUTING ACCEPT [238:17198]
#:INPUT ACCEPT [176:12893]
#:POSTROUTING ACCEPT [0:0]
#:OUTPUT ACCEPT [132:9210]
#-A POSTROUTING -j MASQUERADE
#-A POSTROUTING -s 192.168.50.0/24 ! -d 192.168.50.0/24 -j MASQUERADE
#-A POSTROUTING -o tun0 -j MASQUERADE
#COMMIT
Hi Greg,
To make VPN work with Firehol, you would have to implement the same iptables rule in your firehol.conf – you will have to also define the VPN interface – tun0 and all the iptables rules.
Unfortunately I would be of little help here as my knowledge of iptables is limited.
Were you able to figure it out? I am stuck on this as well. If so please share your conf file 🙂