服务器流量暴涨,首先通过访问日志查看。每个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