jdx/mise · error
refusing firewall default incoming
Error message
refusing firewall default incoming {} without SSH_CONNECTION because process ancestry could not be inspected; set allow_lockout = true to acknowledge the lockout risk What it means
The lockout check needs to know whether the process runs under SSH to decide how strictly to validate; it inspects process ancestry (parent chain for sshd). When ancestry cannot be inspected at all (Some(None) case), mise cannot rule out a remote session and conservatively refuses the default-incoming change unless allow_lockout = true. This protects developers who apply a deny-by-default firewall from locking themselves out of a remote machine.
Solutions
- Set SSH_CONNECTION appropriately if you are actually remote and want the precise check.
- Acknowledge the risk explicitly by setting `allow_lockout = true` in the [firewall] section.
- Run from a normal local shell or standard SSH login where process ancestry is inspectable.
- Add an explicit incoming TCP allow rule covering your SSH peer and port before changing the default policy.
Example fix
// before (mise.toml) [firewall] default_incoming = "deny" // after [firewall] default_incoming = "deny" allow_lockout = true
Defensive patterns
Strategy: validation
Validate before calling
if std::env::var("SSH_CONNECTION").is_err() && !cfg.allow_lockout {
eprintln!("ancestry inspection unavailable: set allow_lockout = true or run from a normal SSH/local shell");
} Type guard
fn lockout_check_decidable(ssh_connection: Option<&str>, allow_lockout: bool) -> bool {
ssh_connection.is_some() || allow_lockout
} Try / catch
match result {
Err(e) if e.to_string().contains("ancestry could not be inspected") => {
eprintln!("run outside the sandbox or set allow_lockout = true");
}
other => other?,
} Prevention
- Run firewall applies from a standard login shell with accessible /proc.
- Avoid sandboxed/containerized environments for privileged firewall changes.
- Set allow_lockout = true deliberately only when you hold console/serial access.
When it happens
Trigger: Calling validate_safety_with_rules (via `mise bootstrap firewall apply`) on a system where the parent-process walk fails or is unavailable (restricted /proc, container without procfs access, sandboxed environment), with SSH_CONNECTION unset and allow_lockout false.
Common situations: Running inside Docker/LXC where /proc ancestry is masked; running under hardened security profiles that block reading other processes' status; environments without /proc (some BSD/macOS variants); automation frameworks that detach from the terminal.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- refusing firewall default incoming
- refusing firewall default incoming
- refusing firewall default incoming
- refusing firewall default incoming
- SSH_CONNECTION must contain client address/port and server…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/f1e6fc9c8704006e.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/firewall.rs:486
&self,
rules: &[FirewallRule],
backend: Option<FirewallBackend>,
ssh_ancestor: Option<bool>,
) -> Result<()> {
if self.state != FirewallState::Enabled
|| self.default_incoming == FirewallPolicy::Allow
|| self.allow_lockout
{
return Ok(());
}
let Some(connection) = &self.ssh_connection else {
return match ssh_ancestor {
Some(false) => Ok(()),
Some(true) => bail!(
"refusing firewall default incoming {} from an SSH-derived process without SSH_CONNECTION: mise cannot verify that remote access will survive; preserve SSH_CONNECTION or set allow_lockout = true",
self.default_incoming.ufw()
),
None => bail!(
"refusing firewall default incoming {} without SSH_CONNECTION because process ancestry could not be inspected; set allow_lockout = true to acknowledge the lockout risk",
self.default_incoming.ufw()
),
};
};
let mut covered = false;
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))View on GitHub (pinned to 533346cc37)