Concepts / Firewalls

Firewalls

Stateful firewall

Also called conntrack, established, related, connection tracking

A firewall that remembers connections, so it can allow replies while refusing new connections in.

Each packet is new, established or related. The classic policy: accept established and related, allow new connections from inside, drop new ones from outside. It is about who started the conversation, not which ports are open.

How a stateful firewall works, step by step

A stateless firewall judges each packet alone, by address and port. A stateful one remembers connections in the kernel's conntrack table, and sorts every packet into a state: new (the first packet of a flow), established (a packet belonging to a flow already seen), related (a new flow a helper ties to an existing one, such as an FTP data channel or an ICMP error), or invalid. Follow the stateful firewall lab, where firewall routes between insideNetwork 10.0.1.0/24 and outsideNetwork 10.0.2.0/24, with insideClient at 10.0.1.10 and outsideServer at 10.0.2.10:

  1. The inside opens a connection out. insideClient sends a SYN to outsideServer. It is new and comes from the inside, so the policy allows it, and conntrack records the flow.
  2. The reply comes home. The server's SYN-ACK is established against that record, so it is accepted even though no rule names the inside host. The classic rule is "accept established and related".
  3. The outside is shut out. When outsideClient sends a SYN in to the inside server, it is new from outside. No rule permits a new inbound flow, so the firewall drops it; the inside server never answers.
  4. Direction, not host. Two machines on the same network never cross the firewall, so they talk freely. The rule is about who started the conversation, not which ports are open.

This is the whole logic of a home router's protection and of a VPN gateway: the inside reaches out, replies return, nothing new gets in.

What it looks like on a Linux machine

The lab's three rules compile to iptables on the forward path, a conntrack match carrying the state:

iptables -A NETLAB-FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A NETLAB-FORWARD -s 10.0.1.0/24 -j ACCEPT
iptables -A NETLAB-FORWARD -j DROP

On nftables the same reads ct state established,related accept. While the inside talks to the outside, the tracked flow shows in conntrack:

$ conntrack -L -p tcp
tcp      6 431999 ESTABLISHED src=10.0.1.10 dst=10.0.2.10 sport=49231 dport=80 src=10.0.2.10 dst=10.0.1.10 sport=80 dport=49231 [ASSURED] mark=0 use=1

[ASSURED] means traffic has gone both ways. Capturing port 80 on the firewall itself shows the asymmetry: the inside-started handshake crosses both ways, the outside-started SYN dies at the firewall.

IP 10.0.1.10.49231 > 10.0.2.10.80: Flags [S], seq 12..., length 0
IP 10.0.2.10.80 > 10.0.1.10.49231: Flags [S.], seq 98..., ack 12..., length 0
IP 10.0.2.11.51880 > 10.0.1.11.80: Flags [S], seq 44..., length 0

The first two lines are the inside-started handshake, SYN out and SYN-ACK back, both crossing the firewall. The third is the outside client's SYN reaching the firewall; no reply ever follows, because the new inbound flow is dropped.

The outside client's curl -sf --max-time 4 http://10.0.1.11/ just times out; the inside server (10.0.1.11) sends zero packets back. The same rules underlie the home router in the NAT and port forward lab and the layered policy of the access control lists lab.

What goes wrong, and how you see it

Asymmetric routing breaks state. If a flow's reply returns through a different firewall that never saw the request, conntrack marks it INVALID and drops it. conntrack -S shows the invalid counter rising, and connections hang. Fix the routing, or the two firewalls must share state.

You forgot the established rule. With only "allow new from inside", even the inside's own replies are judged new and dropped, so nothing works in either direction. Accept established,related first.

A helper is missing. Active FTP's data channel is a related flow a helper recognises. Without nf_conntrack_ftp, the data connection looks new and is dropped; the control channel works but transfers stall. See the FTP active and passive lab.

Idle flows expire. A UDP conntrack entry lives 30 seconds, an established TCP one much longer. A long-idle connection whose entry has timed out is treated as new and blocked; keepalives hold the entry open.

Questions people ask

Stateful vs stateless firewall? A stateless filter decides packet by packet on addresses and ports; a stateful one tracks the connection, so a single "accept established,related" rule lets every reply home without opening ports inbound. It is simpler and safer.

Is a stateful firewall the same as NAT? No. NAT also uses conntrack and blocks unsolicited inbound as a side effect, but a firewall's protection is the explicit policy you can read, and it works on IPv6 where NAT is not used.

What are related connections? New flows a helper ties to one already allowed: FTP data channels, SIP media, and ICMP errors about a tracked flow. They are accepted by the same established,related rule.

See it on real machines

Learn it on real machines.

Every lab is a small network of real Linux machines in your browser. Start with a free account: Google or email, no card.

Start this lab, free