Using a Raspberry Pi as a Firewall

In this article, I’ll show you how to configure a Raspberry Pi as a firewall device. You might ask why would I do this? One reason is, it’s just fun. Raspberry Pis are cool little devices to experiment with. The second reason is more practical, saving power. My old firewall was an older x86 system, and while it is pretty bare-bones it still used a large amount of power (~150 watts) compared to the ~3.5 watts for the Pi. And for a device that runs 24/7, that adds up over time.

In this article, I assume you know your way around a managed network switch, have some experience with administering Linux and have configured a Raspberry Pi before.
So here we go…

Network-wise, we’re going to need a good managed switch to accomplish this feat. We’ll need a switch that supports both VLAN tagging and VPID. We’ll be setting up two VLANs, one for the WAN(internet) connection and another for the LAN(internal) traffic. In this article, we’ll use the default VLAN (1) for our LAN subnet and we’ll use VLAN 99 for our WAN subnet. Go ahead and configure those two VLANs on your switch, now. Your normal LAN devices (workstations, etc) should be on switch ports that are on VLAN 1 and set to untagged.

First we’ll need to configure two special switch ports. I chose port #1 for the internet connection and port #2 for the firewall connection. Assign port #1 to VLAN 99 and make sure it’s untagged. Then configure VPID for this port, by assigning incoming, untagged traffic to VLAN 99.
For the firewall port (#2) we’re going to assign both VLAN 1 and VLAN 99 to the port, both of them setup as being tagged.
Plug an ethernet cable between your internet router/modem/etc and port #1 on the switch. If your switch doesn’t do auto-sense on it’s ports, or it doesn’t have a designated ‘uplink’ port, your going to need to use a crossover cable.

Now for the Pi configuration.
We’re going to use a model B Pi and a basic setup with Raspbian (Wheezy) on a 4GB SD card. Download Raspbian from the usual sources and transfer it to the SD card. For the initial setup, you’re going to need to use the console (HDMI) or plug it into a standard (VLAN 1) switch port with DHCP enabled. I’ll assume you’re using the console, for the rest of this configuration.
During the initial boot, you’ll want to perform some basic setup steps;

  • Change the ‘pi’ user’s password!
  • Enable the ssh daemon
  • Configure the locale settings to your liking
  • Configure the keyboard settings
  • Switch the system to using the console, rather than booting into X

Install the ‘vlan’ package which contains the utilities needed to manage VLAN virtual interfaces:

apt-get install vlan

Once the initial setup is complete, we’ll tackle the network configuration. Because we’re tagging both VLANs assigned to the firewall switch port, we’re going to need to make quite a few edits to the /etc/network/interfaces file.
We’re going to use the 192.168.0.0/24 subnet for our LAN devices and pretend 10.2.3.0/24 is our WAN subnet.
We’re going to need to configure VLAN virtual interfaces and assign them appropriate IP addresses. Our default gateway is going to be the router on the WAN subnet.
One more thing. We’ll need to have the physical ethernet interface on the Pi become active, before the virtual interfaces can be setup. So we’re going to assign an unused (and unrouted) subnet to the physical interface. Then we’ll setup the virtual interfaces to be started as a post step to the physical interface.

Here’s an example of the completed /etc/network/interfaces file:


auto lo
iface lo inet loopback

# Our physical ethernet interface
auto eth0
iface eth0 inet static
address 192.168.3.4
netmask 255.255.255.0
post-up ifup eth0.1
post-up ifup eth0.99

# LAN VLAN(1) virtual interface
iface eth0.1 inet static
address 192.168.0.1
netmask 255.255.255.0

# WAN VLAN(99) virtual interface
iface eth0.99 inet static
address 10.2.3.4
netmask 255.255.255.0
gateway 10.2.3.1

We need to configure traffic forwarding, so that we can actually past traffic from the LAN to the WAN interfaces. To do that, we need to edit /etc/sysctl.conf and uncomment the ‘net.ipv4.ip_forward=1’ line. Then use ‘sysctl -p’ to reload the kernel settings.

Next will come the firewall rules. This is going to vary widely depending on what you want to allow (or not). In our case, we’re going to explicitly allow traffic from our LAN, out through the firewall, on a port-by-port basis.
We’re also going to need to setup a source NAT rule, which will NAT the outbound traffic from our LAN. We’ll also limit ‘admin’ access to the firewall, from our LAN subnet.

We’ll create a new /etc/iptables file, with the following contents:


#################################################
# Filter tables
#################################################
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
#################################################
# INPUT
#################################################
-A INPUT -i lo -j ACCEPT
# Allow ICMP traffic to the firewall (uncomment to allow)
#-A INPUT -i eth0.1 -p icmp --icmp-type any -j ACCEPT
# Allow traffic for stateful rules, if we have any
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SSH traffic into the firewall, but only from the LAN subnet
-A INPUT -i eth0.1 -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j DROP
#################################################
# FORWARD
# eth0.1=internal-net, eth0.99=wan-net
#################################################
# Forward traffic for stateful rules, if we have any
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
# Forward SSH out to the world
-A FORWARD -i eth0.1 -o eth0.99 -p tcp --dport 22 -j ACCEPT
# Forward DNS out to the world
-A FORWARD -i eth0.1 -o eth0.99 -p tcp --dport 53 -j ACCEPT
-A FORWARD -i eth0.1 -o eth0.99 -p udp --dport 53 -j ACCEPT
# Forward NTP out to the world
-A FORWARD -i eth0.1 -o eth0.99 -p tcp --dport 123 -j ACCEPT
-A FORWARD -i eth0.1 -o eth0.99 -p udp --dport 123 -j ACCEPT
# Forward HTTP/HTTPS out to the world
-A FORWARD -i eth0.1 -o eth0.99 -p tcp --dport 80 -j ACCEPT
-A FORWARD -i eth0.1 -o eth0.99 -p tcp --dport 443 -j ACCEPT
# Log and drop any remaining packets
-A FORWARD -i eth0.1 -o eth0.99 -j LOG
-A FORWARD -i eth0.1 -o eth0.99 -j DROP
COMMIT
#################################################
# NAT tables
#################################################
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Source NAT outbound traffic to the IP address of the firewall's interface on the WAN subnet
-A POSTROUTING -o eth0.99 -j SNAT --to-source 10.2.3.4
COMMIT

Those rules will allow a basic setup:

  • Allow SSH into the firewall from systems in our LAN subnet
  • Forward DNS and NTP traffic out to systems on the internet
  • Forward HTTP and HTTPS traffic out to systems on the internet
  • Source NAT any outbound traffic destined for the internet
  • Any traffic attempting to leave our LAN, and not explicitly allowed, is logged and then dropped.

Once we’ve saved the firewall rules, we need to have them loaded each time the system boots. we do that by adding a line to /etc/rc.local:

echo "Loading iptables rules..."
/sbin/iptables-restore </etc/sysconfig/iptables

Now shutdown the Raspberry Pi, connect it to port #2 on your network switch, and then boot it back up.
If you’ve done everything correctly, you should be able to SSH into the new firewall (using 192.168.0.1 in this case).

Login and validate that you can access an external network resource from the firewall…

wget -O- http://www.google.com

Hopefully you got a page or two of HTML, which proves that the firewall can reach Google via HTTP, and it validates that the SNAT rule is working correctly.

Now, on a LAN device, set your default gateway to the LAN IP address of the firewall. Then perform the same ‘wget’ test. If all goes well, you’ll see the same bulk of HTML as before.

And with that you’re up and running. Depending on you needs, you’ll most likely want to add additional ‘FORWARD’ rules for any other traffic that you chose to allow out of your network.

Posted in Embedded, Hardware | Leave a comment