ip-drop

This small daemon was inspired by autoDrop. You can find a SYS5 init style start/stop script here [L1 ]. It requires a recent linux system tclx, tail and iptables. - JBR May 2007

 # ip-drop.conf
 #
 # Drop IP addresses at the firewall when an attack is seen in
 # the hosts log files.  You will need the TclX scripting package
 # to run this program.  On RHEL and its work-a-likes you can
 # install it with "yum install tclx".
 #
 set chain ip-drop

 # A list of allowed hosts that are never blocked
 # Shell style file globbing can be used here.
 #
 set allow {
        *.cfa.harvard.edu
 }

 # Logs to watch
 #
 # The set of logs to be watched.
 #
 # A regular expression is used to trigger a blocking rule. Whitespace
 # must be quoted.
 # 
 # A regular expression is used to extract the host name value
 # from the log line which triggers the rule.  Parens should be
 # used to indicate the part of the extraction regular expression
 # that contains the hostname value to be blocked.  The host name
 # can be an IP address or a DNS hostname.
 #
 # Times are expressed in seconds or may be suffixed with m, h, or d
 # for minutes, hours or days.
 #
 #       log                     trigger                         extract                  hits    in      timeout
 #       ---                     -------                         ----------              ----    --      -------
 watch   /var/log/messages {     "authentication failure;"       "rhost=([^ ]*)"         3       10      36h     }
 watch   /var/log/secure   {     "Failed password"               "from [:f]*([^ ]*)"     4       10      36h
                                "Did not receive ident"         "from [:f]*([^ ]*)"     2       10      36h
                        }

ip-drop.tcl


 #!/usr/bin/tcl
 #
 # This script was inspired by autoDrop https://wiki.tcl-lang.org/16639
 #
 set conf  /etc/ip-drop.conf
 set log   /var/log/ip-drop


 proc daemonize { log } {                # From: https://wiki.tcl-lang.org/2224
   close stdin
   close stdout
   close stderr
   if {[fork]} {exit 0}
   id process group set
   if {[fork]} {exit 0}
   set fd [open /dev/null r]
   set fd [open $log a];        fconfigure $fd -buffering line
   set fd [open $log a];        fconfigure $fd -buffering line
   cd /
   umask 022
   return [id process]
  }

 proc ms { ms } {
    expr int([string map { s {} m *60 h *60*60 d *60*60*24 } $ms] *1000)
 }

 proc iptables { opt host } {
        puts "[clock format [clock seconds]] iptables $opt $::chain -s $host -j DROP"

        exec iptables $opt $::chain -s $host -j DROP
 }

 proc lline { file log patterns } {
    if { [gets $file line] < 0 } {
        puts stderr "ip-drop: not watching $log"
        close $file
    }
    foreach { trigger extract maxhits interval timeout } $patterns {
        if { [regexp $trigger $line] && [regexp $extract $line -> host] } {
            foreach allowed $::allow { if { [string match $allowed $host] } { return } }

            if { [catch {
                if { [incr ::Hits($host,$trigger)] == $maxhits } {
                    if { [catch { after cancel $::Drop($host) } reply] } {
                        iptables -I $host
                    }

                    set ::Drop($host) [after [ms $timeout] "iptables -D $host; unset ::Drop($host)"]
                }
            }] } {
                set ::Hits($host,$trigger) 1
            }

            after [ms $interval] [list incr ::Hits($host,$trigger) -1]
        }
    }
 }

 proc watch { log patterns } {
    fileevent [set file [open "| tail -0f $log"]] r [list lline $file $log $patterns]
    lappend ::tails [pid $file]
 }

 proc shutdown {} {
    foreach pid $::tails { kill $pid }

    catch { exec iptables -D INPUT -j $::chain }        reply; # puts $reply
    catch { exec iptables -F $::chain }                 reply; # puts $reply
    catch { exec iptables -X $::chain }                 reply; # puts $reply

    puts "[clock format [clock seconds]] ip-drop: exiting"
    exit
 }

 set tails {}

 daemonize $log

 source $conf

 lappend allow {}        ; # If the extract string fails then skip dropping a null hostname.

 catch { exec iptables -N $::chain }             reply; # puts $reply
 catch { exec iptables -F $::chain }             reply; # puts $reply
 catch { exec iptables -D INPUT -j $::chain }    reply; # puts $reply
 catch { exec iptables -I INPUT -j $::chain }    reply; # puts $reply

 signal ignore  SIGHUP
 signal unblock {QUIT TERM}
 signal trap    {QUIT TERM} shutdown

 puts "[clock format [clock seconds]] ip-drop: start"
 vwait forever