Same Job, Different Shell Part 9: Firewall Status, Read-Only

This is a read-only post on purpose. Not “how to configure a firewall”, just: a port test from Part 8 came back filtered, and the fastest next step on either platform is looking at what the local firewall currently allows before you go anywhere near the network path.

Linux: three tools, because the underlying tech genuinely changed

Linux firewalling has been through more churn than anything else in this series. iptables was the standard for two decades. nftables replaced it at the kernel level years ago, with iptables itself now usually a compatibility shim translating to nftables rules underneath. ufw (Uncomplicated Firewall) sits on top of either, as a simpler frontend aimed at not having to write raw rule syntax at all. All three can be present on the same box and it’s worth knowing how to read each.

$ iptables -L -n -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

That’s a genuinely real, empty ruleset from this sandbox: no rules in any chain, and every chain’s default policy is ACCEPT, meaning nothing is being blocked at the iptables layer at all. -L list, -n numeric (don’t resolve hostnames, same reasoning as route -n back in Part 4), -v verbose (adds packet/byte counters per rule, useful for confirming whether a rule is actually matching traffic or just sitting there unused).

$ nft list ruleset

came back completely empty on this same box, no tables, no chains at all, not even the default ones iptables -L showed. That’s not a contradiction: this particular install’s iptables binary is the legacy backend, not the nft-based compatibility layer, so the two tools are genuinely reading two different rule stores here rather than two views of the same one. On a box running the nftables backend for iptables-nft, you’d expect nft list ruleset to show the same chains iptables -L does, translated into nft’s syntax. Don’t assume; check both if you’re not sure which backend a given box is running.

$ ufw status verbose
Status: inactive

ufw reporting inactive on the same box that iptables -L shows an empty-but-unrestrictive ruleset for is consistent, not contradictory: ufw is a management layer over iptables/nftables, and “inactive” means ufw itself has never been enabled here, so it hasn’t written anything into the underlying tables. If ufw status says active, the useful next step is sudo ufw status numbered, which lists every rule with an index number, handy for referencing a specific rule to delete later even though this is a read-only post.

Windows: Get-NetFirewallRule and netsh advfirewall

PS> Get-NetFirewallRule -Enabled $true -Direction Inbound |
    Format-Table DisplayName, Direction, Action, Enabled -AutoSize

That’s the version that shows up in a lot of write-ups, and it fails on a real box:

Get-NetFirewallRule: Cannot process argument transformation on parameter 'Enabled'. Cannot convert value "True" to
type "Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.Enabled[]". Error: "Invalid cast from
'System.Boolean' to 'Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.Enabled[]'."

Get-NetFirewallRule is a CIM cmdlet, and -Enabled isn’t a plain boolean parameter the way it looks, it’s a generated enum type (NetSecurity.Enabled, values True/False) underneath. PowerShell’s usual “a boolean will just convert to whatever’s expected” behavior doesn’t apply here, so passing the literal $true throws a cast error. The fix is to pass the string True, which PowerShell converts to the enum correctly:

PS> Get-NetFirewallRule -Enabled True -Direction Inbound |
    Format-Table DisplayName, Direction, Action, Enabled -AutoSize

Worth knowing before you’re mid-incident and it silently breaks a script. -PolicyStore ActiveStore scopes it to rules actually in effect right now rather than every rule defined anywhere, including ones a GPO might be overriding:

PS> Get-NetFirewallRule -PolicyStore ActiveStore -Enabled True -Direction Inbound |
    Format-Table DisplayName, Direction, Action, Enabled -AutoSize

Windows Firewall ships with hundreds of predefined rules (one per built-in service, feature, and app), so filtering by enabled/inbound right away is the difference between a readable table and a wall of scrolling text.

The classic netsh command is still there, and still the fastest way to answer the single most common question, is the firewall even on. Verified for real, output matched exactly:

PS> netsh advfirewall show allprofiles state

Domain Profile Settings:
----------------------------------------------------------------------
State                                 ON
Private Profile Settings:
----------------------------------------------------------------------
State                                 ON
Public Profile Settings:
----------------------------------------------------------------------
State                                 ON
Ok.

Domain/Private/Public profile state (ON/OFF) in one shot, the direct equivalent of ufw status’s active/inactive line, just per network profile instead of one global switch. For the full current-profile ruleset in the old text format:

C:\> netsh advfirewall show currentprofile

Get-NetFirewallRule is the one to reach for when you need to filter, script, or export; netsh advfirewall show is the one to reach for when you just need a yes/no answer fast, matching the ufw status vs iptables -L -v split above almost exactly: a quick top-level check, and a detailed rule-by-rule one.

Quick reference

What you wantLinuxWindows
Is the firewall even onufw statusnetsh advfirewall show allprofiles state
Full detailed rulesetiptables -L -n -vGet-NetFirewallRule -PolicyStore ActiveStore
Modern/native rule storenft list ruleset(single store; no iptables/nftables-style split)
Numbered rules (for later reference)ufw status numberedGet-NetFirewallRule | Select DisplayName, Name
Only enabled, inbound rulesiptables -L INPUT -n -vGet-NetFirewallRule -Enabled True -Direction Inbound

What’s next

Part 10 covers packet capture: tcpdump against pktmon, including a real captured TCP handshake and a real captured DNS query/response pair, since at some point in any real investigation, reading rule tables and connection state stops being enough and you need to see the actual packets.

Cross-references: for the full iptables-to-nftables migration path, including why the compatibility shim exists and where it breaks down, see iptables to nftables: Migrating Production Firewalls Without Downtime; for nftables’ own rule model, table/chain/set structure, and atomic updates, see nftables: The Modern Netfilter Framework Every Network Engineer Should Know.