Tuesday, August 2, 2016

Making a router/firewall with Gentoo and Raspberry Pi3


I have small growing collection of Raspberry Pi2s and 3s to tinker with. ARM SoCs (System on Chips) are low powered yet very resourceful machines. I find these boards really neat to work on. I will show how to take a RPI3 and turn it into a wifi router. I chose the RPI3 over the RPI2 b/c the RPI3 has integrated wifi. The RPI2 needs a USB wifi dongle, which means it is competing with the ethernet port since USB and ethernet share the same system bus. The RPI3 has a separate bus for the integrated wifi which means better performance.

OS Install

Since I am big fan of Gentoo Linux I chose it as the OS for my router. The Gentoo Wiki has fantastic documentation and was a tremendous help in getting the router working. There is a quick start guide in the wiki that will get your SD card ready.

I use a serial console to access the device. Now since the RPI3 has bluetooth there extra work needed to get console access via serial. This has been documented here. Gentoo needs the following.

First you have a /boot/cmdline.txt file with the folllowing:

gentoo-rpi3 rican-linux # cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

Then you need a /boot/config.txt file with the follwing:

gentoo-rpi3 rican-linux # cat /boot/config.txt
# See /boot/overlays/README for all available options

gpu_mem = 64
core_freq=250
dtoverlay=pi3-miniuart-bt

Finally you need to edit the following section of the /etc/inittab file

# SERIAL CONSOLES
s0:12345:respawn:/sbin/agetty -L 115200 ttyAMA0 vt340
#s0:12345:respawn:/sbin/agetty -L 9600 ttyS0 vt100

#s1:12345:respawn:/sbin/agetty -L 9600 ttyS1 vt100


With these files in place you should be able to insert the SD card, connect the serial console to the GPIO pins and the OS should boot.

Initial setup

Once you are in you will want to set a root password and update the system. Before the update is performed be sure /etc/portage/make.conf is set the way you want it. Here is my setup:


gentoo-rpi3 rican-linux # cat /etc/portage/make.conf
# These settings were set by the catalyst build script that automatically
# built this stage.
# Please consult /usr/share/portage/config/make.conf.example for a more
# detailed example.
CFLAGS="-O2 -pipe -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard"
CXXFLAGS = "$ {CFLAGS}"
MAKEOPTS="-j2"
# WARNING: Changing your CHOST is not something that should be done lightly.
# Please consult https://wiki.gentoo.org/wiki/Changing_the_CHOST_variable before changing.
CHOST="armv7a-hardfloat-linux-gnueabi"
PORTDIR="/usr/portage"
DISTDIR="/usr/portage/distfiles"
PKGDIR="/usr/portage/packages"

# This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
LC_MESSAGES=C

Then execute the following commands

emerge --sync
Emerge -avuND @World

This should take a while since Gentoo compiling all the packages from source. Once this is completed you will need to install the linux-firmware package. This is done by typing emerge -av linux-firmware. Finally reboot.

Wireless and Ethernet interface setup

Once Gentoo reboots verify the wireless interface is recognized by entering ifconfig -a. If there is no wlan0 interface then run the following command dmesg |grep brcmfmac. If you see the following then the kernel is not loading the proper firmware:

[    8.963114] brcmfmac_sdio mmc1:0001:1: Direct firmware load for brcm/brcmfmac43430-sdio.bin failed with error -2
[    9.968646] brcmfmac: brcmf_sdio_htclk: HT Avail timeout (1000000): clkctl 0x50
[   10.978751] brcmfmac: brcmf_sdio_htclk: HT Avail timeout (1000000): clkctl 0x50

If this occurs first verify that wireless firmware is in the /lib/firmware/brcm directory. If the files are there then try reloading the kernel module. This by done by entering the following commands:

modprobe -r brcmfmac
modprobe brcmfmac

Then run ifconfig -a again to see if wlan0 is recognized. If not then download and install the latest brcm firmware files from the RPI github site. Once the the files are copied to /lib/firmware/brcm reload the kernel module again. The wlan0 interface should be recognized.

Now it is time to configure networking. Gentoo use OpenRC as its init system. OpenRC uses the /etc/conf.d/net file to manage the network interfaces. I will post my config file and then explain:

gentoo-rpi3 rican-linux # cat /etc/conf.d/net
# Set mac address on interfaces
mac_eth0 = "b8: 27: eb: 25: 89: 1e"
mac_wlan0="b8:27:eb:70:dc:4b"

# wlan0 settings
modules_wlan0="!iwconfig !wpa_supplicant"
config_wlan0="192.168.0.1 netmask 255.255.255.0"

The first section sets the mac address of the interfaces. The RPI3 assigns a new mac address to the interfaces after every reboot. This first section will keep that from happening. The next section defines the wlan0 interface.

The first line is what is needed to set wlan0 as an AP. The second line set the ip address of the interface.

Before we activate the interfaces verify that the /etc/resolv.conf is not symlinked to the resolv.conf file found in /lib/systemd by typing ls -l /etc/resolv.conf. If this is the case then unlink the file by typing unlink /etc/resolv.conf.

Finally activate the interfaces.

cd /etc/init.d
ln -s net.lo net.eth0
ln -s net.lo net.wlan0
rc-update add net.eth0 default
rc-update add net.wlan0 default
/etc/init.d/net.eth0 start
/etc/init.d/net.wlan0 start

DHCP, DNS, and Hostapd configuration

A simple dhcp server to use is dnsmasq. It is installed by entering emerge -av dnsmasq. When you install the package there is a default dnsmasq.conf file created in /etc.  Make a backup of the file with mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak.  Now create a blank config file with nano -w /etc/dnsmasq.conf and something similar to what is below.

gentoo-rpi3 rican-linux # cat /etc/dnsmasq.conf
dhcp-range=wlan0,192.168.0.10,192.168.0.250,72h
interface=wlan0

This will take care of both dhcp and dns setting for users who join the AP. 

AP configuration is controlled by hostapd. Installing the daemon is done by  typing emerge -av hostapd.  Like dnsmasq create a backup file for the /etc/hostapd/hosapd.conf file. Here is sample of the config file.

gentoo-rpi3 rican-linux # cat /etc/hostapd/hostapd.conf
interface=wlan0
hw_mode=g
channel=6
ieee80211n=1
wmm_enabled=1
country_code=US
ssid=ssid
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK  
rsn_pairwise = CCMP
wpa_passphrase=password

Finally enable these services.

/etc/init.d/dnsmasq start
rc-update add dnsmasq default
/etc/init.d/hostapd start
rc-update add hostapd default

iptables set up

If someone wants the RPI to have the ability to NAT and packet filter then iptables is the tool. It will allow through a series of rules to allow, block, NAT and host of other features that Gentoo has documentation on its iptables wiki, Security Handbook, and Home Router wiki. Iptables is very powerful so be careful when you are editing policies via ssh or you will kick yourself out. For the initial setup I would recommend doing it from the serial console and then testing your rules before saving.

The first thing to do is clear iptables and the nat table before starting. This is done by doing the following.

gentoo-rpi3 rican-linux # iptables -F
gentoo-rpi3 rican-linux # iptables -t nat -F
gentoo-rpi3 rican-linux # iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination      

Chain FORWARD (policy DROP)
target     prot opt source               destination      

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination      
gentoo-rpi3 rican-linux # iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination      

Chain INPUT (policy ACCEPT)
target     prot opt source               destination      

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination      

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

The first two commands clear the table and second two lists the table. Now that it is verified that the tables are flushed lets begin.

We will is setup packet filtering then move on to NAT. Defining the default policy for the 3 chains that iptables uses is the first thing to do. These chains are INPUT, FORWARD, and OUTPUT. The INPUT chain controls what traffic is allowed to connect to your firewall. FORWARD controls what traffic you allow through the firewall. OUTPUT controls what traffic the firewall sends out. This is how you set this up.

gentoo-rpi3 rican-linux # iptables -I INPUT -p TCP --dport ssh -i eth0 -j ACCEPT
gentoo-rpi3 rican-linux # iptables -P INPUT DROP
gentoo-rpi3 rican-linux # iptables -P FORWARD DROP
gentoo-rpi3 rican-linux # iptables -P OUTPUT ACCEPT
gentoo-rpi3 rican-linux # iptables -vL
Chain INPUT (policy DROP 19 packets, 1729 bytes)
 pkts bytes target     prot opt in     out     source               destination      
  423 28968 ACCEPT     tcp  --  eth0   any     anywhere             anywhere             tcp dpt:ssh

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination      

Chain OUTPUT (policy ACCEPT 135 packets, 13152 bytes)
 pkts bytes target     prot opt in     out     source               destination

If you were doing this via the serial console then the first (which allows ssh traffic to the eth0 interface) rule could be entered later. However if you were editing your policy via shh coming in on the eth0 interface, then you need to add this rule before you lock down your policy. I set the INPUT and FORWARD policies to DROP and the OUTPUT policy to ACCEPT. This setting will mean that any connection that I want to allow to the firewall or through the firewall I need to define or else the firewall will drop it. However any connection the firewall sends out will be allowed.

Now that the default policies are set we can set out access rules.


gentoo-rpi3 rican-linux #iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
gentoo-rpi3 rican-linux #iptables -I INPUT 1 -i wlan0 -j ACCEPT
gentoo-rpi3 rican-linux #iptables -I INPUT 1 -i lo -j ACCEPT
gentoo-rpi3 rican-linux # iptables -I FORWARD -i wlan0 -s 192.168.0.0/255.255.255.0 -j ACCEPT

gentoo-rpi3 rican-linux # iptables -A FORWARD -i eth0 -d 192.168.0.0/255.255.255.0 -j ACCEPT
gentoo-rpi3 rican-linux # iptables -vL
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination      
  624 48671 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere          
  111  7392 ACCEPT     all  --  wlan0  any     anywhere             anywhere          
 4298  305K ACCEPT     tcp  --  eth0   any     anywhere             anywhere             tcp dpt:ssh

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination      
 1338 82115 ACCEPT     all  --  wlan0  any     192.168.0.0/24       anywhere          
  136 82600 ACCEPT     all  --  eth0   any     anywhere             192.168.0.0/24    

Chain OUTPUT (policy ACCEPT 47 packets, 5574 bytes)

These rules allows the wlan0 network (192.168.0.0/24) to pass through the firewall to anywhere. Now to access the public internet, the local network (192.168.0.0/24) needs to be hidden by the public ip address of your router/firewall (in this case eth0). This is done through a process call Network Address Translation (NAT). When an entire network is translated into one public address this is called Dynamic NAT or Hide NAT. The setup is pretty simple.

gentoo-rpi3 rican-linux # iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
gentoo-rpi3 rican-linux # iptables -t nat -vL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  any    eth0    anywhere             anywhere 

The NAT table has two additional chains PREROUTING and POSTROUTING. PREROUTING defines how the router/firewall will nat the traffic before it makes it forwarding decision. POSTROUTING defines how the router/firewall will nat the traffic as its being forwarded out the device. Since a Hide NAT is for outbound traffic it is added to the POSTROUTING chain.

Now just add your devices to the wifi network and you should have connectivity. If you want to see what devices are on your wifi network run the command iw dev wlan0 station dump.

UPDATE I: I forgot to explain how to save your iptables changes so they will be persistent after reboots.


/etc/init.d/iptables save
rc-update add iptables default

UPDATE II: You will need to enable routing by adding the following lines to /etc/sysctl.conf then running the command sysctl -p.

net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 1


No comments:

Post a Comment