Access Nftables output chain

Does anyone know how the nftables output chain can be accessed? I’m trying to control traffic sourced from the vyos system itself, and I can’t seem to make this work with ‘in’ ‘out’ or ‘local’

Sorry for the double post it came up uncategorized accidentally

Unfortunately, so far chain output can’t be controlled/modified through vyos cli. We are currently working on a new firewall re-writing which will provide us more options and flexibility.
For example, it may look something similar to:

vyos@hook-cli# set firewall ip 
Possible completions:
 > forward              IPv4 forward firewall
 > input                IPv4 input firewall
+> name                 IPv4 custom firewall
 > output               IPv4 output firewall

For now, you can use zone-based firewall

Could you share an example of this? I need to stop all traffic originating from the vyos router to reach any external host but allow routed traffic to pass (forward chain).

Is it possible to add this statically to the nftables.conf or is that not used or always overwritten perhaps?

Which version are you using?

Latest rolling version 1.4

Regarding your initial question, as @n.fort stated you cannot manage the nftables OUTPUT chain via the VyOS CLI. That said, I believe the VyOS CLI is just a modified Bash shell so you can always view the underlying nftables configuration by simply running sudo nft list ruleset.

Looking through your post history, I’m going to assume you understand conceptually how zone-based firewalls work so I’ll skip explaining that in answering your subsequent question. If I’m wrong about that, please reply accordingly and I, or someone else, can provide an explanation.

From a high level, you would implement this by creating at least three (3) zones; one (1) for the firewall itself (VyOS), one (1) for clients/hosts behind the firewall, and one (1) for clients/hosts outside the firewall. You’ll then assign the appropriate internal and external interface(s) to the appropriate internal and external zone(s). VyOS has an built-in “object” called local that represents the firewall itself and you reference that object when creating the zone for the firewall.

For each zone you create you will also create corresponding zone pairs (i.e. firewalls) that will regulate uni-directional traffic from a source zone to a destination zone. It is generally considered best practice to use the following naming convention for the firewalls: source zone name, hyphen (-), destination zone name (e.g. lan-wan, local-wan, etc.). Even if you don’t expect or require traffic between some zones it’s a good idea to create zone pairs for traffic in both directions and set the default-action to drop. You then attach those firewalls to the zones.

Here’s an example of the commands you would use to implement this:

set firewall zone lan default-action 'drop'
set firewall zone lan description 'Zone for trusted internal hosts'
set firewall zone lan from local firewall name 'local-lan'
set firewall zone lan from wan firewall name 'wan-lan'
set firewall zone lan interface 'eth1'

set firewall zone local default-action 'drop'
set firewall zone local description 'Zone for local firewall (VyOS)'
set firewall zone local from lan firewall name 'lan-local'
set firewall zone local from wan firewall name 'wan-local'
set firewall zone local local-zone

set firewall zone wan default-action 'drop'
set firewall zone wan description 'Zone for untrusted external hosts'
set firewall zone wan from lan firewall name 'lan-wan'
set firewall zone wan from local firewall name 'local-wan'
set firewall zone wan interface 'eth0'

From there, you would just modify the rules in the appropriate firewall to allow designated traffic according to whatever criteria you specify. Hope that helps!

1 Like

Yep, that’s perfect. Thanks so much for the detail. This gives me everything I need to do a proof of concept. From what I know of nftables I would assume the underlying configuration in this case does actually use the output chain but I will verify that when I test it out.

I have used vyos for a long time but I had the opportunity this week to operate it in a red team vs blue team cyber game. Based on the results I think I would have been better off with zone based fw rulesets :wink:

Coincidentally enough, I also work in cybersecurity and have undertaken the task of peeking under the hood with VyOS albeit with a version just prior to the switch to nftables. Even though I’m quite certain you’ll do the same I’ll go ahead and confirm your assumption about nftables.

There is a lot that I’m going to leave out for the sake of brevity and understanding but under the hood there are a couple of tables called vyos_filter for the IPv4 and IPv6 families in nftables. The zones and zone pairs I referenced in my previous reply are actually just regular chains in those tables. Regular chains are transparent to the kernel and traffic only moves through them when they are explicitly invoked via a jump statement.

Zones you create in VyOS correspond to chains that have VZONE in all caps in the name followed by the name you assigned the zone. The zone pairs/firewalls you create have NAME in all caps followed by the firewall name. There are base chains for INPUT, FORWARD, and OUTPUT that have hooks into the kernel so traffic hits those first.

The base chains have rules with logic that checks the inbound/outbound interface of ingress/egress traffic, respectively. Each of those rules has a jump statement targeting a specific zone (i.e. VZONE chain) that depends on the matching inbound/outbound interface. From there the traffic “jumps” to the corresponding zone pair (i.e. NAME chain) depending the matching inbound/outbound interface. The rules you specify in the zone pair firewalls (i.e. lan-wan, local-wan, etc.) are in those NAME chains.

Years ago, I dumped several of the VyOS configs out to text files and decided to create my own homebrew router using Saltstack. I did manage to put something together but it was far more trouble than it was worth. The reason I use VyOS is because it’s the perfect abstraction layer situated just high enough above the OS. I don’t need to worry about manually installing all the dependencies and configuring the kernel/OS but I also have an intuitive interface for interacting with all those dependencies in a consistent, easily reproducible manner.

Whipped this up in the airport on my laptop in hyper-v, I only had a 1.3 rolling image locally so I had to tweak the syntax a bit but it worked. The end result was outbound traffic from the router was denied with an operation not permitted message. In the game the router had a trojan implanted and was creating reverse shells to systems inside the network back to red team controlled assets. This zone policy would have been pretty helpful, but now I have learned.

Can you also share the path for the vyos generated nft configuration? /etc/nftables.conf is just the default.

Thanks again for the feedback.

vyos@vyos:~$ ping
PING ( 56(84) bytes of data.
ping: sendmsg: Operation not permitted

Someone else can correct me if I’m wrong but I don’t believe the nftables configs are written to disk in an .conf file. I’ve always obtained that portion of the configuration by redirecting the output of sudo nft list ruleset to a text file. This is pure speculation on my part but I suspect that the nftables configuration is dynamically generated by parsing/compiling the VyOS configuration and loaded into memory as the running configuration. Other parts of the VyOS configuration, however, can be found in locations you’d expect them to be in. For example, kernel settings can be found in /etc/sysctl.d.

After boot they generated based on /config/config.boot during commit
For 1.4 they also regenerated per each firewall commit
In general we don’t have any permanent config for daemons/services as it dynamically generated from config/commit
But sure that some drivers/sysctl/kernel options should be enabled by default without configs on the user site

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.