jdx/mise · error

firewall rule '{name}' mixes IPv4 and IPv6 source/destinatio

Error message

firewall rule '{name}' mixes IPv4 and IPv6 source/destination networks

What it means

A firewall rule may filter on both `source` and `destination` CIDR networks, but both must belong to the same IP family. When one side is IPv4 and the other IPv6 (source.addr().is_ipv4() differs), the match cannot be expressed as a single backend rule and config parsing aborts with this error. The fields themselves parsed fine — only the family combination is rejected.

Source

Thrown at src/system/firewall.rs:413

                .interface
                .map(|interface| validate_interface(interface.trim()))
                .transpose()?;
            let source = rule
                .source
                .map(|source| source.parse::<IpNet>())
                .transpose()
                .wrap_err_with(|| format!("firewall rule '{name}' has an invalid source"))?;
            let destination = rule
                .destination
                .map(|destination| destination.parse::<IpNet>())
                .transpose()
                .wrap_err_with(|| format!("firewall rule '{name}' has an invalid destination"))?;
            if source.is_some_and(|source| {
                destination.is_some_and(|destination| {
                    source.addr().is_ipv4() != destination.addr().is_ipv4()
                })
            }) {
                bail!("firewall rule '{name}' mixes IPv4 and IPv6 source/destination networks");
            }
            let port = rule.port.map(FirewallPort::from_toml).transpose()?;
            if port.is_some() && rule.protocol.is_none() {
                bail!("firewall rule '{name}' sets port without protocol");
            }
            rules.push(FirewallRule {
                name,
                state: rule.state,
                direction: rule.direction,
                action: rule.action,
                port,
                protocol: rule.protocol,
                source,
                destination,
                interface,
            });
        }
        let ssh_connection = std::env::var("SSH_CONNECTION")

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Make both sides the same family: change the destination to an IPv4 net, or the source to an IPv6 net.
  2. Split into two rules — one IPv4-only, one IPv6-only — each with matching families.
  3. Omit `source` or `destination` when you only need to match one side (a missing side matches any address).

Example fix

# before
[[bootstrap.linux.firewall.rules]]
name = "lan-to-services"
source = "192.168.1.0/24"
destination = "fd00::/64"
port = 8443
protocol = "tcp"

# after
[[bootstrap.linux.firewall.rules]]
name = "lan-to-services-v4"
source = "192.168.1.0/24"
destination = "10.0.0.0/8"
port = 8443
protocol = "tcp"

[[bootstrap.linux.firewall.rules]]
name = "lan-to-services-v6"
source = "fd00:ab::/48"
destination = "fd00::/64"
port = 8443
protocol = "tcp"
Defensive patterns

Strategy: validation

Validate before calling

# pre-flight: source and destination must share an IP family
python3 - <<'PY'
import tomllib, ipaddress
fw = tomllib.load(open('mise.toml','rb')).get('bootstrap',{}).get('linux',{}).get('firewall',{})
for r in fw.get('rules',[]):
    s, d = r.get('source'), r.get('destination')
    if s and d and ipaddress.ip_network(s).version != ipaddress.ip_network(d).version:
        raise SystemExit(f"rule {r['name']}: mixed families {s} vs {d}")
PY

Prevention

When it happens

Trigger: A rule with `source = "192.168.1.0/24"` and `destination = "fd00::/64"` (or the reverse) — both parse as IpNet, then the `is_ipv4()` comparison fails. Common on dual-stack rules copied from nftables examples.

Common situations: Dual-stack hosts where the LAN is IPv4 but services sit on IPv6 (or vice versa); editing only one address when adapting an example rule; leftover ULA default (fd00::/8) combined with private IPv4 ranges.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/c4149a782a828bad. Report an issue: GitHub.