服务器流量暴涨,首先通过访问日志查看。每个IP并发不是很高,分布也很广,但是都是只访问主页,可见不是抓取服务导致。通过“netstat -nat|awk ‘{print awk $NF}’|sort|uniq -c|sort -n”t命令查看,连接中还有很多SYN_RECV、TIME_WAIT、ESTABLISHED。看来是小型的攻击。首先是修改web服务的配置,缩减了timeout时间,在nginx上禁止一部分并发比较高的IP,小改系统参数。流量降了20M,但还是太高。nginx虽然禁止了IP,如果用浏览看是一个空白页面,但是每个连接还会产生很小的流量。最后决定将最小的返回值也封掉,直接在iptables上drop掉连接。每个超限IP暂定封2天。写个脚本自动运行。
此脚本在在centos5.3+python2.6.6使用通过。需要注意,此脚本统计日志完毕会将nginx日志文件清空,如需保留日志,请自行修改。每次加载iptables规则时会清楚上次所有规则,如果有使用其它规则也要自行处理。
#!/bin/python #-*- coding:utf-8 -*- # Filename: drop_ip_iptables.py # Revision: 1.0 # Date: 2013-2-21 # Author: simonzhang # web: www.simonzhang.net # Email: simon-zzm@163.com ### END INIT INFO import os import time from string import strip #### 参数和脚本中使用到的系统命令 nginx_log = "/usr/local/nginx/logs/nginx.access.log" # 统计nginx日志中IP访问数量 check_comm = "/bin/cat %s |awk ' ''{print $1}'|sort |uniq -c|sort -n -k1 -r" % nginx_log # 放在crontab中10分钟跑一次,访问超出n次的全部封掉 overproof = 3000 # 被封地址记录文件 # 文件中记录封IP时间和IP地址。时间单位为秒 lock_ip_list = "/usr/local/nginx/logs/lock_ip_list.txt" # 被封地址解开时间。时间单位为秒 unlock_time = 3600*24*2 def manage_lock_ip(): # 获取当前时间 get_now_time = int(time.time()) # 管理日志字典 man_ip = {} # 处理日志中的IP try: log_file = open('%s' % lock_ip_list, 'rb').readlines() for get_ip_info in log_file: _get_ip_info = strip(get_ip_info).split(' ') man_ip['%s' % _get_ip_info[1]] = int(_get_ip_info[0]) except: exit(0) # 清空iptable列表,和ip记录日志 os.popen('/sbin/iptables -F') clean_file = open('%s' % lock_ip_list, 'rb+') clean_file.truncate() # 开始处理IP,被封没有超时的IP写入iptables和日志中 log_file = open('%s' % lock_ip_list, 'ab') for loop_ip in man_ip.keys(): if (get_now_time - man_ip[loop_ip]) < unlock_time: os.popen('/sbin/iptables -I INPUT -s %s -j DROP' % loop_ip) log_file.write('%s %s\n' % (man_ip[loop_ip], loop_ip)) log_file.close() def main(): # 已封IP地址字典 drop_ip_list = {} # 加载已封IP日志 try: log_file = open('%s' % lock_ip_list, 'rb').readlines() for get_drop_ip_info in log_file: _get_drop_ip_info = strip(get_drop_ip_info).split(' ') drop_ip_list['%s' % _get_drop_ip_info[1]] = int(_get_drop_ip_info[0]) except: os.mknod('%s' % lock_ip_list) # 获取nginx日志中的访问超高的ip并写入日志 access_high_ip = os.popen('%s' % check_comm).readlines() for get_ip_count in access_high_ip: try : _get_ip_count = strip(get_ip_count).split(' ') _get_ip = _get_ip_count[1] _get_count = _get_ip_count[0] except: pass if (int(_get_count) > int(overproof)) and (_get_ip not in drop_ip_list.keys()): now_time = int(time.time()) log_file = open('%s' % lock_ip_list, 'ab+') log_file.write('%s %s\n' % (now_time, _get_ip)) log_file.close() # 统计完毕清空nginx日志 log_file = open('%s' % nginx_log, 'wb') log_file.truncate() # 处理要封的IP和要解开的IP manage_lock_ip() if __name__ == '__main__': main()
再补充两条日志分析命令
# 单位时间内统计单个IP只访问首页的数量:
#check_comm = “/bin/cat %s|grep ‘GET / HTTP/1.1’|awk ‘ ”{print $1}’|sort |uniq -c|sort -n -k1 -r” % nginx_log
# 单位时间内统计单个IP访问相同页面的数量
#check_comm = “/bin/cat %s|awk -F'”‘ ‘{print $1 $2}’|awk ‘ ”{print $1″ “$6” “$7” “$8}’|sort -k2,4 -r|uniq -c|sort -n -k1 -r” % nginx_log
源码下载
drop_ip_tables