Categories
Linux Networking raspberrypi

Using a Raspberry Pi 2 as a Router + Configuring Raspbian for IPv6 with Aussie Broadband

I recently decided to move away from using my Wifi access point as a router, and instead use an old my Raspberry Pi 2 as my router. I had a few reasons for doing this:

  • I wanted a more up-to-date device as my internet facing box. My Wifi AP hasn’t received any firmware updates in several years, so I don’t trust the security of it very highly.
  • I wanted to learn more about networking, particularly how to properly configure IPTables on Linux and how to see what traffic is flowing on my network.
  • I wanted to turn on the Aussie Broadband IPv6 beta, and get an internet-facing IPv6 network.

The Hardware

  • Raspberry Pi 2
  • Network Interface 1 – enxb827eb579e58 – Built-in Pi eth – internet facing
  • Network Interface 1 – enx3c18a0054c1e – Lenovo USB3 Ethernet adaptor (running at USB2 speed) – internal facing

This is sufficient for my network, which is limited to 50mb/s. However, if you have 100Mb/s or more you will hit the limit of what the built-in ethernet port on the Pi 2 can support. You may be able to go further with a Pi 4, which has gigabit ethernet and USB3.

The Software

  • Raspbian 10 Buster
  • NetworkManager – provides IPv4 networking
  • dhcpcd5 – provides IPv6 DHCPv6
  • fail2ban – brute force protection
  • iptables – firewalling and networking rules
  • iptables-persistent – load up my iptables rules on boot

Part 1 – Installation

To start, I just did a Raspbian Lite install, and then set up the Pi to provide SSH. I was then able to configure everything remotely.

Next, I wanted to install NetworkManager. I followed some instructions from here, but I didn’t remove dhcpcd5, as it is able to do DHCPv6 with Prefix-Delegation, something it seems isn’t possible with NetworkManager: https://raspberrypi.stackexchange.com/questions/29783/how-to-setup-network-manager-on-raspbian

sudo apt install network-manager
sudo apt purge openresolv 
sudo nano /etc/dhcpcd.conf
# Add to top of /etc/dhcpcd.conf
ipv6only

Network Manager configuration:

Make sure to edit the config files using the nmcli con edit command, eg nmcli con edit internet
Note in both configs the method=ignore line, as IPv6 is configured by dhcpcd5

root@routepi ###/e/N/system-connections> pwd
/etc/NetworkManager/system-connections
root@routepi ###/e/N/system-connections> cat internet.nmconnection
[connection]
id=internet
uuid=231de2fd-f890-49ba-baed-295fe30a5ee1
type=ethernet
interface-name=enxb827eb579e58
permissions=
timestamp=1565155463

[ethernet]
mac-address-blacklist=

[ipv4]
dns-search='lan';
method=auto

[ipv6]
addr-gen-mode=stable-privacy
dns-search=
method=ignore
root@routepi ###/e/N/system-connections> cat house.nmconnection
[connection]
id=house
uuid=4a683432-11e6-3c31-be8e-0603ca5fb6ce
type=ethernet
autoconnect-priority=-999
permissions=
timestamp=1563862423

[ethernet]
mac-address=3C:18:A0:05:4C:1E
mac-address-blacklist=

[ipv4]
address1=10.1.1.9/24
dns-search=
ignore-auto-dns=true
method=manual

[ipv6]
addr-gen-mode=stable-privacy
dns-search=
method=ignore

Dhcpcd5 configuration:

The main part of interest are the lines requesting an IA_NA and also an IA_PD from the internet facing interface, and assigning them to the internal interface. Aussie Broadband requires that you request both an IA_NA and an IA_PD, os this is the config to make it work:

allowinterfaces enxb827eb579e58
interface enxb827eb579e58
# Address from the /64
ia_na 1
# Request /56 and assign it to other interface
ia_pd 2 enx3c18a0054c1e/1

Whole file is here for reference:

# A sample configuration for dhcpcd.
# See dhcpcd.conf(5) for details.

# Allow users of this group to interact with dhcpcd via the control socket.
#controlgroup wheel
ipv6only
# Inform the DHCP server of our hostname for DDNS.
hostname

# Use the hardware address of the interface for the Client ID.
#clientid
# or
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
# Some non-RFC compliant DHCP servers do not reply with this set.
# In this case, comment out duid and enable clientid above.
duid

# Persist interface configuration when dhcpcd exits.
persistent

# Rapid commit support.
# Safe to enable by default because it requires the equivalent option set
# on the server to actually work.
option rapid_commit

# A list of options to request from the DHCP server.
option domain_name_servers, domain_name, domain_search, host_name
#option classless_static_routes
# Respect the network MTU. This is applied to DHCP routes.
#option interface_mtu

# Most distributions have NTP support.
#option ntp_servers

# A ServerID is required by RFC2131.
require dhcp_server_identifier

# disable running any hooks; not typically required for simple DHCPv6-PD setup
script /bin/true

# Disable dhcpcd's own router solicitation support; allow slaacd
# to do this instead by setting "inet6 autoconf" in hostname.em0
noipv6rs

# Generate SLAAC address using the Hardware Address of the interface
#slaac hwaddr
# OR generate Stable Private IPv6 Addresses based from the DUID
#slaac private


allowinterfaces enxb827eb579e58
interface enxb827eb579e58
# Address from the /64
ia_na 1
# Request /56 and assign it to other interface
ia_pd 2 enx3c18a0054c1e/1


# Example static IP configuration:
#interface eth0
#static ip_address=192.168.0.10/24
#static ip6_address=fd51:42f8:caae:d92e::ff/64
#static routers=192.168.0.1
#static domain_name_servers=192.168.0.1 8.8.8.8 fd51:42f8:caae:d92e::1

# It is possible to fall back to a static IP if DHCP fails:
# define static profile
#profile static_eth0
#static ip_address=192.168.1.23/24
#static routers=192.168.1.1
#static domain_name_servers=192.168.1.1

# fallback to static profile on eth0
#interface eth0
#fallback static_eth0

SysCTL config to allow routing

Now we need to turn on some kernel options in the sysctl config file:

root@routepi ###~> cat /etc/sysctl.conf 
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.enxb827eb579e58.accept_ra=2

The ipv4 line sets the pi to forward IPv4 packets
The net.ipv6.conf.enxb827eb579e58.accept_ra=2 is important because without that, the routing table won’t be automatically updated with the routes provided by the Aussie Broadband Router Advertisments (RA’s). This config has three possible values:
0 = don't accept RA's
1 = accept RA's if not acting as a router
2 = accept RA's even if acting as a router
I found this info here: http://strugglers.net/~andy/blog/2011/09/04/linux-ipv6-router-advertisements-and-forwarding/

Fail2Ban Configuration

Important in any machine that is internet-facing is to have some sort of brute-force lockout system. I’m using fail2ban to help secure my SSH. However, because I run SSH on port 2, I need a little extra fail2ban config:

root@routepi ###~> cat /etc/fail2ban/jail.d/routepi.local 
[sshd]
port    = 2
ignoreip = 138.80.14.0/24, 10.1.0.0/16

IPTables configuration

Finally, to properly secure incoming traffic, I use IPTables. My iptables config is fairly lax, but provides the basics of preventing any incoming traffic without an established session from an internal device. I am using the iptables-persistent package to auto-load my iptables rules on boot. It can be installed with:

sudo apt install iptables-persistent
root@routepi ###/> cat /etc/iptables/rules.v4
# Jays IPTables on Pi
# enxb827eb579e58 - WAN interface
# enx3c18a0054c1e - LAN interface
# 10.1.1.13 - TVPi

*filter
:INPUT DROP
:FORWARD DROP
:OUTPUT ACCEPT
## INPUT rules
# ACCEPT related or established connections
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# icmp
-A INPUT -p icmp -j ACCEPT
# DHCP from ISP
-A INPUT -p udp --dport 68 -j ACCEPT
# DHCP for internal
-A INPUT -i enx3c18a0054c1e -p udp --dport 67 -j ACCEPT
# SSH
-A INPUT -p tcp --dport 2 -j ACCEPT
-A INPUT -p udp -m multiport --dports 60001:60099 -j ACCEPT
# DNS - only on internal
-A INPUT -i enx3c18a0054c1e -p udp --dport 53 -j ACCEPT
-A INPUT -i lo              -p udp --dport 53 -j ACCEPT
# We dont participate in mDNS, so drop it without logs
-A INPUT -p udp --dport 5353 -j DROP
# We dont participate in syncthing, so drop it without logs
-A INPUT -p tcp --dport 22000 -j DROP
-A INPUT -p udp --dport 22000 -j DROP
-A INPUT -p udp --dport 21027 -j DROP
# We dont participate in tvheadend, so drop it without logs
-A INPUT -p udp --dport 65001 -j DROP
# We dont participate in uuuggghhh NetBios, so drop it without logs
-A INPUT -p udp -m multiport --dports 137,138,139 -j DROP
-A INPUT -p tcp -m multiport --dports 137,138,139 -j DROP
# We dont participate in igmp, so drop it without logs
-A INPUT -p 2 -j DROP
# Log everything else before it's dropped - limit to 1/s
-A INPUT -i enxb827eb579e58 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix :nf4_INPUT_ext_dropped:
-A INPUT -i enx3c18a0054c1e -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix :nf4_INPUT_int_dropped:
## End INPUT rules

# Forward all packets that are being DNAT'd
-A FORWARD -m conntrack --ctstate DNAT -j ACCEPT
# Forward all packets on the LAN side
-A FORWARD -i enx3c18a0054c1e -j ACCEPT
# Forward active connections on the WAN side
-A FORWARD -i enxb827eb579e58 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT


*nat
:PREROUTING ACCEPT
:INPUT ACCEPT
:POSTROUTING ACCEPT
:OUTPUT ACCEPT

# HTTP(S) Forwarding
-A PREROUTING -m addrtype --dst-type LOCAL -p tcp --dport 80 -j DNAT --to-destination 10.1.1.13:80
-A PREROUTING -m addrtype --dst-type LOCAL -p tcp --dport 443 -j DNAT --to-destination 10.1.1.13:443
# UDP too for SPDY/HTTP3
-A PREROUTING -m addrtype --dst-type LOCAL -p udp --dport 443 -j DNAT --to-destination 10.1.1.13:443

# port 4 goes to TVPi SSH
-A PREROUTING -m addrtype --dst-type LOCAL -p tcp --dport 4 -j DNAT --to-destination 10.1.1.13:4
# MOSH ports
-A PREROUTING -m addrtype --dst-type LOCAL -p udp --dport 60200:60299 -j DNAT --to-destination 10.1.1.13:60200-60299
# port 3 goes to TVPi SSH
-A PREROUTING -m addrtype --dst-type LOCAL -p tcp --dport 3 -j DNAT --to-destination 10.1.1.11:22
# MOSH ports
-A PREROUTING -m addrtype --dst-type LOCAL -p udp --dport 60100:60199 -j DNAT --to-destination 10.1.1.11:60100-60199

# MASQ for packets that are being DNAT'd, so that they go back to the router
-A POSTROUTING -m conntrack --ctstate DNAT -j MASQUERADE

# MASQ (NAT) all packets that are accepted by the forwarding
-A POSTROUTING -o enxb827eb579e58 -j MASQUERADE
-A POSTROUTING -o enx3c18a0054c1e -m conntrack --ctstate RELATED,ESTABLISHED -j MASQUERADE
COMMIT
root@routepi ###/> cat /etc/iptables/rules.v6
# Jays ipv6 config
# enxb827eb579e58 - WAN interface
# enx3c18a0054c1e - LAN interface

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

## INPUT rules
# Allow related or established traffic
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Allow NDP on all interfaces (it's link-local, so pretty safe)
-A INPUT -p icmpv6 --icmpv6-type router-solicitation      -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type router-advertisement     -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation   -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement  -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type redirect                 -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type 141                      -j ACCEPT -m comment --comment "inverse NDP" 
-A INPUT -p icmpv6 --icmpv6-type 142                      -j ACCEPT -m comment --comment "inverse NDP"
# Allow internal icmp
-A INPUT -p icmpv6 -i enx3c18a0054c1e -j ACCEPT
# Allow external/internal echo req/resp
-A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-A INPUT -p icmpv6 --icmpv6-type echo-reply   -j ACCEPT
# Multicast Receiver Notification messages
-A INPUT -p icmpv6 --icmpv6-type 130 -j ACCEPT -m comment --comment "Listener Query" 
-A INPUT -p icmpv6 --icmpv6-type 131 -j ACCEPT -m comment --comment "Listener Report" 
-A INPUT -p icmpv6 --icmpv6-type 132 -j ACCEPT -m comment --comment "Listener Done" 
-A INPUT -p icmpv6 --icmpv6-type 143 -j ACCEPT -m comment --comment "Listener Report v2" 
# SEND Certificate Path Notification messages
-A INPUT -p icmpv6 --icmpv6-type 148 -j ACCEPT -m comment --comment "Certificate Path Solicitation" 
-A INPUT -p icmpv6 --icmpv6-type 149 -j ACCEPT -m comment --comment "Certificate Path Advertisement"
# Multicast Router Discovery messages
-A INPUT -p icmpv6 --icmpv6-type 151 -j ACCEPT -m comment --comment "Multicast Router Advertisement" 
-A INPUT -p icmpv6 --icmpv6-type 152 -j ACCEPT -m comment --comment "Multicast Router Solicitation" 
-A INPUT -p icmpv6 --icmpv6-type 153 -j ACCEPT -m comment --comment "Multicast Router Termination" 
# Drop fake loopback traffic 
-A INPUT -s ::1/128 ! -i lo -j DROP
# Allow incoming DHCPv6 from ISP
-A INPUT -p udp --dport 546 -j ACCEPT
# SSH
-A INPUT -p tcp --dport 2 -j ACCEPT
-A INPUT -p udp -m multiport --dports 60001:60099 -j ACCEPT
# DNS - only on internal
-A INPUT -i enx3c18a0054c1e -p udp --dport 53 -j ACCEPT
-A INPUT -i lo              -p udp --dport 53 -j ACCEPT
# We dont participate in mDNS, so drop it without logs
-A INPUT -p udp --dport 5353 -j DROP
# We dont participate in syncthing, so drop it without logs
-A INPUT -p tcp --dport 22000 -j DROP
-A INPUT -p udp --dport 22000 -j DROP
-A INPUT -p udp --dport 21027 -j DROP
# We dont participate in tvheadend, so drop it without logs
-A INPUT -p udp --dport 65001 -j DROP
# We dont participate in uuuggghhh NetBios, so drop it without logs
-A INPUT -p udp -m multiport --dports 137,138,139 -j DROP
-A INPUT -p tcp -m multiport --dports 137,138,139 -j DROP
# Log everything else before it's dropped - limit to 1/s
-A INPUT -i enxb827eb579e58 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix :nf6_INPUT_ext_dropped:
-A INPUT -i enx3c18a0054c1e -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix :nf6_INPUT_int_dropped:
## End INPUT rules

# Allow internal traffic out and external traffic in if rel/est
# This should also cover all icmpv6 error messages
-A FORWARD -i enx3c18a0054c1e -j ACCEPT
-A FORWARD -i enxb827eb579e58 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Allow imcpv6 echo
-A FORWARD -p icmpv6 --icmpv6-type echo-request -j ACCEPT
-A FORWARD -p icmpv6 --icmpv6-type echo-reply   -j ACCEPT

# TVpi - 2403:0000:0000:0000::7
# SSH
-A FORWARD -d 2403:0000:0000:0000::7 -p tcp --dport 4 -j ACCEPT
-A FORWARD -d 2403:0000:0000:0000::7 -p udp --dport 60000:60099 -j ACCEPT
# HTTP/s
-A FORWARD -d 2403:0000:0000:0000::7 -p tcp --dport 80 -j ACCEPT
-A FORWARD -d 2403:0000:0000:0000::7 -p tcp --dport 443 -j ACCEPT
-A FORWARD -d 2403:0000:0000:0000::7 -p udp --dport 443 -j ACCEPT


COMMIT

Finally – Backing Up

There’s always a risk of SD card failure on a Raspberry Pi, so I make sure to backup all my configurations into a git repository in my home directory. Here is the script I use to do so:

root@routepi ###~/l/router_conf> cat backup_router_conf.fish
#!/usr/bin/env fish

rsync -a /etc/dnsmasq.d ./
rsync -a /etc/iptables ./
rsync -a /etc/NetworkManager ./
rsync -a /etc/sysctl.conf ./
rsync -a /etc/dhcpcd.conf ./
rsync -a /etc/fail2ban/jail.d ./fail2ban/

Any time I make a change, I will run the script to backup any changed config files, and then commit + push them to my git server.

That’s it! Feel free to leave a comment if you have a similar setup, or suggestions on how to do things better.

Categories
Linux raspberrypi

How to install Kodi 18.3 on Raspberry Pi 3 with Raspbian 10 Buster

Note – 2020-01-06 – The latest version of Kodi is now available in the standard Raspian 10 repository, so installing the standard way will get a working, up-to-date version, no need to add extra sources. To install just run:

sudo apt-get install kodi

Kept for historical purposes:

Since the release of Raspbian 10 Buster, I have really enjoyed using it on the Raspberry Pi 2 that I use as my network router. The main features I like are:

  • Newer services and libraries, including:
    • Nginx
    • MariaDB
  • Python 3.7
  • Fish Shell version 3.0.x

However, the Pi 3 I have hooked up to my TV wasn’t able to be upgraded, because the version of Kodi media player included with buster is currently 17.6. Also, it doesn’t have proper acceleration for the Raspberry Pi hardware, and doesn’t launch properly without X11 window manager running. However, I discovered that the Pipplware team has been packaging an improved Kodi version. See more about Pipplware here: http://pipplware.pplware.pt/

I found this guide (https://linuxsuperuser.com/install-latest-version-kodi-raspbian-jessie/) on how to use the Pippleware repo on Raspbian Jessie, but it isn’t fully compatible with Buster, so here’s the instructions to use the guide on Buster:

Make a backup of your SD card, so that you can rollback if needed.

https://thepihut.com/blogs/raspberry-pi-tutorials/17789160-backing-up-and-restoring-your-raspberry-pis-sd-card

Add the pipplware list to your APT sources list. Note the buster part. You can do this by running:

ADDED 2019-09-14 – As mentioned by Neil in the comments, before you add the pipplware repository you should uninstall any existing kodi packages, to prevent conflicts. Thanks Neil! You can do this by running:

sudo apt purge kodi kodi-data kodi-bin kodi-repository-kodi 

Now you can add the pipplware repository to your sources list:

sudo bash -c "echo 'deb http://pipplware.pplware.pt/pipplware/dists/buster/main/binary /' >/etc/apt/sources.list.d/pipplware.list"

Add the pipplware key to APT, so that software from their repository is trusted:

wget -O - http://pipplware.pplware.pt/pipplware/key.asc | sudo apt-key add -

Update the APT sources:

sudo apt-get update && sudo apt-get dist-upgrade

You should now be able to install the 18.3 version of Kodi:

sudo apt-get install kodi