This small daemon was inspired by
autoDrop. You can find a SYS5 init style start/stop script here [
1]. 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 http://wiki.tcl.tk/16639
#
set conf /etc/ip-drop.conf
set log /var/log/ip-drop
proc daemonize { log } { # From: http://wiki.tcl.tk/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