#!/bin/bash # # firewall-check-ssh-fail -- 2008-10-09, last edit 2008-11-02 # # script to scan /var/log/messages for ssh login failures and merge # offender's CIDR block into a ban list # # Copyright (C) 2008 Grant Coady GPLv2 # # usage: perhaps run as a cron job to merge new ssh login attackers # into the firewall's ssh-ban-list file # # requires ip2cn-server for the -c or -n options, see: # http://bugsplatter.id.au/ip2cn/ # # home site: http://bugsplatter.id.au/firewall/ # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # location of ssh failed login ban list for firewall, this list is merged # with new ssh failed login attempts, edit the list to remove items # command line option defaults ban_list="/usr/local/etc/ssh-ban-list" # -b cidr= # -c delete= # -d log_file= # -f hit=0 # -h inetport=4743 # -i: ip2cn-server default: 4743 names= # -n verbose= # -v xbanlist="/usr/local/etc/ssh-ban-list-exceptions" # -x show_usage_n_exit() # - { echo " firewall-check-ssh-fail -- check for sshd login failures usage: firewall-check-ssh-fail [-cDn] [-f log_file_name] [-i port] -b filename ban list file full filename, default: /usr/local/etc/ssh-ban-list -c use CIDR/n block and show country names -d delete existing list, default is to merge with list -f filename input file name, default: /var/log/messages -h hit on ssh port counts as login attempt (default is to check for sshd reported login failures) -i port_num use alternate inetport for ip2cn-server -n show country names in IP list -x filename file with list of IPs never to ban, in grep format, default: /usr/local/etc/ssh-ban-list-exceptions -v be verbose examples: firewall-check-ssh-fail zcat /var/log/messages.1.gz | firewall-check-ssh-fail -nv zcat /var/log/messages.*.gz | firewall-check-ssh-fail -dnf \\ \"- /var/log/messages\" " > /dev/stderr exit 1 } # read command line while getopts "b:cdf:hi:nvx:" option do case $option in b) ban_list=$OPTARG;; c) cidr=1;; d) delete=1;; f) log_file=$OPTARG;; h) hit=1;; i) inetport=$OPTARG;; n) names=1;; v) verbose=1;; x) xbanlist=$OPTARG;; *) show_usage_n_exit;; esac done # option -i: specify different ip2cn-server port get_cidr_block() # IP_addr { echo $1 | gawk -v port=$inetport ' BEGIN { service = "/inet/tcp/0/localhost/" port } { print |& service; service |& getline; print } ' 2> /dev/null # return "" for error } # option -f: set log file name, -v: verbose [ "$log_file" == "" ] && log_file="/var/log/messages" \ && [ $verbose ] && echo " using /var/log/messages" # option -h: find ssh hits or failed ssh login IP addresses tmp=$(mktemp) || exit 1 awk -v hit=$hit ' (hit > 0) && /DPT=22 / { split($11, a, "="); print a[2]; next } $5 !~ /^sshd/ { next } /Did not receive|Invalid user/ { print $NF } /reverse mapping/ { gsub(/[][]/, "", $12); print $12 } /Address/ && /does not map/ { print $7 } ' $log_file | sort -t. -n -k1,1 -k2,2 -k3,3 -k4,4 | uniq > $tmp # option -x: remove exception IPs if [ -r $xbanlist ] then tmp2=$(mktemp) || exit 1 touch $tmp # silence missing file error grep -Evf $xbanlist $tmp > $tmp2 && mv $tmp2 $tmp else echo "error: cannot read '-x $xbanlist' file" > /dev/stderr exit 1 fi # option -d: delete old ban list [ $delete ] && rm -f $ban_list # option -c or -n: convert to CIDR and/or lookup country name ra= [ $names ] && ra="p" # reply addr is IP [ $cidr ] && ra="b" # reply addr is CIDR block, overrides -n if [ -n "$ra" ] then while read ip rest do reply="$(get_cidr_block "$ip $ra # c n")" if [ -n "$reply" ] then echo "$reply" >> $ban_list else # no ip2cn-server, use IP echo $ip >> $ban_list fi done < $tmp else cat $tmp >> $ban_list fi # remove duplicates from $ban_list touch $ban_list # silence missing file error sort -t. -n -k1,1 -k2,2 -k3,3 -k4,4 < $ban_list | uniq > $tmp \ && mv $tmp $ban_list # -v: display the output file [ $verbose ] && cat $ban_list \ && echo " found $(wc -l $ban_list) offender IPs" # end