Detecting port scans from nftables and iptables log files
Every device with public IP does get a constant stream of port scans every day. If one just wants to inspect these port scans with a simple and fast program that follows Unix philosophy, you are out of options. Or that was the case when I first wanted to do something like this, so I had to write this program myself, but more on that later. The program introduced in this article should work with nftables, iptables and firewalld or any firewall which uses “ipt_LOG” kernel module for logging.
Building and installing LPSD
Before doing anything you should enable logging in to your firewall
if you have not done so yet. If you are using nftables refer to their wiki
nft add rule filter input log) if you don’t know how to do
that (it’s very trivial). If you are using iptables it can be enabled
iptables -A INPUT -j LOG” or something like that.
Ideally, you may want to only log the blocked traffic to keep the log
file sizes down. After enabling logging, entries should start appearing
in the syslog or kern.log file.
The source code is available in Gitlab and Github, just use the one you prefer.
After cloning the repository the program can be installed with “install” target or just built with “all” target (if you don’t want to make an installation).
# make install
gcc -Wall -std=gnu17 -O2 -pthread lpsd.c -o lpsd
install lpsd /usr/bin/
gzip man/lpsd.1 -c > man/lpsd.1.gz
install -m 644 man/lpsd.1.gz /usr/share/man/man1/lpsd.1.gz
The install target also installs a man page “lpsd(1)”, so refer to that if the information provided in the help isn’t enough.
First a short introduction to LPSD’s features:
- It can read log from a file or multiple files.
- The log can also be read from the standard user input.
- Results can be exported to file or CSV file.
- Dates can be filtered based on month or day.
- Threads can be utilised when finding port scans.
- Log entries can be reordered if they are not in order already.
So nothing too fancy but that’s the point. Do one thing and do it well (not sure about that last point:)).
The usage is fairly simple but there are some examples to give some idea (real IPs on the output so I censored them):
$ lpsd -i kern.log --date 03-22
2022-03-22 04:05:58 141.101.196.xxx 23 ports
2022-03-22 06:55:53 221.165.156.xxx 8 ports
2022-03-22 10:53:44 91.243.44.xxx 31 ports
2022-03-22 11:57:13 91.243.44.xxx 27 ports
2022-03-22 13:59:06 45.33.64.xxx 26 ports
2022-03-22 17:05:53 89.248.165.xxx 5 ports
Works also with IPv6:
$ lpsd -i kern.log
2022-04-08 17:20:59 2002:c000:0203:0000:0000:0000:0000:0002 1000 ports
2022-04-08 17:23:40 2002:c000:0203:0000:0000:0000:0000:0003 1000 ports
2022-04-08 17:24:16 2002:c000:0203:0000:0000:0000:0000:0004 1000 ports
2022-04-08 17:24:36 2002:c000:0203:0000:0000:0000:0000:0005 1000 ports
2022-04-08 17:25:00 2002:c000:0203:0000:0000:0000:0000:0006 1000 ports
Reporting port scanners to abuseipdb
You may want to report these IP addresses to abuseipdb or some equivalent. I have written a script for this purpose, so you may want to check it out. It parses the CSV output of LPSD and prepares it for a bulk report.
Gitlab and Github.
Why C? Why not Python, Rust etc.?
Short answer: because C is fast and I’m familiar with it.
I want this to be able to run with low power devices with the smallest overhead possible, so Python is ruled out. Rust could be another option, but C is generally more performant and has lower memory usage (I don’t say this as an absolute), and C and its ecosystem are more mature. Overall Rust is an overhyped language.