Firewalls, segmentation & default-deny
How firewalls filter traffic by rules, why default-deny and network segmentation shrink your blast radius, and why a firewall alone never stops app-layer attacks.
A firewall is a policy engine that decides which network packets are allowed to pass and which are dropped. It makes that decision by matching each packet against an ordered rule set. Segmentation is the architecture that firewall decisions enforce: carving one flat network into isolated zones so a break-in in one place does not become access to everything.
How a firewall matches traffic
The classic unit of matching is the 5-tuple — five fields read from each packet's headers. A rule says "if these fields look like this, then allow or drop."
- Source IP — where the packet claims to come from
- Destination IP — where it is headed
- Source port — the sending side's port (usually ephemeral, high-numbered)
- Destination port — the service being reached (
443for HTTPS,22for SSH,5432for Postgres) - Protocol —
tcp,udp,icmp, and so on
A stateless filter judges each packet in isolation. A stateful firewall tracks connections: once it sees a legitimate outbound TCP handshake, it remembers that flow and automatically permits the return packets, instead of you having to write a mirror rule for reply traffic. Nearly every modern firewall — iptables/nftables, cloud security groups, hardware appliances — is stateful. That connection tracking is what lets you write a single "allow outbound to port 443" rule and still receive the response.
Default-deny is the whole game
The single most important firewall decision is the default action — what happens to a packet that matches no rule. There are two philosophies, and only one is safe.
- Default-allow (blocklist): everything is permitted except what you explicitly ban. You are forever chasing new things to block, and anything you forgot is open. A new service that spins up on a fresh port is reachable by default.
- Default-deny (allow-list): everything is dropped except what you explicitly permit. You enumerate exactly the flows your system needs and nothing else exists. Forgetting to add a rule fails closed — the service is unreachable, which is annoying but safe.
# nftables: a default-deny input policy
table inet filter {
chain input {
type filter hook input priority 0; policy drop; # deny by default
ct state established,related accept # stateful: allow replies
iif lo accept # loopback
tcp dport 22 ip saddr 203.0.113.0/24 accept # SSH from admin range only
tcp dport 443 accept # public HTTPS
# everything else hits 'policy drop'
}
}Write the deny as the policy, not as a final drop rule you might forget. policy drop means a misconfiguration or a deleted rule fails closed. Test the change from a second session before you disconnect, so a mistake in the SSH rule doesn't lock you out of your own box.
Segmentation shrinks the blast radius
A flat network is one where every host can reach every other host. It is convenient and catastrophic: one compromised laptop, one exploited web server, and the attacker can now talk to the database, the backup server, and the domain controller directly. Segmentation splits the network into zones with firewalls between them, so a foothold in one zone does not grant reach into the others. This is blast-radius reduction — you cannot prevent every breach, so you design so that one breach stays small.
- DMZ — internet-facing services (reverse proxy, public API) live here, isolated from internal systems. If the web server is popped, the attacker is still outside the internal zone.
- Internal / application tier — app servers that the DMZ may talk to on specific ports only.
- Database tier — data stores reachable *only* from the application tier, never from the DMZ and never from the internet.
- Management / admin — bastion hosts, monitoring, and admin panels on a separate segment reachable only from trusted operator networks or a VPN.
Traffic that crosses the network's edge — client-to-server, in and out of the perimeter (north-south) — is where people focus. The lateral, server-to-server traffic *between internal systems* (east-west) — including one internal tier reaching another, like app tier to database — is where attackers actually move once inside, and it is usually wide open. Micro-segmentation takes the idea to its limit: policy per workload, so even two app servers in the same tier can only talk on the exact ports they need. Zero-trust goes further and stops trusting network location at all — every request must prove identity and authorization regardless of which segment it came from, because "it's on the internal network" is not evidence of anything once an attacker is on the internal network.
Where firewalls are misconfigured
- Databases, caches, and admin panels exposed to the internet — a Postgres on
5432, a Redis on6379, a.envor Kibana or Jenkins reachable from anywhere. These belong on internal segments, bound to private interfaces, never on a public0.0.0.0. - Overly broad allow rules —
allow tcp from 0.0.0.0/0where a/24would do, or opening a whole port range because narrowing it was tedious. Every extra permitted flow is attack surface you now own. - Unrestricted east-west traffic — segments exist on paper but the intra-zone policy is allow-all, so lateral movement is trivial once one host falls.
- No egress filtering — outbound is left wide open. Inbound rules stop the front door; egress rules matter because a compromised host reaches back out. Limiting outbound to known destinations and ports disrupts data exfiltration and command-and-control (C2) callbacks, and turns a quiet breach into one that trips an alarm when it tries to phone home.
A firewall is necessary but not sufficient. If port 443 is open to your web app — and it must be — then every application-layer attack (SQL injection, auth bypass, SSRF, deserialization) travels inside that permitted, encrypted flow and the firewall sees nothing wrong. Packet filtering cannot inspect application logic. Network controls limit reach; they do not fix vulnerable code. Only test and reconfigure firewalls on assets you own or are explicitly authorised to touch — a rule change can black-hole production or lock out operators.
Defense checklist
- Set the default policy to drop on inbound, and prefer default-deny on egress too — allow-list the outbound destinations your services genuinely need.
- Segment by trust and function: DMZ, app tier, data tier, management. Put databases, caches, and admin interfaces on internal segments only, bound to private IPs.
- Restrict east-west traffic — do not let same-zone hosts reach each other on ports they don't need. Micro-segment sensitive workloads.
- Keep allow rules narrow: specific source ranges, specific destination ports, no
0.0.0.0/0where a defined range works. - Add egress filtering to blunt exfiltration and C2, and to generate a signal when something tries to reach an unexpected destination.
- Treat network location as no proof of trust — require identity and authorization per request (zero-trust) for anything sensitive.
- Audit the rule set regularly: remove stale allows, verify what is actually exposed with an external port scan of assets you own, and confirm the default action is still deny.
- Remember the layer boundary — pair the firewall with application-layer defenses (input validation, authn/authz, a WAF) because traffic on
443passes straight through.