Packet Filtering in Practice
Topic 50

Packet Filtering in Practice

Filtering

The firewall theory of the previous topic becomes, in practice, two very different tool families. On Linux, packet filtering is netfilter — the in-kernel framework — driven from userspace by iptables or its successor nftables. In the cloud it is security groups and network ACLs, configured through an API rather than a command. The concepts carry over cleanly, but the models do not: Linux gives you ordered rule chains evaluated top to bottom, while a cloud security group is a stateful set of allow-only rules with no order and no deny.

The gap between those models is where engineers get burned. Someone who learned firewalls on iptables reaches for a deny rule in a security group and discovers it doesn't exist; someone who learned them on security groups assumes rule order doesn't matter and writes an iptables chain that allows everything before it ever reaches the deny. This topic grounds both, so you know which mental model the tool in front of you actually uses.

The netfilter path a packet traverses
PREROUTING
INPUT / FORWARD
OUTPUT
POSTROUTING

netfilter Hooks and Chains

netfilter exposes five hook points in the kernel's packet path, and the chains you write attach to them. The three that matter day to day are INPUT (packets destined for this host), OUTPUT (packets this host originates), and FORWARD (packets routed through this host to somewhere else). The distinction is not cosmetic: a packet arriving at a router or gateway hits FORWARD, not INPUT, so a firewall meant to protect machines behind the box must filter in the FORWARD chain or it filters nothing.

Each packet traverses exactly the chains for its path — locally-delivered traffic sees INPUT, locally-generated sees OUTPUT, transit traffic sees FORWARD — and within a chain the rules run in order until one matches. That ordering, plus the hook a packet lands on, fully determines its fate.

iptables versus nftables

iptables is the legacy interface: separate binaries per protocol family (iptables, ip6tables), fixed tables, and a rule syntax that grows unwieldy fast. nftables is the unified successor that replaced it as the default on modern distributions — one tool for IPv4 and IPv6, one consistent syntax, sets and maps for compact rules, and atomic ruleset reloads so you never have a half-applied policy. Both drive the same netfilter core; nftables is the better surface for new work.

# nftables: drop everything inbound except SSH from one CIDR, stateful return
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0\; policy drop\; }
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input ip saddr 10.0.0.0/8 tcp dport 22 accept
# the policy drop above is the default-deny; only matched packets survive

Rule Order and Match-First

Within a chain, netfilter takes the first matching rule and stops. That makes rule order a correctness concern, not a style preference. A broad accept placed above a specific drop means the drop never runs — the packet matched the accept first and left the chain. The classic bug is a top-of-chain "allow all from the office /16" sitting above a "deny this one abused host inside the office," which silently lets the abuser through.

Cloud security groups invert this: their rules are unordered because they are allow-only, so there is no "earlier rule wins" to reason about — a packet is allowed if any rule permits it. The moment you need "allow this subnet but not that one host inside it," a security group can't express it and you must drop down to a stateless NACL, which is ordered and does support deny.

Cloud Security Groups versus NACLs

In the major clouds the two filtering layers are deliberately different. A security group is stateful and attaches to an instance or interface; it holds allow-only rules and automatically permits return traffic, which is why it has no deny rule — you express policy by what you allow. A network ACL is stateless and attaches to a subnet; it has numbered, ordered rules, supports both allow and deny, and because it is stateless you must permit the ephemeral return ports explicitly.

The two compose: a NACL is the coarse, ordered, subnet-wide guard that can block a known-bad CIDR, and the security group is the fine, stateful, per-instance allow-list. Reaching for a deny in a security group, or forgetting the return-port range in a NACL, are the two errors that follow directly from confusing their models.

iptables / nftables vs Cloud Security Groups

iptables / nftables are ordered chains evaluated first-match-wins, supporting allow, drop, and reject in any sequence you arrange. Rule order is part of the policy's correctness, and a misplaced rule changes the outcome. Use them on Linux hosts and gateways where you need the full vocabulary, including deny rules and per-chain control over INPUT, OUTPUT, and FORWARD.

Cloud security groups are unordered, stateful, allow-only object rules — a packet passes if any rule permits it, and there is no deny rule at all. That surprises engineers expecting an explicit block; for that you drop to a stateless NACL. Use security groups as the per-instance allow-list and let their statefulness handle return traffic automatically.

Common Mistakes
  • Placing a broad accept above a specific drop in an iptables chain. First-match-wins means the drop never executes, so the host you meant to block sails straight through the earlier allow.
  • Flushing chains with iptables -F over SSH when the default policy is drop. The flush removes the rule allowing your own session, the connection freezes mid-command, and you've locked yourself out of a remote box.
  • Trying to add a deny rule to a cloud security group. Security groups are allow-only and have no deny; the explicit block you want belongs on a stateless NACL, and assuming otherwise leaves the traffic permitted.
  • Forgetting egress rules exist. A default-open outbound security group lets a compromised instance reach any destination; restrict egress to the endpoints the workload needs instead of leaving it wide.
  • Filtering router traffic in the INPUT chain. Packets passing through the box hit FORWARD, not INPUT, so an INPUT-only rule protects the router itself but does nothing for the machines behind it.
Best Practices
  • Prefer nftables over iptables for new rules to get atomic ruleset reloads and one syntax for IPv4 and IPv6, avoiding the half-applied state a multi-command iptables edit can leave.
  • Order chains specific-deny before broad-allow, since first-match-wins means a rule's position is its meaning; put the targeted blocks above the wide permits.
  • Apply firewall changes with a timed rollback — iptables-apply or a scheduled flush — so a rule that severs your SSH session reverts automatically instead of locking you out.
  • Use NACLs for coarse subnet-wide deny of known-bad CIDRs and security groups for the fine per-instance allow-list, matching each control to the model it actually supports.
  • Restrict egress explicitly in both security groups and host chains, so a breached workload can reach only its required destinations rather than the whole internet.
Comparable conceptseBPF / Cilium (programmable filtering)Windows Firewall (the other host firewall)

Knowledge Check

A Linux box routes traffic for machines behind it. Which chain filters that transit traffic?

  • INPUT, because every single packet that reaches the box must pass through it first
  • FORWARD, because the packets are routed through to another host
  • OUTPUT, because the box sends the packets onward
  • PASSTHROUGH, the dedicated transit chain

You want to allow a subnet but block one abusive host inside it. Where does this belong?

  • A security group, adding a deny rule for the one host
  • A security group, listing both the whole subnet and the single host together
  • A NACL, with an ordered deny for the host above the subnet allow
  • A route table entry discarding the host's traffic

Why does rule order matter in an iptables chain but not in a cloud security group?

  • iptables stops at the first match, while a security group passes a packet if any allow rule matches
  • iptables runs in the kernel and security groups run in userspace
  • iptables is stateless and security groups are stateful
  • security groups apply a hidden internal priority number that quietly reorders all of your rules for you

You got correct