{"record":{"id":"2f51095e9f8d8aa4","repo":"jdx/mise","slug":"refusing-firewall-default-incoming-over-ssh-no","errorCode":null,"errorMessage":"refusing firewall default incoming {} over SSH: no incoming TCP allow rule covers peer {} on server port {} with an unrestricted interface; add a covering rule or set allow_lockout = true","messagePattern":"refusing firewall default incoming (.+?) over SSH: no incoming TCP allow rule covers peer (.+?) on server port (.+?) with an unrestricted interface; add a covering rule or set allow_lockout = true","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/firewall.rs","lineNumber":545,"sourceCode":"                    _ => {}\n                }\n                continue;\n            }\n            // SSH_CONNECTION does not identify the ingress interface. An\n            // interface-constrained allow cannot prove that it preserves this\n            // session, so keep looking for an unrestricted covering allow.\n            if rule.interface.is_none() {\n                covered = true;\n                if matches!(\n                    backend,\n                    Some(FirewallBackend::Nftables | FirewallBackend::Ufw)\n                ) {\n                    break;\n                }\n            }\n        }\n        if !covered {\n            bail!(\n                \"refusing firewall default incoming {} over SSH: no incoming TCP allow rule covers peer {} on server port {} with an unrestricted interface; add a covering rule or set allow_lockout = true\",\n                self.default_incoming.ufw(),\n                connection.peer,\n                connection.server_port\n            );\n        }\n        Ok(())\n    }\n\n    pub(crate) fn plans(&self) -> Vec<ResourcePlan> {\n        let inspection = self.inspection.as_ref();\n        let backend = inspection\n            .and_then(|inspection| inspection.backend)\n            .unwrap_or(self.backend);\n        let desired = format!(\n            \"{} via {}; incoming {}; outgoing {}; {}\",\n            match self.state {\n                FirewallState::Enabled => \"enabled\",","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/firewall.rs#L527-L563","documentation":"When changing the default incoming policy over SSH, mise requires positive proof that at least one incoming TCP allow rule covers your current peer (connection.peer) and server port with no interface restriction. After walking all rules, none qualifies (covered == false), so applying would almost certainly block your session and the change is refused.","triggerScenarios":"`mise bootstrap firewall apply` over SSH where every rule either: is not incoming, is not allow/preserving, does not restrict to tcp/22 (server_port), does not contain connection.peer in its source network, or binds to a specific interface that is not the SSH ingress.","commonSituations":"SSH allow rule written for the wrong port (e.g. 22 vs a custom Port in sshd_config); allow rule limited to a LAN source while you SSH from a WAN/VPN address; rule bound to eth0 while SSH arrives over tails0/wg0; IPv6 SSH session but only IPv4 allow rules defined.","solutions":["Add an incoming allow rule matching your actual peer and SSH port, e.g. source = your client CIDR, port = sshd port, protocol = \"tcp\", action = \"allow\", no interface restriction.","Fix the existing allow rule's port, source CIDR, or interface so it covers the current connection (check SSH_CONNECTION values).","Set allow_lockout = true only if you have out-of-band console access."],"exampleFix":"// before (SSH from 203.0.113.7 on port 22)\n[[firewall.rules]]\nname = \"allow-ssh\"\nsource = \"10.0.0.0/8\"\nport = 22\nprotocol = \"tcp\"\naction = \"allow\"\n\n// after\n[[firewall.rules]]\nname = \"allow-ssh\"\nsource = \"203.0.113.7/32\"\nport = 22\nprotocol = \"tcp\"\naction = \"allow\"","handlingStrategy":"validation","validationCode":"let conn = std::env::var(\"SSH_CONNECTION\").expect(\"SSH_CONNECTION\");\nlet (peer, _sport, server_port) = parse_ssh_fields(&conn);\nlet covered = rules.iter().any(|r| r.is_incoming_allow_tcp(peer, server_port) && r.interface.is_none());\nif !covered { eprintln!(\"no allow rule covers {peer} on {server_port}; add one before applying\"); }","typeGuard":"fn ssh_is_covered(rules: &[FirewallRule], peer: IpAddr, server_port: u16) -> bool {\n    rules.iter().any(|r| r.direction == Direction::Incoming\n        && r.action.preserves_access()\n        && r.protocol == Some(Protocol::Tcp)\n        && r.port.map(|p| p.contains(server_port)).unwrap_or(false)\n        && r.source.as_ref().map(|s| s.contains(&peer)).unwrap_or(true)\n        && r.interface.is_none())\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"no incoming TCP allow rule covers\") => {\n        eprintln!(\"add an allow rule for the peer/port from SSH_CONNECTION\");\n    }\n    other => other?,\n}","preventionTips":["Compare your allow rule's port/source with `echo $SSH_CONNECTION` before applying.","If sshd runs on a custom port, mirror it in the firewall rule.","Avoid binding the SSH allow rule to a specific interface unless certain of the ingress device.","Add rules for both IPv4 and IPv6 if the host is dual-stack."],"tags":["firewall","ssh","lockout-protection","rule-coverage"],"backgroundTag":"invalid-state-transition","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}