Use Case: Firewall

Introduction

This document shows an example use case of how to use application hosting to create an advanced router and firewall using nftables.

WeOS                                  # Firewall Application
     .-------.  .-------.             # .-------.  .-------.
     | vlan1 |  | vlan2 |             # | vlan1 |  | vlan2 |
     '---+---'  '---+---'             # '---+---'  '---+---'
         '-.      .-'                 #     '---.  .---'
       .---+------+---.               #       .-+--+-.
       |      br0     +-------.       #       | eth0 |
       '-+---+--+-----'       |       #       '--+---'
   .-----' .-'  '-.           |       #          |
.--+-.  .--+-.  .-+--.    .---+---.   #          |             .----.
|eth1|  |eth2|  |eth3|    |  fw0  |   #          |             |eth4|
'----'  '----'  '----'    '---+---'   #          |             '----'
                              '------------------'

Our application will take exclusive control of the eth4 port, which is connected to our ISP, i.e. it is our WAN interface. We will then connect one end of a vEth pair (fw0) to the host system bridge (br0) and the other end (eth0) will be placed in the application’s network namespace.

We will then set up the application to configure the necessary network interfaces and load our firewall configuration, when the application starts.

Prerequisites

To complete this howto you will need: - An application image with iproute2, iputils and nftables installed.

Also, before we can configure our application instance, there are a few things that need to be prepared first. Follow the 4 steps below in the links and then continue this guide.

  1. Enable Application Hosting.
  2. Prepare an external media (USB/SD-Card) for persistent storage of the the application image. This is described here in Prepare the Device.
  3. Import your image (remember that your image needs to have iproute2, iputils and nftables installed).
  4. Label your image.

Given that you have now enabled application hosting, prepared an external media, imported your application image and labeled it we can proceed with configuring our application.

Application Configuration

Our application needs three files backed by persistent storage:

  • app-interfaces: For BusyBox’s version of ifupdown to configure our local VLAN interfaces
  • app-sysctl.conf: For enabling IP forwarding on startup
  • app-fw.nft: Configuration for nftables

Create the files in the root folder of the external media with the following content:

app-interfaces

auto lo
iface lo inet loopback

auto vlan1
iface vlan1 inet static
    address 192.168.1.1
    netmask 255.255.255.0
    pre-up ip link set dev eth0 up
    pre-up ip link add dev vlan1 link eth0 type vlan id 1

auto vlan2
iface vlan2 inet static
    address 192.168.2.1
    netmask 255.255.255.0
    pre-up ip link set dev eth0 up
    pre-up ip link add dev vlan2 link eth0 type vlan id 2

auto wan
iface wan inet dhcp
pre-up nft -f /etc/fw.nft

app-sysctl.conf

net.ipv4.ip_forward=1

app-fw.nft

flush ruleset

table filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iif lo accept
        meta l4proto icmp accept
        goto common
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        iif vlan1 accept
        iif vlan2 accept
        goto common
    }

    chain common {
        ct state invalid drop
        # Always allow the return traffic related to flows that were
        # initiated by a local host.
        ct state established,related accept
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }

    chain postrouting {
        type nat hook postrouting priority 0;
        # Hide local addresses for traffic going out over the Internet.
        oifname wan masquerade
    }
}

The nftables configuration above is an example of a very basic masquerading firewall that allows all outgoing connections and the generated return traffic. Edit the configuration to suit your preferences.

WeOS Configuration

First, here is the output of show media to display the name used for our external media: images. Also, the output of show app to display the name used for our label, mylabel, that was set during the steps of the Prerequisites section.

example:/#> show media
MEDIA       DEVICE      LABEL          FORMAT   RW     SIZE  USED  DESCRIPTION
Active                                                                        
internal    mtd:Config  -              jffs2    Yes   14.0M    5%  config,cert
images      mmcblk1p1   app_images     ext4     Yes    9.7G    0%
Inactive                                                                      
Available                                                                     
example:/#> show app
                                                                              
Applications
No applications found.
                                                                              
Application Images
NAME                                 LABEL            VALID  VERIFIED  VERSION
myimage-1.0                          mylabel          Yes    Yes       1.0
                                                                              
Image storage
Storage media    : images
Storage path     : app/images/

If you have chosen other names for your external media and image label, make sure to use those from here on.

Now, we are ready to configure our application instance and connect it to the WeOS networking stack:

example:/#> config app-hosting
example:/config/app-hosting/#> app fw
╒ Configuration Errors: 1 ═══════════════════════════════════════════════════╕
│#   ERROR  Description                                                      │
│1   0865   Missing image label                                              │
└────────────────────────────────────────────────────────────────────────────┘
example:/config/app-hosting/app-fw/#> image mylabel
example:/config/app-hosting/app-fw/#> share veth fw0 as eth0
example:/config/app-hosting/app-fw/#> share port eth4 as wan
example:/config/app-hosting/app-fw/#> share media images app-fw.nft as /etc/fw.nft
example:/config/app-hosting/app-fw/#> share media images app-sysctl.conf as /etc/sysctl.conf
example:/config/app-hosting/app-fw/#> share media images app-interfaces as /etc/network/interfaces
example:/config/app-hosting/app-fw/#> capability cap_net_admin
example:/config/app-hosting/app-fw/#> capability cap_net_raw
example:/config/app-hosting/app-fw/#> show
Name           : fw
Status         : Enabled
Autostart      : Yes
Description    :
Image label    : mylabel
Command        : /sbin/init
Shell          : sh
Net. Namespace : user-ns
                                                                              
Shared Resources
TYPE  HOST                              GUEST               OPTS              
media images/app-fw.nft                 /etc/fw.nft
media images/app-sysctl                 /etc/sysctl.conf
media images/app-interfaces             /etc/network/interfaces
port  eth4                              wan
veth  fw0                               eth0
                                                                              
Environment Variables
NAME                    VALUE                                                 
                                                                              
Capabilities
NAME                                                                          
CAP_NET_ADMIN
CAP_NET_RAW
example:/config/app-hosting/app-fw/#> end
example:/config/app-hosting/#> end

Now we set up our VLANs, ensuring that vEth port fw0 is a tagged member of both VLANs:

example:/config/#> vlan 1
example:/config/vlan-1/#> untagged eth1
example:/config/vlan-1/#> tagged eth3,fw0
example:/config/vlan-1/#> end
example:/config/#> vlan 2
example:/config/vlan-2/#> untagged eth2
example:/config/vlan-2/#> tagged eth3,fw0
example:/config/vlan-2/#> end
example:/config/#> iface vlan1
example:/config/iface-vlan1/#> inet static 192.168.1.2/24
example:/config/iface-vlan1/inet-static-192.168.1.2/#> end
example:/config/iface-vlan1/#> end
example:/config/#> iface vlan2
example:/config/iface-vlan2/#> inet static 192.168.2.2/24
example:/config/iface-vlan2/inet-static-192.168.2.2/#> end
example:/config/iface-vlan2/#> end

Finally, we will configure WeOS to act as a DHCP server on VLAN 1 and 2, making sure to advertise our application as being the default gateway; also add a default route to the application for the local device:

example:/config/#> dhcp-server
example:/config/dhcp-server/#> subnet 192.168.1.0/24
example:/config/dhcp-server/subnet-192.168.1.0/#> gateway 192.168.1.1
example:/config/dhcp-server/subnet-192.168.1.0/#> name-server 8.8.8.8
example:/config/dhcp-server/subnet-192.168.1.0/#> end
example:/config/dhcp-server/#> subnet 192.168.2.0/24
example:/config/dhcp-server/subnet-192.168.2.0/#> gateway 192.168.2.1
example:/config/dhcp-server/subnet-192.168.2.0/#> name-server 8.8.8.8
example:/config/dhcp-server/subnet-192.168.2.0/#> end
example:/config/dhcp-server/#> end
example:/config/#> ip
example:/config/ip/#> route default 192.168.1.1
example:/config/ip/#> route default 192.168.2.1
example:/config/ip/#> leave
example:/#> copy running-config startup-config

Connectivity Test

Now we are ready to attach to our application verify connectivity:

example:/#> app attach fw
#

We should be able to reach WeOS on both VLANs as well as external resources on the Internet:

# ifup -a
ifup: interface lo already configured
# ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128
gre0@NONE        DOWN
gretap0@NONE     DOWN
erspan0@NONE     DOWN
ip_vti0@NONE     DOWN
ip6tnl0@NONE     DOWN
vlan1@eth0       UP             192.168.1.1/24 fe80::144a:32ff:fe1f:6eb/64
vlan2@eth0       UP             192.168.2.1/24 fe80::144a:32ff:fe1f:6eb/64
wan@gretap0      UP             198.18.221.120/24 fe80::207:7cff:fe65:f844/64
eth0@if21        UP             fe80::144a:32ff:fe1f:6eb/64
# ping -c1 192.168.1.2
PING 192.168.1.2 (192.168.1.2): 56 data bytes
64 bytes from 192.168.1.2: seq=0 ttl=64 time=0.181 ms

--- 192.168.1.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.181/0.181/0.181 ms
# ping -c1 192.168.2.2
PING 192.168.2.2 (192.168.2.2): 56 data bytes
64 bytes from 192.168.2.2: seq=0 ttl=64 time=0.266 ms

--- 192.168.2.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.266/0.266/0.266 ms
# ping -c1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=52 time=4.923 ms

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 4.923/4.923/4.923 ms
#

We can also verify that we cannot reach vlan1 or vlan2 on the WeOS side by connecting a host pc to eth4 (wan on the application side). But first we need to let that pc know how to reach that vlan:

###
### From the host pc
###
user@hostpc $ sudo ip route add 192.168.1.0/24 via <ip of wan interface> 
user@hostpc $ sudo ip route add 192.168.2.0/24 via <ip of wan interface> 

###
### Try to ping the application side of vlan1 and vlan2 (should succeed)
###
user@hostpc ping -c1 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.319 ms

--- 192.168.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.319/0.319/0.319/0.000 ms

user@hostpc $ ping -c1 192.168.2.1
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=0.306 ms

--- 192.168.2.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.306/0.306/0.306/0.000 ms

###
### Try to ping the WeOS side of vlan1 and vlan2 (should fail)
###
user@hostpc $ ping -c1 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.

--- 192.168.1.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

user@hostpc $ ping -c1 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.

--- 192.168.2.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms