#!/usr/bin/env ruby =begin Monitor SYN FLOOD attack use netstat and drop its ip every 1m or 5ms use shorewall or iptables, depanding on the crontab job settings Example: */5 * * * * /usr/bin/ruby /path/to/denyip.rb > /dev/null by xianhua.zhou , 2008.5.4 !!!JUST TRY IT, NOT SURE ALWAYS WORK!!! =end # The ip will be dropped if sent SYN connections >= MAX_SYN_LIMIT MAX_SYN_LIMIT = 20 # log file log_file = "/tmp/bad_ips.txt" # get all ip include SYN_RECV cmd_get_ips = "/bin/netstat -nt | /bin/grep :80 | /bin/grep -i SYN_RECV | /usr/bin/awk '{print $5}' | /usr/bin/awk -F : '{print $1}' | /usr/bin/sort" # drop ip cmd $cmd_drop_ip = "/sbin/shorewall drop %s" bad_ips = {} # check log file, create it if not exists File.new(log_file, "w").close unless File.exists? log_file # get dropped ip from log file dropped_ips = File.new(log_file).readlines # attack? has_attack = false # drop ip def drop_ip(ip) if File.exists?($cmd_drop_ip.split(' ')[0]) puts %x{#{$cmd_drop_ip % ip}} else puts %x{/sbin/iptables -A INPUT -s #{ip} -j DROP} end end if Process.uid != 0 puts "Your uid is not 0, this program requires root privileges!" exit end %x{#{cmd_get_ips}}.each_line {|ip| next if dropped_ips.include? ip if bad_ips.has_key? ip bad_ips[ip] += 1 else bad_ips[ip] = 1 end if bad_ips[ip] >= MAX_SYN_LIMIT has_attack = true dropped_ips << ip drop_ip(ip) end } # save bad ip to the log file if has_attack File.open(log_file, "w"){|f|f.write(dropped_ips)} puts Time.now.strftime("%Y-%m-%d %H:%M:%S") + " #{dropped_ips.size} --" end