#!/bin/bash # Check pawn attempts # Warn on blank log # Incorrect timestamp - very old TS or latest TS older than older TS # out of order or blocks of time are missing # Give options to process only a certain number of day's log # Check if mawk is available -> if yes, use that if [[ "$(id -u)" != "0" ]] then echo "Need root access to check /var/log/ufw.log file" exit 1 fi # Since changing language improves performance LC_ALL=C # Adding the port-program mapping to a shell variable # We shall pass it to awk later declare port_programs=$(sudo ss -lpntu | awk 'BEGIN {FS=":"} NR>1 && $1 !~ "\\[" {print $2, $NF} # Row does NOT contains [ -> Fetch 2nd and last columns NR>1 && $1 ~ "\\[" {print $4, $NF} # Row contains [ -> Fetch 4th and last columns ' | awk '{ port = $1 program = substr($3, index($3, "\"")+1, index($3, ",")-index($3,"\"")-2) # Add multiple programs listening on a single port as comma separated list if (port_programs[port]==""){ port_programs[port]=program }else if (index(port_programs[port], program) > 0){ # Remove duplicates next } else{ port_programs[port]=port_programs[port]","program } } END { for (port in port_programs) print port, port_programs[port] }') cat /var/log/ufw.log | mawk ' function GetValue(currentColumnValue, stringToSearch) { if(currentColumnValue~"^"stringToSearch){ sub(stringToSearch"=", "", currentColumnValue) currentColumnValue=(currentColumnValue=="")?"-NA-":currentColumnValue return currentColumnValue } } $0~/BLOCK/{ # loop through each column # Capture value of those that contain "IN", "OUT" "SRC", "DST", "SPT", "DPT" & "PROTO" capture for (n=1;n<=NF;n++){ if($n=="[UFW" && $(n+1)~/]$/) EVENT=$n "-" $(n+1) if($n=="[UFW" && $(n+2)~/]$/) EVENT=$n "-" $(n+1) "-" $(n+2) if($n~/^IN/) IN = GetValue($n, "IN") if($n~/^OUT/) OUT = GetValue($n, "OUT") if($n~/^SRC/) SRC = GetValue($n, "SRC") if($n~/^DST/) DST = GetValue($n, "DST") if($n~/^SPT/) SRCPORT = GetValue($n, "SPT") if($n~/^DPT/) DSTPORT = GetValue($n, "DPT") if($n~/^PROTO/) PROTO = GetValue($n, "PROTO") } print EVENT, IN, OUT, SRC, DST, SRCPORT, DSTPORT, PROTO }' | uniq -c | sort -n | awk -v port_programs_bash="$port_programs" ' BEGIN{ # Column headers printf ("%6s %15s %10s %10s %15s %15s %8s %8s %8s %20s\n", "Count", "Event-Type", "IN-BOUND?", "OUT-BOUND?", "SRC-Addr", "DST-Addr", "SRC-PORT", "DST-PORT", "Protocol", "Port-Listeners") print "-----------------------------------------------------------------------------------------------------------------------------" #Deserialize the port_programs_bash len=split(port_programs_bash, temp) for (i=2;i<=len;i+=2) { port_programs[temp[i-1]]=temp[i] } i=0 } { total+=$1 SRC_IPS[$5]+=$1 DST_IPS[$6]+=$1 printf ("%6d %15s %10s %10s %15s %15s %8d %8d %8s %20s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, port_programs[$7]) } END { print "" print "Top-5 Most-blocked Destination-IPs" print "-----","-----", "-----", "-----", "-----", "-----" printf ("%16s\t%5s \n", "IP", "Count") PROCINFO["sorted_in"] = "@val_num_desc" for (IP in DST_IPS){ printf ("%16s\t%5d \n", IP, DST_IPS[IP]) i++; if(i==5) break } print " " print "Top-5 Most-blocked Source-IPs" print "-----","-----", "-----", "-----", "-----" printf ("%16s\t%5s \n", "IP", "Count") for (IP in SRC_IPS){ printf ("%16s\t%5d \n", IP, SRC_IPS[IP]) j++; if(j==5) break } print "" print "Total records parsed = "total } ' # Check if there are huge gaps in TS cat /var/log/ufw.log | awk '{ curr_ts=$1 # TS out of order if(last_ts != "" && curr_ts < last_ts) something_wrong_at[i++]=curr_ts if (last_ts != "" && curr_ts > (last_ts + huge_gap)) gaps_at[++j]=curr_ts last_ts=curr_ts } '