Command Palette

Search for a command to run...

Hectal
PHASE 6Advanced ~16 min· topic 5 of 8

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.

0/8 · 0%

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.

/etc/nftables.confwhole filetext
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;
  }
}
terminal
$ sudo nft -c -f /etc/nftables.conf && sudo nft -f /etc/nftables.conf
sudo nft list chain inet filter input | head -4
── expected output ──
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
`-c` checks the file first. Keep a second SSH session open while changing firewall rules, so a mistake doesn't lock you out.

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.

terminal
$ aws ec2 authorize-security-group-ingress --group-id sg-0db1 --protocol tcp --port 5432 --source-group sg-0app
aws ec2 describe-security-groups --group-ids sg-0db1 --query 'SecurityGroups[0].IpPermissions[0].UserIdGroupPairs[0].GroupId'
── expected output ──
{
"Return": true
}
"sg-0app"
Security groups that reference each otherdiagram
Rendering diagram…

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.

terminal
$ nc -zv -w3 10.0.1.20 6379 # port dropped by firewall
nc -zv -w3 10.0.1.20 6380 # port rejected by firewall
── expected output ──
nc: connect to 10.0.1.20 port 6379 (tcp) timed out: Operation now in progress
nc: connect to 10.0.1.20 port 6380 (tcp) failed: Connection refused

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.

terminal
$ curl -sv -m 10 https://api.razorpay.com 2>&1 | grep -E 'Connected|timed out'
── what you'll see ──
* Connection timed out after 10001 milliseconds

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:5432 may 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 the DOCKER-USER chain.

Remember this

  1. 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. 2

    Stateful firewalls track connections (conntrack), so ESTABLISHED,RELATED replies are allowed by one rule. Stateless filters need explicit rules for both directions, including ephemeral reply ports (1024–65535).

  3. 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. 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. 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. 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

01

What's the practical difference between stateful and stateless firewalls?

02

Why reference a security group instead of an IP range?

Practice

01

Write rules so a web server accepts 80 and 443 from anywhere, SSH from one IP, and nothing else inbound.

02

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.