There’s a few tickets open relating to issues with hairpinning on dynamic interfaces, where the rule needs to know the interface address and it cannot be statically assigned, including:
- ⚓ T4375 hairpin nat (nat reflector) "hijacks" all outgoing traffic on specified port to any destination
- ⚓ T2196 Dynamic ipv4 interface list hairpin
Often these problems have been fixed via split horizon DNS (internal DNS resolves to internal IP, avoiding the NAT), something like EdgeOS’ ADDRvX_iface automatic address groups or user-scripted rough equivalents.
I’d like to have a crack at an integrated solution since it’s a common and annoying problem, and I’ve got a couple of ideas on how best to present it in the config.
Under the hood, the foundation piece will likely be a set of dhclient/pppd/if-up/down.d hooks that populate an nft set based on IP config and changes.
1. Allow a dynamic-group to be populated from any interface with explicit config
Something along the lines of:
set firewall group dynamic-group address-group WAN-EXT
set interface ethernet eth0 address dhcp
set interface ethernet eth0 tracking-address-group WAN-EXT
Each DA_WAN-EXT
set would be populated appropriately as IPs are assigned and changed.
I’ve targeted dynamic-groups as we already know they’ll be changing at runtime, there won’t be any assumptions made about config matching runtime state. I haven’t yet checked out whether they’re subject to unexpected flushing or other potential problems that would break this idea.
The user gets to control exactly how the groups are named and used, multiple interfaces can feed a single set (hopefully - I still need to check that we can track state in all the required hooks for adds/removes) and backs into the dynamic-group work already done.
2. Create a tracking-group type that works in a similar fashion
Very similar to the above, configured as:
set firewall group tracking-group WAN-EXT interface eth0
set firewall group tracking-group WAN-EXT interface pppoe1
set interface ethernet eth0 address dhcp
Working on a similar schema, but not exposed at all for additional explicit configuration.
3. Add match syntax in the firewall/NAT/etc rules, to match interface addresses:
In this scenario, we automatically create internal sets along the lines of IA_eth0
for any configured interface and always populate them from hooks.
Then, configuring at the match side, we always have access to it as long as the interfaces exist:
set interface ethernet eth0 address dhcp
set nat destination rule 100 destination interface-address eth0
set firewall ipv4 output filter rule 100 source interface-address pppoe1 [...]
This won’t need to worry about mixing together multiple interface states into a single set and makes it explicit what we’re doing - there’s no magical virtual address-group.
4. Automatic address-groups with defined names, similar to EdgeOS
The internal implementation of this is similar to extending the match syntax, but we move it into firewall group handling.
It’ll be immediately familiar to people coming from EdgeOS and is likely the simplest option to implement.
To avoid any namespace clashes with existing configs, especially when people have munged an old EdgeOS config into VyOS, it would ideally be defined as a new type of tracking-group, similar to #2 above.
Anyone have any opinions or suggestions?