Page MenuHomeVyOS Platform

Zone-Based Firewalling in VyOS Sagitta 1.4
Closed, ResolvedPublicBUG

Description

Motivation:

Zone-Based Firewalls (ZBF) are a well-known and relied-upon pattern when dealing with more complex network setups. The lack of such feature quickly makes firewall configurations a nightmare.

The Bug:

The Sagitta 1.4 documentation details how to set up ZBF: https://docs.vyos.io/en/latest/configexamples/zone-policy.html

set firewall zone dmz default-action drop
set firewall zone dmz interface eth0.30
set firewall zone dmz from lan firewall ipv6-name lan-dmz-6

Observed behavior

However, in VyOS 1.4-rolling-202308240020 (nightly), there is no such a configuration option:

vyos@vyos# set firewall 
Possible completions:
 > global-options       Global Options
 > group                Firewall group
 > ipv4                 IPv4 firewall
 > ipv6                 IPv6 firewall

      
[edit]
vyos@vyos# set firewall ipv4 
Possible completions:
 > forward              IPv4 forward firewall
 > input                IPv4 input firewall
+> name                 IPv4 custom firewall
 > output               IPv4 output firewall

Either the documentation is wrong or the function is indeed gone missing (Dropped?? Why?).

Expected behavior:

  • Either: Explain ZBF for VyOS 1.4 in the documentation as it would work now
  • Or: Re-integrate ZBF into VyOS 1.4

Details

Difficulty level
Unknown (require assessment)
Version
1.4-rolling-202308240020, 1.5-rolling-202310120020
Why the issue appeared?
Will be filled on close
Is it a breaking change?
Unspecified (possibly destroys the router)
Issue type
Bug (incorrect behavior)

Event Timeline

The firewall refactoring released 4th aug 2023 only (so far) took care about the documentation in the configuration section:

https://docs.vyos.io/en/latest/configuration/firewall/index.html

So yes, the blueprint section (aka examples) must be updated aswell.

I would also like to know if zone based firewall still work or support is removed?

n.fort changed the task status from Open to In progress.Oct 13 2023, 2:10 PM
n.fort claimed this task.
n.fort added a project: VyOS 1.5 Circinus.
n.fort changed Version from 1.4-rolling-202308240020 (nightly) to 1.4-rolling-202308240020, 1.5-rolling-202310120020.
Viacheslav changed the task status from In progress to Needs testing.Oct 21 2023, 1:48 PM

@Viacheslav @n.fort Regarding https://github.com/vyos/vyos-1x/pull/2388: I just tested on 1.4-rolling-202311021131: ZBF config from 1.4-rolling-202304120317 loaded without a hitch.
This is a 8 zone, 959 line config.

Good to know it worked @marc_s . Thanks for letting us know!

The migration scripts still have an issue @n.fort I sent you the issues with loading a 1.4 rolling to 1.5 in slack

@n.fort Unfortunately, I'm hitting an issue with traffic sent from the router itself.
Running 1.4-rolling-202311021131.
Interfaces eth0 and pppoe0 are WAN. Interfaces bond0* are LAN.
For example, running dig google.nl @9.9.9.9 from the VyOS CLI will fail. The kernel log says:

$ sudo dmesg -T | grep -i '9.9.9.9'
[Sat Nov  4 12:40:14 2023] [ipv4-INTERNET_TO_LOCAL-default-D]IN= OUT=pppoe0 SRC=x.x.x.x DST=9.9.9.9 LEN=81 TOS=0x00 PREC=0x00 TTL=64 ID=20808 PROTO=UDP SPT=34297 DPT=53 LEN=61

Notice the chain: ipv4-INTERNET_TO_LOCAL (belonging to inbound traffic), but OUT=pppoe0 (outbound traffic): this traffic is matched incorrectly by nft!

To check what went wrong, I started comparing the generated nft config. This is the NEW config:

$ sudo nft list chain vyos_filter VZONE_LOCAL_OUT
        chain VZONE_LOCAL_OUT {
                oifname "lo" counter packets 422 bytes 36282 return
                oifname "bond0.40" counter packets 109 bytes 3488 jump NAME_CAST_TO_LOCAL
                oifname "bond0.40" counter packets 0 bytes 0 return
                oifname "bond0.70" counter packets 0 bytes 0 jump NAME_GEDEELD_TO_LOCAL
                oifname "bond0.70" counter packets 0 bytes 0 return
                oifname { "bond0.7", "bond0.30", "bond0.90", "bond0.88" } counter packets 1381 bytes 136408 jump NAME_GUESTS_TO_LOCAL
                oifname { "bond0.7", "bond0.30", "bond0.90", "bond0.88" } counter packets 0 bytes 0 return
                oifname { "eth0", "pppoe0" } counter packets 1056 bytes 108666 jump NAME_INTERNET_TO_LOCAL
                oifname { "eth0", "pppoe0" } counter packets 0 bytes 0 return
                oifname "bond0.80" counter packets 48 bytes 4722 jump NAME_KINDEREN_TO_LOCAL
                oifname "bond0.80" counter packets 0 bytes 0 return
                oifname { "bond0.1", "podman-cntr-net" } counter packets 5451 bytes 577267 jump NAME_MANAGEMENT_TO_LOCAL
                oifname { "bond0.1", "podman-cntr-net" } counter packets 0 bytes 0 return
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 0 bytes 0 jump NAME_REMOTE_TO_LOCAL
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 0 bytes 0 return
                counter packets 0 bytes 0 log prefix "[ipv4-zone_LOCAL-default-D]" drop comment "zone_LOCAL default-action drop"
        }

Notice the jump chains for traffic in VZONE_LOCAL_OUT being wrong. This is how it _should_ look (from version 1.4-rolling-202304120317):

$ sudo nft list chain vyos_filter VZONE_LOCAL_OUT
        chain VZONE_LOCAL_OUT {
                oifname "lo" counter packets 9922 bytes 2865943 return
                oifname "bond0.40" counter packets 835 bytes 26768 jump NAME_LOCAL_TO_ALL
                oifname "bond0.40" counter packets 835 bytes 26768 return
                oifname "bond0.70" counter packets 0 bytes 0 jump NAME_LOCAL_TO_ALL
                oifname "bond0.70" counter packets 0 bytes 0 return
                oifname { "bond0.7", "bond0.30", "bond0.90", "bond0.88" } counter packets 54 bytes 9622 jump NAME_LOCAL_TO_ALL
                oifname { "bond0.7", "bond0.30", "bond0.90", "bond0.88" } counter packets 54 bytes 9622 return
                oifname { "eth0", "pppoe0", "eth1.281" } counter packets 5558 bytes 716558 jump NAME_LOCAL_TO_ALL
                oifname { "eth0", "pppoe0", "eth1.281" } counter packets 5558 bytes 716558 return
                oifname "bond0.80" counter packets 129 bytes 48375 jump NAME_LOCAL_TO_ALL
                oifname "bond0.80" counter packets 129 bytes 48375 return
                oifname { "bond0.1", "podman-cntr-net" } counter packets 12364 bytes 1888823 jump NAME_LOCAL_TO_ALL
                oifname { "bond0.1", "podman-cntr-net" } counter packets 12364 bytes 1888823 return
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 1761 bytes 189214 jump NAME_LOCAL_TO_ALL
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 1761 bytes 189214 return
                counter packets 0 bytes 0 drop comment "zone_LOCAL default-action drop"
        }

There you see the jump chains are NAME_LOCAL_TO_ALL, just how I set it up:

$ show zone | commands | match LOCAL_TO_ALL
set zone CAST from LOCAL firewall name 'LOCAL_TO_ALL'
set zone GEDEELD from LOCAL firewall name 'LOCAL_TO_ALL'
set zone GUESTS from LOCAL firewall name 'LOCAL_TO_ALL'
set zone INTERNET from LOCAL firewall name 'LOCAL_TO_ALL'
set zone KINDEREN from LOCAL firewall name 'LOCAL_TO_ALL'
set zone MANAGEMENT from LOCAL firewall name 'LOCAL_TO_ALL'
set zone REMOTE from LOCAL firewall name 'LOCAL_TO_ALL'

In original @ https://github.com/nicolas-fort/vyos-1x/blob/8269866a5d467c8c05e770720a268e1f3cb868c0/data/templates/firewall/nftables-zone.j2 I see (R52-R53):

{%         if zone_conf.from_local is vyos_defined %}
{%             for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}

and in current @ https://github.com/nicolas-fort/vyos-1x/blob/9975ad209704ab9d0fda32324d0432f257c67668/data/templates/firewall/nftables-zone.j2 I see (R42-R43):

{%         if zone_conf.from is vyos_defined %}
{%             for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}

However, I couldn't file source code references to the zone_conf.from_local variable in the latest code, that I _did_ find in the old code (https://github.com/nicolas-fort/vyos-1x/blob/0c4a9e232627c38e4a1915ae97b1accaee06c841/src/conf_mode/zone_policy.py). But maybe I'm rambling now :-)

Could you please investigate? Happy to test, let me know.
Thanks!

Working on it! Thanks for the details!

Thanks, I'll wait for the merge and test the new iso ASAP.

That looks better:

        chain VZONE_LOCAL_OUT {
                oifname "lo" counter packets 387 bytes 33672 return
                oifname "bond0.40" counter packets 14 bytes 496 jump NAME_LOCAL_TO_ALL
                oifname "bond0.40" counter packets 0 bytes 0 return
                oifname "bond0.70" counter packets 0 bytes 0 jump NAME_LOCAL_TO_ALL
                oifname "bond0.70" counter packets 0 bytes 0 return
r packets 0 bytes 0 jump NAME_LOCAL_TO_ALL
                oifname { "bond0.7", "bond0.30", "bond0.90", "bond0.88" } counter packets 0 bytes 0 return
                oifname { "eth0", "pppoe0", "eth1.281" } counter packets 3 bytes 180 jump NAME_LOCAL_TO_ALL
                oifname { "eth0", "pppoe0", "eth1.281" } counter packets 0 bytes 0 return
                oifname "bond0.80" counter packets 2 bytes 80 jump NAME_LOCAL_TO_ALL
                oifname "bond0.80" counter packets 0 bytes 0 return
                oifname { "bond0.1", "podman-cntr-net" } counter packets 2 bytes 128 jump NAME_LOCAL_TO_ALL
                oifname { "bond0.1", "podman-cntr-net" } counter packets 0 bytes 0 return
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 0 bytes 0 jump NAME_LOCAL_TO_ALL
                oifname { "wg0", "vti0", "vtun0", "podman-ts-net" } counter packets 0 bytes 0 return
                counter packets 0 bytes 0 drop comment "zone_LOCAL default-action drop"
        }
vyos@gw:~$ dig +short google.nl @9.9.9.9
172.217.23.195

Nice :-) Thanks @n.fort

I'm marking this one as resolved since ZBF was already re-introduced.

For errors that may appear in the future regarding ZBF, please submit new bug-report.