Firewalls and Filtering in Practice
A firewall enforces which traffic is allowed to flow, and the whole security value comes from a default-deny posture: permit what is needed, drop everything else. This topic moves from the concept to a real, readable nftables ruleset on a Meridian host — stateful rules, ingress and egress filtering — and to the difference between packet-filtering, stateful, and application-layer firewalls.
Egress filtering, the part most teams skip, gets special attention here, because restricting outbound traffic is what strangles command-and-control and slows exfiltration when a host is already compromised.
Default-Deny and Rule Order
A firewall is only as good as its default. You start by dropping everything, then allow the specific flows a host actually needs — because you can never enumerate everything bad, so a deny-list is always incomplete. Rules are evaluated in order, so a broad early allow can nullify every rule after it; getting the order right matters as much as getting the rules right.
Stateful Filtering
A stateful firewall tracks connections, so it can allow the return traffic of an established session without a separate rule. This is why you allow "new inbound to port 443" and let the replies flow automatically, and why ct state appears throughout a real ruleset. Without state, you would have to open wide port ranges for return traffic, which is exactly the broad exposure default-deny exists to avoid.
Ingress vs Egress
Most teams filter inbound traffic carefully and leave outbound wide open — which lets malware call home and exfiltrate freely once it lands. Restricting egress to known destinations is one of the highest-value, least-used controls in security: it breaks command-and-control, slows data theft, and turns a quiet compromise into one that trips against a wall it did not expect.
table inet filter {
chain input {
type filter hook input priority 0; policy drop; # default-deny inbound
ct state established,related accept # stateful: allow return traffic
iif lo accept
ip saddr 10.20.0.0/24 tcp dport 22 accept # SSH only from the admin subnet
tcp dport 443 accept # the app
}
chain output {
type filter hook output priority 0; policy drop; # default-deny OUTBOUND too
ct state established,related accept
ip daddr 10.20.5.10 tcp dport 5432 accept # only to the database
tcp dport { 53, 443 } ip daddr @update_servers accept # DNS + updates to known hosts
}
}
This ruleset drops all inbound and outbound by default, allows established return traffic statefully, permits SSH only from the admin subnet and HTTPS to the app, and — crucially — constrains egress so the host can reach only the database and a known set of update servers. A web shell dropped on this host cannot open a command-and-control channel to an arbitrary internet address, because the output chain simply drops it.
Layers of Firewall
Firewalls come in layers that see progressively more and cost progressively more. A packet filter (nftables, iptables) works at layer 3 and 4 with no connection memory; a stateful firewall tracks connections; and an application-layer or next-generation firewall understands HTTP and can enforce app-aware policy — the web application firewall of Chapter 6. They compose; a WAF is not a substitute for the lower layers, it sits on top of them.
Packet filter — fast, per-packet layer 3/4 rules, no connection memory.
Stateful — tracks connections and allows return traffic safely; the modern baseline.
Application-layer (WAF / NGFW) — inspects layer 7 (HTTP, TLS SNI), can block SQLi/XSS patterns and enforce app policy; richer and heavier, and not a replacement for the lower layers.
- A permissive default — allow, with a few denies — instead of default-deny, when you can never enumerate everything bad to block.
- Filtering ingress only and leaving egress unrestricted, giving malware a free channel for command-and-control and exfiltration.
- Rule-order mistakes: a broad allow above specific denies, or a cleanup rule that never matches because an earlier rule already accepted the traffic.
- Relying on a host firewall alone or a network firewall alone, instead of layering both, so one bypass is total.
- Opening wide port ranges for return traffic instead of using stateful established/related rules.
- Default-deny in both directions and allow only the specific, documented flows each host needs.
- Use stateful rules and explicitly allow established and related return traffic rather than opening wide port ranges.
- Restrict egress to known destinations to break command-and-control and slow exfiltration.
- Layer host and network firewalls, keep rules under version control, and review them as the environment changes.
- Put internet-facing web apps behind a WAF as an added layer, not as a replacement for L3/L4 filtering.
Knowledge Check
Why is default-deny the foundation of a firewall's security value?
- You cannot enumerate every bad flow
- It is much faster for the firewall to process than an allow-list
- It lets the firewall skip stateful connection tracking
- It only applies to outbound traffic
Why is restricting egress one of the highest-value, least-used firewall controls?
- It breaks command-and-control and slows exfiltration
- It speeds up inbound connections to the web app
- It removes the need for any inbound rules
- It is strictly required for stateful connection tracking to function
What does a stateful firewall let you avoid that a stateless one forces on you?
- Opening wide port ranges for return traffic, since it tracks connections
- Writing any explicit allow rules for outbound traffic at all
- Using a default-deny policy
- Inspecting HTTP payloads for attacks
You got correct