Tips for reducing complexity of dual-stack firewall?


I am continuing to work on switching from pfSense to VyOS for my home router and I think the last thing I need to sort out are the firewall rules.

I have the following zones:

  • PRIVATE: contains the LAN and WAN modem admin interface
  • PUBLIC: The Internet - contains the PPPoE interface
  • DMZ: contain the DMZ interface for servers
  • LOCAL: The firewall itself

I am running the router dual-stack (IPv6 and IPv4), which has resulted in a lot of configuration to write. In the end I write a little script to generate the VyOS commands, because there were so many commands / permutations to write.

This is all just ‘boilerplate’ before I write any extra rules.

I think the complexity is created by:

  • All the different permutations of the 4 different zones talking to each other
  • Having to write nearly everything twice (IPv4 and IPv6)
  • Having to enable stateful firewall for each ruleset

Am I missing something? Is there a way this could made shorter / simpler?


Just discovered while browsing the CLI completion options, that stateful firewall can be enabled globally. I think by doing:

set firewall state-policy established action accept
set firewall state-policy related action accept
set firewall state-policy invalid action drop

That will result in a lot less firewall rules. Any downsides?

Not had any response to this but I am pretty sure there the answer is that if you want a dual-stack firewall you have to duplicate all the rules, once for IPv4 and once for IPv6. Even for a small firewall, this starts to become quite hard to manage - to ensure that all the rules are consistently applied to both IPv4 and IPv6.

I have considerably extended my Ruby script to take zone configuration and firewall rules as JSON and then outputs a VyOS configuration file (or a series of set commands).

For example this JSON (95 lines long):

Input JSON

Generates this VyOS configuration (279 lines long):

Output Config File

It is still work in progress, and I am yet to actually apply it to my router.

I wrote a ruby class to make generating VyOS configuration easy.

For example this:

config =
config.interfaces.ethernet('eth0').address = ['', '2001:db8::ffff/64']

Results in this:

interfaces {
    ethernet eth0 {
        address ''
        address '2001:db8::ffff/64'

I have just found this!
Looks like I am not the only person looking to try and make it easier to build VyOS firewall configurations:

Although it doesn’t do anything with IPv6.