Page MenuHomeVyOS Platform

Config-path-derived group/prefix-list membership (analogous to Junos `apply-path`)
In progress, NormalPublicFEATURE REQUEST

Description

Summary

Add a mechanism to populate firewall groups (network-group, address-group, their IPv6 equivalents, interface-group) and policy prefix-lists (prefix-list, prefix-list6, access-list, access-list6) from another part of the live configuration — e.g. every configured BGP neighbor address, or every connected interface subnet — instead of requiring the operator to duplicate those values by hand and keep the copy in sync forever.

Motivation / real-world use case

We run a DFZ edge router (VyOS 1.5.1, FRR 10.5.2) with several firewall groups whose entire purpose is to mirror facts that already exist elsewhere in the same configuration:

set firewall group network-group IPv4_EBGP_HOSTS network '62.141.40.144/32'
set firewall group network-group IPv4_EBGP_HOSTS network '62.141.40.145/32'
set firewall group network-group IPv4_EBGP_HOSTS network '185.1.155.254/32'
...

Every one of these addresses is *also* present, verbatim, under protocols bgp neighbor <address> — that's the whole point of the group (only allow BGP/SSH/etc. from our actual configured peers). The group is not encoding any independent decision; it is a hand-maintained copy of information the router already has. Every time a peer is added, removed, or renumbered, both places have to be updated, in the same commit, correctly, or the group silently drifts from reality with no commit-time check that it's still complete. We run four routers with this same pattern, which quadruples the chance of one of them missing an update.

The same problem shows up with policy prefix-list/prefix-list6: e.g. a prefix-list meant to represent "every prefix we originate" or "every connected subnet on this router" is, again, just a manual copy of information already present under protocols bgp ... network, interfaces ..., or protocols static route.

What we actually want

A way to declare, on the group/list itself, that its membership should be derived from another config-tree path rather than entered manually - conceptually identical to Junos's policy-options prefix-list <name> apply-path "<config path>", which populates a prefix-list directly from another part of the configuration (e.g. every configured BGP neighbor address).

Current workaround (and why it's not ideal)

Manually enumerate every value in the group/prefix-list and maintain it in parallel with the config it's meant to reflect. Concretely:

  • No commit-time check that the list is complete - a newly added BGP neighbor that isn't also added to the group silently loses whatever protection/permission the group was meant to grant.
  • Has to be repeated per router and kept in sync across a fleet (four routers, same pattern, four chances to get it wrong).
  • The group's own commit history stops communicating *why* an entry exists - it's just a flat list, disconnected from the config that actually motivates each entry.

Requested syntax (example)

set firewall group network-group IPv4_EBGP_HOSTS apply-path 'protocols bgp neighbor'

For config that's duplicated per-VRF (a very common shape - BGP, static routes, etc. all exist both under the default VRF and under vrf name <name> protocols ...), a wildcard segment to expand every instance of a tag node at that position:

set firewall group network-group IPv4_EBGP_HOSTS apply-path 'vrf name * protocols bgp neighbor'

Same option, same semantics, on a prefix-list:

set policy prefix-list OWN-ORIGINATED apply-path 'protocols bgp address-family ipv4-unicast network'

Why this matters beyond our specific case

This is a general pattern, not specific to our BGP-hosts example. We went through every "manually enumerated list" concept currently in VyOS (firewall.xml.in, policy.xml.in) and sorted them by whether they'd plausibly benefit:

ResourceFitWhy
firewall group network-group / ipv6-network-groupHighour motivating case - mirrors addresses/subnets that already exist elsewhere (BGP neighbors, interface addresses, static route targets)
firewall group address-group / ipv6-address-groupHighsame, for single addresses
firewall group interface-groupMedium-highe.g. "every interface belonging to VRF X", "every bond member"
firewall group mac-groupMediumcould mirror service dhcp-server ... static-mapping ... mac-address
policy prefix-list / prefix-list6High"every network we originate", "every connected subnet"
policy access-list / access-list6Highsame as prefix-list, older/simpler match syntax
firewall group domain-groupLowno obvious existing config to mirror
firewall group port-groupLowports are rarely already a config fact elsewhere
policy as-path-list / community-list / extcommunity-list / large-community-listLowthese encode routing *policy intent* (regexes/values chosen by the operator), not a copy of infrastructure that already exists elsewhere in config - there's no natural derivation source
policy route-mapNot applicablea match/action chain, not a membership list

Notably, VyOS already has a precedent for a group that isn't manually populated: firewall group remote-group fetches its membership from an external URL on an interval. apply-path is the same underlying idea - "this group's membership isn't typed in by hand, it's fetched from a source of truth" - just with the source being the router's own configuration instead of a remote HTTP(S) endpoint. This would not be a foreign concept to the project.

Architecturally, this looks achievable without a large new subsystem. firewall group membership is a single shared resource - not only firewall rules but also nat66, policy route, load-balancing wan, and system conntrack all reference these groups by name against the same underlying nftables set, so solving apply-path once at the group-definition layer (inside firewall.py's existing group-config assembly, before the nftables-defines.j2 template renders elements = { ... }) would benefit every one of those consumers automatically, with no per-consumer changes needed. policy prefix-list is a separate consumer with a different backend (FRR config, not nftables), but VyOS's Config class already exposes the two primitives such a resolver would need generically: list_nodes(path) (enumerate tag-node instances - e.g. every configured BGP neighbor address) and return_values(path) (read a multi-value leaf node). A small, shared, backend-agnostic path-resolver module - called independently by firewall.py and by policy.py, each merging the resolved values into the list they already assemble before rendering - would cover both without duplicating the path-walking logic.

Environment

  • VyOS 1.5.1 (circinus), official generic ISO, FRR 10.5.2
  • Verified against the vyos-1x source (rolling branch): python/vyos/config.py (list_nodes/return_values), data/templates/firewall/nftables-defines.j2 (group elements rendered from a plain Python list), interface-definitions/nat66.xml.in / policy_route.xml.in / load-balancing_wan.xml.in / system_conntrack.xml.in (all reference firewall group by name), interface-definitions/firewall.xml.in (remote-group as an existing precedent for non-manual group population), interface-definitions/policy.xml.in (prefix-list/access-list/as-path-list/community-list/extcommunity-list/large-community-list/route-map structure)

Details

Version
-
Is it a breaking change?
Unspecified (possibly destroys the router)
Issue type
Feature (new functionality)

Event Timeline

rherold created this object in space S1 VyOS Public.

I can create a patch for this if wanted

Viacheslav changed the task status from Open to In progress.Aug 7 2026, 9:11 AM
Viacheslav assigned this task to rherold.
Viacheslav triaged this task as Normal priority.

Update: the "Low fit" verdict above for policy as-path-list/community-list/extcommunity-list/large-community-list ("no natural derivation source") needs a caveat.

While designing our own BGP large-community scheme following RFC 8195 (https://www.rfc-editor.org/rfc/rfc8195), we found a case where a community's value actually is
directly derivable from existing config after all: RFC 8195 Function 3 (relation-to-origin: internal/customer/peer/transit) maps mechanically from a neighbor's
already-configured local-role (RFC 9234), and Functions 4/6 (selective no-export/prepend) use the neighbor's own already-known remote-as as their parameter.

This is a different shape of derivation than what apply-path proposes here, though: apply-path copies a list of existing values (e.g. every configured BGP neighbor
address) into a group/prefix-list. The RFC 8195 case instead needs a computed single value (own ASN + a fixed function number + a part derived from local-role/remote-as) —
no list, no copy. That's why we filed it as its own, separate feature request rather than folding it in here: T9173.

Flagging this only so the "no derivation source" statement above isn't read as a blanket claim for this config-type — for the general case (operator-authored regex/values)
it still holds.

@rherold I've closed the original PR in favor of pursuing the alternative approach discussed there. Let us keep this task open for further discussion.