CVE-2025-52889
Description
Incus is a system container and virtual machine manager. When using an ACL on a device connected to a bridge, Incus version 6.12 and 6.13 generates nftables rules for local services (DHCP, DNS...) that partially bypass security options security.mac_filtering, security.ipv4_filtering and security.ipv6_filtering. This can lead to DHCP pool exhaustion and opens the door for other attacks. A patch is available at commit 2516fb19ad8428454cb4edfe70c0a5f0dc1da214.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/lxc/incus/v6Go | < 6.14.0 | 6.14.0 |
Affected products
1Patches
22516fb19ad84incusd/firewall/nftables: Fix ordering of basic rules
1 file changed · +24 −25
internal/server/firewall/drivers/drivers_nftables_templates.go+24 −25 modified@@ -192,38 +192,19 @@ var nftablesInstanceBridgeFilter = template.Must(template.New("nftablesInstanceB chain in{{.chainSeparator}}{{.deviceLabel}} { type filter hook input priority -200; policy accept; - # Basic connectivity - {{ if or .aclInDropRules .aclInRejectRules .aclInAcceptRules .aclOutDropRules .aclOutAcceptRules .aclInDefaultRule }} - - {{ if .dnsIPv4 }} - {{ range .dnsIPv4 }} - iifname "{{$.hostName}}" ip daddr "{{.}}" tcp dport 53 accept - iifname "{{$.hostName}}" ip daddr "{{.}}" udp dport 53 accept - {{ end }} - {{ end }} - - {{ if .dnsIPv6 }} - {{ range .dnsIPv6 }} - iifname "{{$.hostName}}" ip6 daddr "{{.}}" tcp dport 53 accept - iifname "{{$.hostName}}" ip6 daddr "{{.}}" udp dport 53 accept - {{ end }} - {{ end }} - - iifname "{{.hostName}}" ether type ip ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp dport 67 accept - iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::1:2 udp dport 547 accept - iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::2 icmpv6 type 133 accept - {{ end }} - # MAC filtering {{ if .macFiltering }} iifname "{{.hostName}}" ether saddr != {{.hwAddr}} drop iifname "{{.hostName}}" ether type arp arp saddr ether != {{.hwAddr}} drop iifname "{{.hostName}}" ether type ip6 icmpv6 type 136 @nh,528,48 != {{.hwAddrHex}} drop {{ end }} + {{ if or .aclInDropRules .aclInRejectRules .aclInAcceptRules .aclOutDropRules .aclOutAcceptRules .aclInDefaultRule .ipv4NetsList }} + iifname "{{.hostName}}" ether type ip ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp dport 67 accept + {{ end }} + # IPv4 filtering {{ if .ipv4NetsList }} - iifname "{{.hostName}}" ether type ip ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp dport 67 accept iifname "{{.hostName}}" ether type arp arp saddr ip != { {{.ipv4NetsList}} } drop iifname "{{.hostName}}" ether type ip ip saddr != { {{.ipv4NetsList}} } drop {{ end }} @@ -232,10 +213,13 @@ chain in{{.chainSeparator}}{{.deviceLabel}} { iifname "{{.hostName}}" ether type ip drop {{ end }} - # IPv6 filtering - {{ if .ipv6NetsList }} + {{ if or .aclInDropRules .aclInRejectRules .aclInAcceptRules .aclOutDropRules .aclOutAcceptRules .aclInDefaultRule .ipv6NetsList }} iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::1:2 udp dport 547 accept iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::2 icmpv6 type 133 accept + {{ end }} + + # IPv6 filtering + {{ if .ipv6NetsList }} iifname "{{.hostName}}" ether type ip6 icmpv6 type 134 drop iifname "{{.hostName}}" ether type ip6 icmpv6 type 136 {{.ipv6NetsPrefixList}} drop iifname "{{.hostName}}" ether type ip6 ip6 saddr != { {{.ipv6NetsList}} } drop @@ -249,6 +233,21 @@ chain in{{.chainSeparator}}{{.deviceLabel}} { iifname "{{.hostName}}" ether type arp accept iifname "{{.hostName}}" ip6 nexthdr ipv6-icmp icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } accept + + {{ if .dnsIPv4 }} + {{ range .dnsIPv4 }} + iifname "{{$.hostName}}" ip daddr "{{.}}" tcp dport 53 accept + iifname "{{$.hostName}}" ip daddr "{{.}}" udp dport 53 accept + {{ end }} + {{ end }} + + {{ if .dnsIPv6 }} + {{ range .dnsIPv6 }} + iifname "{{$.hostName}}" ip6 daddr "{{.}}" tcp dport 53 accept + iifname "{{$.hostName}}" ip6 daddr "{{.}}" udp dport 53 accept + {{ end }} + {{ end }} + {{ end }} # ACLs
a7c33301738aincusd/firewall: Add basic rules on nftables
5 files changed · +70 −6
internal/server/device/nic_bridged.go+41 −3 modified@@ -1182,11 +1182,50 @@ func (d *nicBridged) setFilters() (err error) { defer revert.Fail() revert.Add(func() { d.removeFilters(config) }) - IPv4Nets, IPv6Nets, err := allowedIPNets(config) + ipv4Nets, ipv6Nets, err := allowedIPNets(config) if err != nil { return err } + var ipv4DNS []string + var ipv6DNS []string + + if d.network != nil { + netConfig := d.network.Config() + + ipv4DNS = []string{} + ipv6DNS = []string{} + + // Pull directly configured DNS name servers (if any). + nsList := util.SplitNTrimSpace(netConfig["dns.nameservers"], ",", -1, false) + for _, ns := range nsList { + if ns == "" { + continue + } + + nsIP := net.ParseIP(ns) + if nsIP == nil { + return fmt.Errorf("Invalid DNS nameserver") + } + + if nsIP.To4() == nil { + ipv4DNS = append(ipv4DNS, ns) + } else { + ipv6DNS = append(ipv6DNS, ns) + } + } + + // Add IPv4 router. + if netConfig["ipv4.address"] != "" && netConfig["ipv4.address"] != "none" { + ipv4DNS = append(ipv4DNS, strings.Split(netConfig["ipv4.address"], "/")[0]) + } + + // Add IPv6 router. + if netConfig["ipv6.address"] != "" && netConfig["ipv6.address"] != "none" { + ipv6DNS = append(ipv6DNS, strings.Split(netConfig["ipv6.address"], "/")[0]) + } + } + var aclRules []firewallDrivers.ACLRule var aclNames []string if config["security.acls"] != "" { @@ -1202,10 +1241,9 @@ func (d *nicBridged) setFilters() (err error) { if err != nil { return err } - } - err = d.state.Firewall.InstanceSetupBridgeFilter(d.inst.Project().Name, d.inst.Name(), d.name, d.config["parent"], d.config["host_name"], d.config["hwaddr"], IPv4Nets, IPv6Nets, d.network != nil, util.IsTrue(config["security.mac_filtering"]), aclRules) + err = d.state.Firewall.InstanceSetupBridgeFilter(d.inst.Project().Name, d.inst.Name(), d.name, d.config["parent"], d.config["host_name"], d.config["hwaddr"], ipv4Nets, ipv6Nets, ipv4DNS, ipv6DNS, d.network != nil, util.IsTrue(config["security.mac_filtering"]), aclRules) if err != nil { return err }
internal/server/firewall/drivers/drivers_nftables.go+5 −1 modified@@ -401,7 +401,7 @@ func (d Nftables) instanceDeviceLabel(projectName, instanceName, deviceName stri } // InstanceSetupBridgeFilter sets up the filter rules to apply bridged device IP filtering. -func (d Nftables) InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, parentManaged bool, macFiltering bool, aclRules []ACLRule) error { +func (d Nftables) InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, IPv4DNS []string, IPv6DNS []string, parentManaged bool, macFiltering bool, aclRules []ACLRule) error { deviceLabel := d.instanceDeviceLabel(projectName, instanceName, deviceName) mac, err := net.ParseMAC(hwAddr) @@ -480,6 +480,10 @@ func (d Nftables) InstanceSetupBridgeFilter(projectName string, instanceName str tplFields["aclOutAcceptRules"] = nftRules.outAcceptRules tplFields["aclOutDefaultRule"] = nftRules.defaultOutRule + // Required for basic connectivity + tplFields["dnsIPv4"] = IPv4DNS + tplFields["dnsIPv6"] = IPv6DNS + err = d.applyNftConfig(nftablesInstanceBridgeFilter, tplFields) if err != nil { return fmt.Errorf("Failed adding bridge filter rules for instance device %q (%s): %w", deviceLabel, tplFields["family"], err)
internal/server/firewall/drivers/drivers_nftables_templates.go+22 −0 modified@@ -190,6 +190,24 @@ chain in{{.chainSeparator}}{{.deviceLabel}} { {{ if or .aclInDropRules .aclInRejectRules .aclInAcceptRules .aclOutDropRules .aclOutAcceptRules .aclInDefaultRule }} ct state established,related accept + {{ if .dnsIPv4 }} + {{ range .dnsIPv4 }} + iifname "{{$.hostName}}" ip daddr "{{.}}" tcp dport 53 accept + iifname "{{$.hostName}}" ip daddr "{{.}}" udp dport 53 accept + {{ end }} + {{ end }} + + {{ if .dnsIPv6 }} + {{ range .dnsIPv6 }} + iifname "{{$.hostName}}" ip6 daddr "{{.}}" tcp dport 53 accept + iifname "{{$.hostName}}" ip6 daddr "{{.}}" udp dport 53 accept + {{ end }} + {{ end }} + + iifname "{{.hostName}}" ether type ip ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp dport 67 accept + iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::1:2 udp dport 547 accept + iifname "{{.hostName}}" ether type ip6 ip6 saddr fe80::/10 ip6 daddr ff02::2 icmpv6 type 133 accept + iifname "{{.hostName}}" ether type arp accept iifname "{{.hostName}}" ip6 nexthdr ipv6-icmp icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } accept {{ end }} @@ -323,6 +341,10 @@ chain out{{.chainSeparator}}{{.deviceLabel}} { oifname "{{.hostName}}" ether type arp accept oifname "{{.hostName}}" ip6 nexthdr ipv6-icmp icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } accept + oifname "{{.hostName}}" udp sport 67 udp dport 68 accept + oifname "{{.hostName}}" ip6 saddr fe80::/10 udp sport 547 accept + oifname "{{.hostName}}" ip6 saddr fe80::/10 icmpv6 type {1, 2, 3, 4, 128, 134, 135, 136, 143} accept + # Network ACLs {{ range .aclOutDropRules}} {{.}}
internal/server/firewall/drivers/drivers_xtables.go+1 −1 modified@@ -811,7 +811,7 @@ func (d Xtables) instanceDeviceIPTablesComment(projectName string, instanceName // If the parent bridge is managed by Incus then parentManaged argument should be true so that the rules added can // use the iptablesChainACLFilterPrefix chain. If not they are added to the main filter chains directly (which only // works for unmanaged bridges because those don't support ACLs). -func (d Xtables) InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, parentManaged bool, macFiltering bool, aclRules []ACLRule) error { +func (d Xtables) InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, IPv4DNS []string, IPv6DNS []string, parentManaged bool, macFiltering bool, aclRules []ACLRule) error { if len(aclRules) > 0 { return fmt.Errorf("ACL rules not supported for xtables bridge filtering") }
internal/server/firewall/firewall_interface.go+1 −1 modified@@ -27,7 +27,7 @@ type Firewall interface { NetworkApplyAddressSets(sets []drivers.AddressSet, nftTable string) error NetworkDeleteAddressSetsIfUnused(nftTable string) error - InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, parentManaged bool, macFiltering bool, aclRules []drivers.ACLRule) error + InstanceSetupBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet, IPv4DNS []string, IPv6DNS []string, parentManaged bool, macFiltering bool, aclRules []drivers.ACLRule) error InstanceClearBridgeFilter(projectName string, instanceName string, deviceName string, parentName string, hostName string, hwAddr string, IPv4Nets []*net.IPNet, IPv6Nets []*net.IPNet) error InstanceSetupProxyNAT(projectName string, instanceName string, deviceName string, forward *drivers.AddressForward) error
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-9q7c-qmhm-jv86ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-52889ghsaADVISORY
- github.com/lxc/incus/commit/2516fb19ad8428454cb4edfe70c0a5f0dc1da214nvdWEB
- github.com/lxc/incus/commit/a7c33301738aede3c035063e973b1d885d9bac7cnvdWEB
- github.com/lxc/incus/security/advisories/GHSA-9q7c-qmhm-jv86nvdWEB
News mentions
0No linked articles in our index yet.