jdx/mise · critical

refusing firewall default incoming {} over SSH: blocking rul

Error message

refusing firewall default incoming {} over SSH: blocking rule '{}' precedes a proven allow for peer {} on server port {}; reorder or narrow the rule, or set allow_lockout = true

What it means

Order-sensitive lockout guard for nftables and ufw: with deny-by-default incoming traffic, the first matching rule wins, and mise found a blocking (deny/reject) rule that matches the current SSH peer and server port before any unrestricted covering allow was seen (`covered` is still false). Applying this ruleset would drop the management session mid-apply, so mise refuses. Declaration order in the rules list is the rendered order for these backends.

Source

Thrown at src/system/firewall.rs:502

        for rule in rules.iter().filter(|rule| {
            rule.state == FirewallRuleState::Present
                && rule.direction == FirewallDirection::Incoming
                && rule
                    .protocol
                    .is_none_or(|protocol| protocol == FirewallProtocol::Tcp)
                && rule
                    .port
                    .is_none_or(|port| port.contains(connection.server_port))
                && rule
                    .source
                    .is_none_or(|source| source.contains(&connection.peer))
                && rule
                    .destination
                    .is_none_or(|destination| destination.contains(&connection.server))
        }) {
            if rule.action != FirewallAction::Allow {
                match backend {
                    Some(FirewallBackend::Nftables | FirewallBackend::Ufw) if !covered => bail!(
                        "refusing firewall default incoming {} over SSH: blocking rule '{}' precedes a proven allow for peer {} on server port {}; reorder or narrow the rule, or set allow_lockout = true",
                        self.default_incoming.ufw(),
                        rule.name,
                        connection.peer,
                        connection.server_port
                    ),
                    Some(FirewallBackend::Firewalld) => bail!(
                        "refusing firewall default incoming {} over SSH: blocking rule '{}' also covers peer {} on server port {}, and firewalld cannot guarantee the allow wins; narrow the rule or set allow_lockout = true",
                        self.default_incoming.ufw(),
                        rule.name,
                        connection.peer,
                        connection.server_port
                    ),
                    // An automatic backend is validated again after it is
                    // resolved.
                    _ => {}
                }
                continue;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Move the SSH allow rule above the broad deny in the rules array — order matters for nftables/ufw rendering.
  2. Narrow the blocking rule so it no longer matches the session: exclude your management IP from its source CIDR, or remove the SSH port from its port range.
  3. Switch the rule to a different interface or port scope that provably cannot cover peer + server port.
  4. With out-of-band console access, set `allow_lockout = true` to proceed despite the risk.

Example fix

# before (mise.toml) — deny precedes the allow
[[bootstrap.linux.firewall.rules]]
name = "block-lan"
source = "10.0.0.0/8"
action = "deny"

[[bootstrap.linux.firewall.rules]]
name = "ssh-ops"
source = "10.0.0.8/32"
port = 22
protocol = "tcp"
action = "allow"

# after — allow first, then the broad deny
[[bootstrap.linux.firewall.rules]]
name = "ssh-ops"
source = "10.0.0.8/32"
port = 22
protocol = "tcp"
action = "allow"

[[bootstrap.linux.firewall.rules]]
name = "block-lan"
source = "10.0.0.0/8"
action = "deny"
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-flight: an unrestricted-interface allow for the SSH port must precede any matching deny
# (manual review of declaration order in mise.toml; the guard itself is the executable check)

Try / catch

if ! mise bootstrap firewall apply 2>fw.err; then
  if grep -q "precedes a proven allow" fw.err; then
    # reorder rules in mise.toml: management allow above broad denies, then retry
    echo "reorder: put the ssh allow first in [[bootstrap.linux.firewall.rules]]"; exit 1
  fi
  cat fw.err; exit 1
fi

Prevention

When it happens

Trigger: `mise bootstrap firewall apply` with deny-ish `default_incoming`, a verified `SSH_CONNECTION`, backend nftables or ufw, and a rule like `action = "deny", source = "10.0.0.0/8"` (matching the peer) declared before the allow covering that peer/port — the loop hits the non-allow rule while `covered == false` and bails.

Common situations: Broad deny rules (geo-blocks, LAN denies, port-sweep blocks) written above the management allow; converting legacy iptables dumps where denies came first; appending the SSH allow later in the file during refactoring.

Related errors


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