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