jdx/mise · error
refusing firewall default incoming
Error message
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 What it means
Same lockout scenario as the nftables/ufw ordering error but for firewalld: a blocking rule earlier in the list also matches your SSH peer and server port, and firewalld's rich-rule/zone semantics cannot guarantee your later allow rule takes precedence. mise refuses the change rather than risk severing the SSH session.
Solutions
- Narrow the blocking rule so it does not cover your SSH peer and server port.
- Add an explicit high-priority allow for your SSH peer/port that firewalld will honor ahead of the block (rich rule ordering).
- Set allow_lockout = true if you accept the lockout risk and have out-of-band access.
Example fix
// before [[firewall.rules]] name = "block-untrusted" direction = "incoming" action = "deny" [[firewall.rules]] name = "allow-ssh" port = 22 protocol = "tcp" action = "allow" // after [[firewall.rules]] name = "allow-ssh" port = 22 protocol = "tcp" action = "allow" [[firewall.rules]] name = "block-untrusted" direction = "incoming" action = "deny"
Defensive patterns
Strategy: validation
Validate before calling
if backend == "firewalld" {
let shadows = rules.iter().take_while(|r| !r.action.preserves_access() || !r.matches(peer, port))
.any(|r| !r.action.preserves_access() && r.could_match(peer, port));
if shadows { eprintln!("firewalld: narrow the blocking rule or add a higher-priority SSH allow"); }
} Type guard
fn no_firewalld_shadowing(rules: &[FirewallRule], peer: IpAddr, port: u16) -> bool {
!rules.iter().any(|r| !r.action.preserves_access() && r.could_match(peer, port))
} Try / catch
match result {
Err(e) if e.to_string().contains("firewalld cannot guarantee") => {
eprintln!("narrow the blocking rule or add a prioritized SSH allow rich rule");
}
other => other?,
} Prevention
- Model firewalld rules with priority-aware rich rules rather than porting nftables-style lists.
- Verify with `firewall-cmd --list-all` which zone rules exist before switching backends.
- Test with a dry run on firewalld hosts; ordering guarantees differ from nftables/ufw.
When it happens
Trigger: `mise bootstrap firewall apply` (validate_safety) over SSH with backend = "firewalld" where an earlier rule whose action does not preserve access matches connection.peer/server_port, and no earlier proven allow exists.
Common situations: Porting an nftables-style deny-first rule list to firewalld; broad drop rules in the public zone shadowing a rich allow rule; migrating hardened configs between backends without reordering.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- refusing firewall default incoming
- refusing firewall default incoming
- refusing firewall default incoming
- refusing firewall default incoming
- firewall rule ' ' uses interface matching, which firewalld…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/34b5e5ff07039f33.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/firewall.rs:518
.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.preserves_access() {
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;
}
// SSH_CONNECTION does not identify the ingress interface. An
// interface-constrained allow cannot prove that it preserves this
// session, so keep looking for an unrestricted covering allow.
if rule.interface.is_none() {
covered = true;
if matches!(View on GitHub (pinned to 533346cc37)