Networking

Unix and Linux network configuration. Multiple network interfaces. Bridged NICs. High-availability network configurations.

Applications

Reviews of latest Unix and Linux software. Helpful tips for application support admins. Automating application support.

Data

Disk partitioning, filesystems, directories, and files. Volume management, logical volumes, HA filesystems. Backups and disaster recovery.

Monitoring

Distributed server monitoring. Server performance and capacity planning. Monitoring applications, network status and user activity.

Commands & Shells

Cool Unix shell commands and options. Command-line tools and application. Things every Unix sysadmin needs to know.

Home » Commands & Shells, Featured, Networking, Security

Working with iptables Logging

Submitted by on November 30, 2019 – 9:41 pm

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

 

Print Friendly, PDF & Email

Leave a Reply