Build Safe iptables Rules with Match and Log Extensions

iptables extensions turn a bare accept-or-drop rule into one that understands connection state, rate and intent. This builds a small, reviewable ruleset with them, using the installed iptables 1.8.10 command with its nf_tables backend, and checks what is actually active. Allow about 15 minutes on a test host, or longer over SSH where you need a console fallback.

Before you start

You need the iptables package and a shell with sudo access. Rule changes affect the host immediately, so keep an existing SSH session open, have console access if possible, and substitute your real interface and service values before applying anything. Commands that just print help are ordinary; listing and changing the live ruleset normally need root.

This guide uses the extensions most often needed in a small host firewall:

1. Identify the backend and available options

Record the program version, then ask each extension for its own options: the module name follows -m, and a target name follows -j.

iptables --version
iptables -m conntrack --help
iptables -m limit --help
iptables -m comment --help
iptables -j LOG --help
iptables -j REJECT --help

On this installation the first command reports iptables v1.8.10 (nf_tables). The help output is the useful compatibility check: it shows, for example, --ctstate, --limit, --limit-burst and --comment. Extension help can vary with the package and kernel, so check it here rather than copying an option from a different host.

2. Inspect the current rules before changing anything

Save a readable view of the current filter table. This does not change any rules. The numeric form avoids DNS lookups and makes the output easier to compare later:

sudo iptables -L INPUT -n -v --line-numbers
sudo iptables -S INPUT

You should see the chain policy followed by numbered rules. Record the output somewhere safe before adding one. Do not assume an empty-looking custom chain means the host has no protection: the built-in chain policy and rules in other tables matter too.

3. Allow established traffic before narrow rules

A common first rule for a host firewall accepts packets belonging to connections already seen in both directions, plus related traffic such as an associated error or data connection:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

The state names come from the conntrack extension. NEW is for a connection starting now; INVALID means connection tracking cannot associate the packet with a known connection. Rule order matters: iptables evaluates matches in the order written, and a terminating target such as ACCEPT stops traversal there.

Check the rule is present before continuing:

sudo iptables -C INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -S INPUT

A successful -C check produces no output and exits with status zero. It is safe to repeat this check, but repeating -A would create a duplicate rule.

4. Allow one intended service

Replace the example port with a service you deliberately expose. This permits new TCP connections to HTTPS on every interface, then records the reason in the rule itself:

sudo iptables -A INPUT -p tcp --dport 443 \
  -m conntrack --ctstate NEW \
  -m comment --comment "allow public HTTPS" \
  -j ACCEPT

The comment extension accepts a comment up to 256 characters according to the installed manpage. Keep it short enough to explain the owner or purpose, not a second policy document. Verify the exact rule:

sudo iptables -C INPUT -p tcp --dport 443 \
  -m conntrack --ctstate NEW \
  -m comment --comment "allow public HTTPS" \
  -j ACCEPT

5. Rate-limit noisy logging

Logging every dropped packet can fill your logs and burn CPU. The limit match uses an average rate and a burst; on this installation the defaults shown by help are 3 per hour and a burst of 5, but specify values explicitly so the rule explains itself:

sudo iptables -A INPUT -m conntrack --ctstate INVALID \
  -m limit --limit 5/min --limit-burst 10 \
  -j LOG --log-prefix "iptables invalid: " --log-level 4

LOG is non-terminating, so a packet still continues to the following rules. The prefix helps you find these records in the system log. The numeric level is accepted by the target help; the precise destination depends on the host's logging configuration. Inspect counters first, then generate only controlled test traffic if you know how the host logs kernel messages:

sudo iptables -L INPUT -n -v --line-numbers

Do not put a broad logging rule ahead of an intended accept rule unless you want the accepted traffic logged too. If the log rate is still too high, tighten it or remove the rule with the exact delete command below.

6. Choose DROP or REJECT deliberately

DROP silently discards a packet. REJECT sends an error response and is valid in the built-in INPUT, FORWARD and OUTPUT chains, among other restrictions documented for the target. For a final default policy, a silent drop is often less revealing:

sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

For a specific service or network where a quick refusal is preferable, use a narrow reject rule and choose its response type from iptables -j REJECT --help.

Warning: do not indiscriminately reject INVALID packets. The installed manpage warns that a delayed packet can be classified as invalid after a healthy retransmission has already moved the connection along, and an error response could damage that connection.

Recovery and undo

Before deleting anything, list line numbers and copy the exact rule. Matching deletion is less fragile than guessing a number, but it has to match the rule as installed:

sudo iptables -D INPUT -p tcp --dport 443 \
  -m conntrack --ctstate NEW \
  -m comment --comment "allow public HTTPS" \
  -j ACCEPT

sudo iptables -D INPUT -m conntrack --ctstate INVALID \
  -m limit --limit 5/min --limit-burst 10 \
  -j LOG --log-prefix "iptables invalid: " --log-level 4

To remove the established-traffic rule, use the same specification with -D. If a change has locked you out, use the console or an existing privileged session to delete the new rule.

Destructive action: avoid iptables -F as a recovery shortcut. Flushing a chain removes all of its rules at once, and can expose services or remove access controls you did not put there yourself.

Done means