WeOS
5.29.0

# 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.

- Prepared the Device
- Imported your image
- Labeled your image

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

« Previous Next »