Topic 6.5
Firewalls: iptables, nftables, Security Groups & NACLs
In one line
A firewall decides which packets may pass, using rules about source, destination, port, and protocol. Stateful firewalls remember connections, so replies are allowed automatically. On Linux, the kernel's netfilter does the filtering, configured with nftables (modern) or iptables, often via ufw or firewalld. In the cloud, security groups are stateful per-instance firewalls and network ACLs are stateless per-subnet ones. Default-deny inbound, open only what's needed, and test both directions.
Think of it like this
A building's security guard with a visitor list. Stateful = the guard remembers you walked out to the parking lot, so you can come back in. Stateless = the guard checks the list every time, in each direction, even for people already inside.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Firewall
- A system that allows or blocks network traffic based on rules.
- Stateful
- Tracking connections so replies to allowed traffic are permitted automatically.
- Stateless
- Judging each packet on its own, so both directions need rules.
- netfilter / conntrack
- The Linux kernel's packet filtering framework and its connection-tracking table.
- nftables / iptables
- Tools to configure netfilter rules (nftables is the modern replacement).
- Security group
- A stateful, per-interface firewall in AWS (similar concepts exist on other clouds).
- Network ACL
- A stateless, per-subnet firewall in AWS with ordered allow and deny rules.
Step by step
01A default-deny host firewall
Tiffin's bastion host should accept SSH only from the office and nothing else inbound. Priya writes a small nftables ruleset: drop by default, allow loopback and replies to existing connections, then the specific openings.
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept # local processes
ct state established,related accept # replies to our own connections
ct state invalid drop
ip saddr 203.0.113.0/28 tcp dport 22 accept # SSH from the office only
icmp type { echo-request, destination-unreachable, time-exceeded } accept
icmpv6 type { echo-request, packet-too-big, nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert } accept
log prefix "nft-drop " limit rate 5/second # log a sample of drops
}
chain output {
type filter hook output priority 0; policy accept;
}
}02Security groups that reference each other
In the Mumbai VPC, the database's security group allows Postgres only from the app servers' security group, not from an IP range. New app servers get access automatically, and nothing else can reach the database even if it's in the same subnet.
03Drop vs reject when debugging
The action changes what the client sees. A drop gives a timeout (the client waits and retries), and a reject gives an immediate 'connection refused' or 'port unreachable'. External-facing firewalls usually drop. Internal ones sometimes reject so that failures are fast and obvious.
Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
The stateless NACL that blocked replies
To tighten the private subnet, Arjun adds a NACL allowing outbound only to port 443, and inbound only from port 443.
Myth vs fact
Myth
A firewall on the network edge is enough.
Fact
Once anything inside is compromised, flat networks let it reach everything. Per-host firewalls, security groups between tiers, and Kubernetes NetworkPolicies limit how far problems spread.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Docker publishes ports by inserting its own iptables rules, which can bypass ufw's rules on the same host. A container started with
-p 5432:5432may be reachable from the internet even though ufw says 5432 is closed. Bind published ports to an address (-p 127.0.0.1:5432:5432) or filter in theDOCKER-USERchain.
Remember this
- 1
Rules match on 5-tuple fields (source IP, destination IP, protocol, source port, destination port) and an action: accept, drop (silent, gives a timeout), or reject (sends a reset or ICMP, gives 'refused').
- 2
Stateful firewalls track connections (conntrack), so
ESTABLISHED,RELATEDreplies are allowed by one rule. Stateless filters need explicit rules for both directions, including ephemeral reply ports (1024–65535). - 3
Linux: netfilter in the kernel, configured via nftables (
nft), the older iptables, or front-ends like ufw (Ubuntu) and firewalld (RHEL). Docker and Kubernetes also add their own rules. - 4
Security groups (AWS): stateful, attached to network interfaces, allow rules only, and can reference other security groups ('allow 5432 from the app's SG') instead of IPs.
- 5
Network ACLs: stateless, per subnet, numbered allow and deny rules evaluated in order. Forgetting the outbound ephemeral port range is the classic mistake.
- 6
Principles: default deny inbound, allow the minimum, prefer referencing groups or identities over IP ranges, and log drops so you can debug.
Explain it without notes
What's the practical difference between stateful and stateless firewalls?
Why reference a security group instead of an IP range?
Practice
Write rules so a web server accepts 80 and 443 from anywhere, SSH from one IP, and nothing else inbound.
Find out whether a timeout is caused by a security group or by the application.
Trade-offs
- ↔
Tight rules reduce attack surface but break things when requirements change, so keep rules in code (Terraform) with reviews. Drop hides services from scanners but makes debugging slower than reject.
Done when you can
I can explain stateful vs stateless filtering.
I write default-deny host rules without locking myself out.
I use security group references and know the NACL ephemeral-port trap.