A default-deny iptables firewall needs an early rule for traffic that belongs to connections the host already started or explicitly allowed. Without that stateful allow rule, replies can be dropped by a later DROP policy even when outbound traffic and allowed service ports look correct.
The conntrack match reads kernel connection-tracking state with --ctstate. ESTABLISHED matches packets for connections that have seen traffic in both directions, while RELATED covers dependent traffic such as selected ICMP errors or helper-managed data connections.
Place the rule before broad DROP or REJECT rules in the INPUT chain. Check for an existing rule before inserting a duplicate, repeat the equivalent rule with ip6tables when the host accepts IPv6, and save the ruleset only after a separate connection test confirms management access still works.
Related: How to set a default iptables chain policy
Related: How to save iptables rules permanently
Tool: iptables Rule Generator
Steps to allow established iptables connections:
- List the current INPUT policy and rule order.
$ sudo iptables -S INPUT -P INPUT ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
Rule order matters. A broad DROP or REJECT rule above the state match can still block reply traffic before this rule is reached. Related: How to list iptables rules with counters
- Check whether the stateful allow rule already exists.
$ sudo iptables -C INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables: Bad rule (does a matching rule exist in that chain?).
No output with exit status 0 means the rule already exists. The error above means it needs to be inserted.
- Insert the conntrack rule at the top of the INPUT chain.
$ sudo iptables -I INPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables may display the state list as RELATED,ESTABLISHED after insertion. The order inside --ctstate does not change the match.
- Confirm the rule appears before service allow rules and drop rules.
$ sudo iptables -S INPUT -P INPUT ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
- Apply the intended default-deny INPUT policy after required allow rules are visible.
$ sudo iptables -P INPUT DROP
Run this only from a console or an already-open remote session after loopback and management rules are present. Use sudo iptables -P INPUT ACCEPT to reopen the policy while correcting a failed test.
- Reset INPUT counters before a controlled connection test.
$ sudo iptables -Z INPUT
Counter reset is optional, but it makes the next verification output easier to read on a busy host.
- Open an outbound connection from the firewalled host.
$ curl -sS http://peer.example.net:8080/ iptables peer service
Use a service you are allowed to reach from the host. The inbound reply packets should match the RELATED,ESTABLISHED rule.
- Check that the state rule counter increased.
$ sudo iptables -L INPUT -n -v --line-numbers Chain INPUT (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 8 719 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 2 2 148 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
The packet count on the RELATED,ESTABLISHED line confirms reply traffic is using the state match.
- Test a new inbound connection that should remain blocked by the firewall.
$ nc -vz server.example.com 9090 nc: connect to server.example.com port 9090 (tcp) failed: Connection timed out
A blocked NEW connection confirms the state rule is not opening every inbound port. If the port should be reachable, add a separate service-specific allow rule before the drop policy.
- Review the final ruleset before saving it permanently.
$ sudo iptables-save *filter :INPUT DROP [0:0] ##### snipped ##### -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT COMMIT
Use the persistence method for the host after reviewing the output. Related: How to save iptables rules permanently
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.