Working with iptables Logging
Most commonly iptables is used to allow, block, or redirect connections. However, it also has a logging feature that can be very useful for network traffic analysis and system security.
In the example below I will get a list of network services listening on my server and create iptables inbound connection logging rules. And then I’ll use some basic shell scripting to analyze collected data.
Here’s how you can get a list of listening network ports and service names:
for i in $(lsof -i -P -n | grep -oP "(?<=\*:)[0-9]{2,}(?= \(LISTEN)" | sort -nu); do lsof -i :${i} | grep -v COMMAND | awk -v i=$i '{print $1,i}' | sort -u; done | column -t
sshd 22
httpd 80
rpcbind 111
httpd 443
xinetd 873
rpc.rquot 875
httpd 1701
mysqld 3306
splunkd 8000
splunkd 8089
Let’s say we want iptables to log all inbound traffic for port 443. Here’s the rule for this:
iptables -I INPUT -p tcp --dport 443 -m state --state NEW -j LOG --log-prefix "HTTPS-443 inbound: "
Save the rules and reload iptables service and in /var/log/messages you should now be able to see entries like these:
Aug 30 16:28:09 ncc1701 kernel: [6205722.091794] HTTPS-443 inbound: IN=bond0 OUT= MAC=a0:48:1c:b8:17:b8:5c:f4:ab:67:c4:4b:08:00 SRC=192.0.101.11 DST=192.168.122.137 LEN=52 TOS=0x00 PREC=0x00 TTL=56 ID=5926 DF PROTO=TCP SPT=14784 DPT=443 WINDOW=29200 RES=0x00 SYN URGP=0 Aug 30 16:28:09 ncc1701 kernel: [6205722.092482] HTTPS-443 inbound: IN=bond0 OUT= MAC=a0:48:1c:b8:17:b8:5c:f4:ab:67:c4:4b:08:00 SRC=192.0.102.70 DST=192.168.122.137 LEN=52 TOS=0x00 PREC=0x00 TTL=54 ID=18456 DF PROTO=TCP SPT=56268 DPT=443 WINDOW=29200 RES=0x00 SYN URGP=0
Seeing the source IP is useful but, perhaps, seeing the source hostname, country of origin, and organization would be even better. Here’s how this can be accomplished:
grep 'HTTPS-443 inbound:' /var/log/messages | while read line
do
src_ip="$(echo ${line} | grep -oP "(?<=SRC=)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(?= DST=)")"
src_desc="$(geoiplookup ${src_ip} 2>/dev/null | grep -v 'not found' | awk -F: '{$1=""; print $0}' | sed 's/\//_/g' | xargs)"
if [ -z "${src_desc}" ]
then
src_desc="IP origin unknown"
fi
echo "${line}" | sed -r "s/SRC=${src_ip} /SRC=${src_ip} ${src_desc} /g"
done
The result might look something like this:
Aug 30 16:11:13 ncc1701 kernel: [6204705.454169] HTTPS-443 inbound: IN=bond0 OUT= MAC=a0:48:1c:b8:17:b8:5c:f4:ab:67:c4:4b:08:00 SRC=104.129.204.86 US, United States US, GA, Georgia, Atlanta, 30301, 33.749001, -84.388000, 524, 404 AS22616 ZSCALER, INC. DST=192.168.122.137 LEN=60 TOS=0x00 PREC=0x00 TTL=58 ID=6674 DF PROTO=TCP SPT=19707 DPT=443 WINDOW=65535 RES=0x00 SYN URGP=0 Aug 30 16:11:14 ncc1701 kernel: [6204706.192422] HTTPS-443 inbound: IN=bond0 OUT= MAC=a0:48:1c:b8:17:b8:5c:f4:ab:67:c4:4b:08:00 SRC=45.12.32.102 IP origin unknown DST=192.168.122.137 LEN=60 TOS=0x00 PREC=0x00 TTL=53 ID=26339 DF PROTO=TCP SPT=56736 DPT=443 WINDOW=14480 RES=0x00 SYN URGP=0 Aug 30 16:11:15 ncc1701 kernel: [6204706.636596] HTTPS-443 inbound: IN=bond0 OUT= MAC=a0:48:1c:b8:17:b8:5c:f4:ab:67:c4:4b:08:00 SRC=192.168.122.1 IP origin unknown DST=192.168.122.137 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=37548 DF PROTO=TCP SPT=38372 DPT=443 WINDOW=14600 RES=0x00 SYN URGP=0
Useful stuff, except you would probably want to exclude your private network addresses from this list. So here’s an enhanced version of the previous command:
grep 'HTTPS-443 inbound:' /var/log/messages | while read line
do
src_ip="$(echo ${line} | grep -oP "(?<=SRC=)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(?= DST=)")"
if [ $(echo ${src_ip} | grep -cE "(^0\.)|(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)") -eq 0 ]
then
src_desc="$(geoiplookup ${src_ip} 2>/dev/null | grep -v 'not found' | awk -F: '{$1=""; print $0}' | sed 's/\//_/g' | xargs)"
if [ -z "${src_desc}" ]
then
src_desc="IP origin unknown"
fi
echo "${line}" | sed -r "s/SRC=${src_ip} /SRC=${src_ip} ${src_desc} /g"
fi
done
So, what practical uses does this information have? Let’s imagine that for some reason I don’t want Zscaler connecting to my server. I run the previous command and save the output to a temporary file /tmp/iplog, and the do something like this:
wc -l /tmp/iplog && grep -c ZSCALER /tmp/iplog && grep ZSCALER /tmp/iplog | grep -oP "(?<=SRC=)([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(?= )" | sort -Vu
817 /tmp/iplog
413
104.129.204.86
In this data sample, this one external IP is responsible for more than half of all requests to my Web server. So blocking it may be a good idea. This command here will give you a list of organizations accessing your server:
grep -v 'origin unknown' /tmp/iplog | grep -oP "(?<= AS)[0-9]{3,} .*(?=DST=)" | awk '{$1=""; print $0}' | sort -u | sed 's/^ //g'
Amazon.com, Inc.
Automattic, Inc
Bell Canada
bet-at-home.com International Ltd.
Case Western Reserve University
CHINA UNICOM China169 Backbone
China Unicom Guangdong IP network
City Network Hosting AB
Cogent Communications
ColoCrossing
Combell NV
Comcast Cable Communications, LLC
...
And this command will count how many time each organization accessed your server:
grep -v 'origin unknown' /tmp/iplog | grep -oP "(?<= AS)[0-9]{3,} .*(?=DST=)" | awk '{$1=""; print $0}' | sed 's/^ //g' | sort | uniq -c | sort -k1rn
413 ZSCALER, INC.
61 Rostelecom
60 China Unicom Guangdong IP network
36 Automattic, Inc
24 Google LLC
15 OVH SAS
12 DataWeb Global Group B.V.
9 Host NIT Inc
9 Microsoft Corporation
You can also get a count by country of origin:
grep -v 'origin unknown' /tmp/iplog | awk -F, '{print $2}' | sed 's/^ //g' | sort | uniq -c | sort -k1rn
588 United States US
68 China CN
67 Russian Federation RU
19 France FR
11 Canada CA
8 Ukraine UA
7 Poland PL
6 Argentina AR
6 Czech Republic CZ
So when they tell you it’s the Russians and the Chinese who are breaking your server, take such information with a grain of salt. Most attacks originate from the US networks.
Perhaps unrelated to our topic of logging, here are a couple of iptables tricks I find useful – both having to do with cleaning up your rules from time to time. Let’s say I banned a particular IP or range and now I want let those guys back in. Here’s an example:
# These IPs and ranges I want to take off my blacklist:
iptables -S | grep DROP | grepcidr 104.129.204.0/24
-A INPUT -s 104.129.204.0/24 -j DROP
-A INPUT -s 104.129.204.86/32 -j DROP
# Here's one way of doing this:
iptables -S | grep DROP | grepcidr 104.129.204.0/24 | while read i; do
j="$(echo ${i} | sed 's/\-A/\-D/g')"
iptables $(eval echo $j)
done && service iptables save
Now, let’s imagine you blacklist some IPs, but you want them to be removed from your blacklist in a few days. Automatically, of course. Well, iptables rules have a handy comment option, allowing you to insert some arbitrary text into the rule without affecting its functionality. So our comment will be a date in epoch format that will show when this particular rule is supposed to expire. Here’s what I mean:
# Let's add this IP range to blacklist with a comment # showing epoch timestamp seven days from now iptables -A INPUT -m comment --comment "$(date -d'now + 7 days' +'%s')" -s 104.129.204.0/24 -j DROP # This is what it will look like: iptables -S | grepcidr 104.129.204.0/24 -A INPUT -s 104.129.204.0/24 -m comment --comment "1568065502" -j DROP
The command below will go through your iptables rules, pick those with what looks like an epoch timestamp, and delete any expired rules:
iptables -S | egrep '[0-9]{10,}' | while read i
do
if [ $(echo ${i} | egrep -o '[0-9]{10,}') -lt $(date +'%s') ]
then
echo "Dropping expired rule: ${i}"
j="$(echo ${i} | sed 's/\-A/\-D/g')"
iptables $(eval echo $j)
fi
done && service iptables save

