> For the complete documentation index, see [llms.txt](https://docs.glesys.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.glesys.com/products/compute/guides-for-server-management/logging-with-iptables-and-nftables.md).

# Logging with iptables and nftables

***

Sometimes it’s helpful to log all inbound and outbound connections on a server—for example, when you need evidence to create firewall rules or to troubleshoot a misconfigured firewall.

The default firewall tool in Debian 9 and previous versions is `iptables`. This tool has been replaced by `nftables` since Debian 10.

{% tabs %}
{% tab title="iptables (Debian 9 and older)" %}
If you run the following commands (as root), all firewall rules will be cleared, and all connections will be logged to `/var/log/messages`:

{% code title="Multiple commands" %}

```
iptables -F
iptables -I INPUT -m state --state NEW -j LOG --log-level 4 --log-prefix "INCOMING: "
iptables -I OUTPUT -m state --state NEW -j LOG --log-level 4 --log-prefix "OUTGOING: "
```

{% endcode %}

Because each connection is logged on a separate line, this quickly becomes hard to read. On a server with heavy traffic, the log file can grow to contain a very large number of lines.

This short and simple one‑liner below summarizes all connections recorded in `/var/log/messages`. Each host/port combination appears on its own line, and the number of connections is counted. Keep in mind that `/var/log/messages` may be rotated.

{% code title="Command" %}

```
cat /var/log/messages | grep "IN=" | awk -F'[= ]+' '{for (i=1;i < NF;i++) { if ($i=="DST") dst=$(i+1);if ($i=="SRC") src=$(i+1); if ($i=="PROTO") proto=$(i+1); if ($i=="DPT") dpt=$(i+1); if($i=="INCOMING:"||$i=="OUTGOING:") dir=$i} printf "%s %-20s %-20s %-15s %s \n",dir," DST:"dst," SRC:"src," PROTO:"proto," DPT:"dpt}' |sort | uniq -c
```

{% endcode %}

The same method can be used to log all blocked connections, which can be very useful for troubleshooting. Below is a complete example of a firewall script that **only allows incoming SSH connections from `10.10.10.10` and blocks and logs all other connections**:

{% code title="firewall-script.sh" %}

```bash
#!/bin/bash
I=/sbin/iptables
$I -F
$I -A OUTPUT -s 0/0 -m state --state RELATED,ESTABLISHED -j ACCEPT

#SSH in
$I -A INPUT -p TCP --dport 22 -s 10.10.10.10 -j ACCEPT

$I -A INPUT -m state --state NEW -j LOG --log-level 4 --log-prefix "INCOMING: "
$I -A OUTPUT -m state --state NEW -j LOG --log-level 4 --log-prefix "OUTGOING: "
$I -A INPUT -j DROP
$I -A OUTPUT -j DROP

exit 0
```

{% endcode %}
{% endtab %}

{% tab title="nftables (Debian 10 and newer)" %}
Create a file named `log-everything.conf` in your home directory, and give it the following content:

{% code title="log-everything.conf" %}

```
#!/usr/sbin/nft -f

flush ruleset

table ip filter {
    chain INPUT {
        type filter hook input priority 0; policy accept;
        ct state new log prefix "INCOMING: " level info
    }

    chain OUTPUT {
        type filter hook output priority 0; policy accept;
        ct state new log prefix "OUTGOING: " level info
    }
}
```

{% endcode %}

Run the following command to clear all firewall rules and log all connections:

{% code title="Command" %}

```
sudo nft -f log-everyting.conf
```

{% endcode %}

You can now follow all connections using:

```
sudo journalctl -k --follow
```

To stop following the log, press <kbd>Ctrl</kbd>+<kbd>C</kbd>.

Because each connection is logged on a separate line, this quickly becomes hard to read. On a server with heavy traffic, the logs can grow to contain a very large number of lines.

This short and simple one‑liner below summarizes all connections recorded in the journal. Each host/port combination appears on its own line, and the number of connections is counted:

{% code title="Command" %}

```
sudo journalctl -k | grep "IN=" | awk -F'[= ]+' '{for (i=1;i < NF;i++) { if ($i=="DST") dst=$(i+1);if ($i=="SRC") src=$(i+1); if ($i=="PROTO") proto=$(i+1); if ($i=="DPT") dpt=$(i+1); if($i=="INCOMING:"||$i=="OUTGOING:") dir=$i} printf "%s %-20s %-20s %-15s %s \n",dir," DST:"dst," SRC:"src," PROTO:"proto," DPT:"dpt}' |sort | uniq -c
```

{% endcode %}

The same method can be used to log all blocked connections, which can be very useful for troubleshooting. Below is a complete example of a firewall configuration that only allows incoming SSH connections from `10.10.10.10` and blocks and logs all other connections. Save the below content to a file called `drop-and-log.conf`.

{% code title="drop-and-log.conf" %}

```
#!/usr/sbin/nft -f

flush ruleset

table ip filter {
    chain INPUT {
        type filter hook input priority 0; policy drop;
        ct state related,established accept
        ip saddr 10.10.10.10 tcp dport 22 accept
        ct state new log prefix "INCOMING: " level info
        drop
    }

    chain OUTPUT {
        type filter hook output priority 0; policy drop;
        ct state related,established accept
        ct state new log prefix "OUTGOING: " level info
        drop
    }
}
```

{% endcode %}

To load this firewall configuration (note that **everything will be blocked except for SSH** **from 10.10.10.10**), run the following:

{% code title="Command" %}

```
sudo nft -f drop-and-log.conf
```

{% endcode %}
{% endtab %}
{% endtabs %}
