From caf303482bcdac49bd20aef7e2a14f4a5dda0b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 27 Jul 2026 09:38:26 -0400 Subject: [PATCH] incusd/firewall/nftables: Use a bridges set for cross-bridge NAT exclusions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- .../server/firewall/drivers/drivers_nftables.go | 16 ++++++++++++++++ .../drivers/drivers_nftables_templates.go | 15 ++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/internal/server/firewall/drivers/drivers_nftables.go b/internal/server/firewall/drivers/drivers_nftables.go index fde9b5942a..91652f39b3 100644 --- a/internal/server/firewall/drivers/drivers_nftables.go +++ b/internal/server/firewall/drivers/drivers_nftables.go @@ -262,6 +262,18 @@ func (d Nftables) NetworkSetup(networkName string, opts Opts) error { } } + // Add the network to the managed bridges set, traffic between those isn't NAT-ed. + tplFields := map[string]any{ + "namespace": nftablesNamespace, + "family": "inet", + "networkName": networkName, + } + + err := d.applyNftConfig(nftablesNetBridgesSet, tplFields) + if err != nil { + return fmt.Errorf("Failed adding network %q to the managed bridges set: %w", networkName, err) + } + if opts.SNATV4 != nil || opts.SNATV6 != nil { err := d.NetworkSetupOutboundNAT(networkName, opts.SNATV4, opts.SNATV6) if err != nil { @@ -320,6 +332,10 @@ func (d Nftables) NetworkClear(networkName string, _ bool, _ []uint) error { return fmt.Errorf("Failed clearing nftables rules for network %q: %w", networkName, err) } + // Remove the network from the managed bridges set. + // This will fail if the set doesn't exist or the network was never added to it. + _, _ = subprocess.RunCommand("nft", "delete", "element", "inet", nftablesNamespace, "bridges", fmt.Sprintf("{ %q }", networkName)) + // Attempt to delete our address sets. // This will fail so long as there are still rules referencing them (other networks). _ = d.RemoveIncusAddressSets("bridge") diff --git a/internal/server/firewall/drivers/drivers_nftables_templates.go b/internal/server/firewall/drivers/drivers_nftables_templates.go index 21d24aa7c2..9b158bfe77 100644 --- a/internal/server/firewall/drivers/drivers_nftables_templates.go +++ b/internal/server/firewall/drivers/drivers_nftables_templates.go @@ -26,14 +26,23 @@ chain fwd{{.chainSeparator}}{{.networkName}} { } `)) +var nftablesNetBridgesSet = template.Must(template.New("nftablesNetBridgesSet").Parse(` +set bridges { + type ifname + elements = { "{{.networkName}}" } +} +`)) + var nftablesNetOutboundNAT = template.Must(template.New("nftablesNetOutboundNAT").Parse(` +set bridges { + type ifname +} + chain pstrt{{.chainSeparator}}{{.networkName}} { type nat hook postrouting priority 100; policy accept; {{ range $ipFamily, $config := .rules }} - {{ range $config.ExcludeInterfaces }} - {{$ipFamily}} saddr {{$config.Subnet}} oifname "{{.}}" accept - {{ end }} + {{$ipFamily}} saddr {{$config.Subnet}} oifname @bridges accept {{ if $config.SNATAddress }} {{$ipFamily}} saddr {{$config.Subnet}} {{$ipFamily}} daddr != {{$config.Subnet}} snat {{$config.SNATAddress}} {{ else }}