Thursday, August 4, 2016

Using Arch Linux as router/firewall on the Raspberry Pi3


The last post I explained how I got a Raspberry Pi3 to work as a wifi router/firewall using Gentoo Linux. So this post I will explain how to configure Arch Linux on the RPI3.

OS Install

There is no downloadable images for the RPI at the official Arch Linux site. The ARM port for Arch is maintained independently at the Arch Linux ARM site. The instructions for setting the SD card for the RPI3 are pretty simple and straightforward. The only addition there is needed is to add the following lines to the /boot/config.txt file if you want to use a serial console.

core_freq=250
dtoverlay=pi3-miniuart-bt

The RPI3 should boot normally and if you are using a serial console there should be no issues. Once Arch boots update your system by entering pacman -Syu. It should not take long then proceed to setting up the interfaces.

Interface Configuration

The documentation to set up the network interfaces can be found here. These are the steps.

First cd to the /etc/systemd/network directory. There you see a file called eth0.network. This file controls how the interface is set up. Remember that the RPI3 changes its MAC address after every reboot. So this part needs to be added to the file to prevent this.

[Link]
MACAddress=current mac address

The current mac address can be found with the command ip link. Then to set up the wlan0 interface, make a copy of the eth0.network file and call it wlan0.network with cp eth0.network wlan0.network. Next make the following changes.

[Match]
Name=wlan0

[Network]
Address=192.168.0.1/24 or whatever ip scheme you choose

[Link]
MACAddress=current mac address

Finally reload the systemd-networkd servivce with the command systemctl restart systemd-networkd and the wlan0 interface will come up.

Networking, DHCP, and DNS Configuration

In order to enable networking copy the /usr/lib/sysctl.d/50-default.conf file to /etc/sysctl.d/ directory. Then add these lines at the end.

# Enable routing
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 1

Restart sysctl with sysctl -p /etc/sysctl.d/50-default.conf and routing is enabled.

I used dnsmasq for both DHCP and as a local DNS resolver. Install the package by entering pacman -S dnsmasq. Then take the default /etc/dnsmasq.conf file and back it up with mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak. Create a new /etc/dnsmasq.conf file and add the following.

dhcp-range=wlan0,192.168.0.10,192.168.0.250,72h
interface=wlan0

If you try to start the service now it will fail. This is because systemd has it own resolver that will conflict with dnsmasq. This needs to be disabled before you proceed by doing this.

systemctl stop systemd-resolvd
systemctl disable systemd-resolvd

Afterwards add these lines or similar to /etc/resolv.conf

nameserver 208.67.220.220
nameserver 208.67.222.222

Finally you can enable dnsmasq.

systemctl enable dnsmasq
systemctl start dnsmasq

Hostapd Configuration

Install hostapd with  pacman -S hostapd. Then like with dnsmasq make a backup of the default config file and create a blank one and add the following.

interface=wlan0
hw_mode=g
channel=6
ieee80211n=1
wmm_enabled=1
country_code=US
ssid=ssid
auth_algs=1
rsn_pairwise=CCMP
wpa=2
wpa_key_mgmt=WPA-PSK  
rsn_pairwise=CCMP
wpa_passphrase=password

Then enable the service.

systemctl enable hostapd
systemctl start hostapd

iptables Configuration

The setup of iptables is pretty similar to what was done on the Gentoo post with some differences. Here are my rules.

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -I INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i wlan0 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
iptables -I FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 192.168.0.0/24 -i wlan0 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

These rules need to added to the /etc/iptables/iptables.rules file or systemd will not start the service. This is done with the command iptables-save > /etc/iptables/iptables.rules. Then enable the service.

systemctl enable iptables
systemctl start iptables

This should be all you need to get the RPI3 working as a wifi router with Arch Linux.

Final thoughts on systemd

Arch Linux uses systemd as its init system. It is a significant change from the init systems found in other Unix operating Systems (ie the BSDs) as well OpenRC that Gentoo Linux uses. A lot has been written over the change to systemd and the benefit or hurt it has brought to Linux. If you want to read more on it a simple google search will suffice. Here are some of  my thoughts. I have set up routers/firewalls both physical and virtual using Gentoo, FreeBSD, NetBSD, and OpenBSD. In everyone of these OSs the init system did not get in my way. Setting up the services was straightforward, the documentation was easy to find and digest. I did not have that experience with systemd. As a Linux user when the Distros I used moved to systemd, I really did not see what all the fuss was about. I heard how some sysadmins were just not happy at how systemd was in their words 'taking over everything'. It wasn't until I tried to set networking on a distro that used systemd that I began to see their point. Systemd really does reach into a lot. It has its own local resolver that you need to disable if you want to set dnsmasq. The Arch Linux Router page on the wiki does not tell you this or at least let you know how to check beforehand. Setting up the interfaces was not straight forward. Should I use netctl or systemd-networkd? I want to be fair. Once I sifted through the docs I was able to get everything work. Now if I need to do this again I can go through the steps fairly quickly. However with other init systems there was very little sifting needed (if any) and I did not run into the gotchas that I did with systemd. I am not trying to be a systemd hater, what I am saying is that is very different. As systemd grows in adoption makers and administrators of Linux based network appliances will probably need relearn how to set up their systems. 

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