昨天接到客户服务器告警,看到一个nginx+php网站正在遭受CC攻击,导致服务器复制居高不下,正常业务访问大多数时候出现502错误。
CC攻击是 DDOS(分布式拒绝服务) 的一种,DDoS是针对IP的攻击,而CC攻击的是网页。CC攻击来的IP都是真实的,分散的。数据包都是正常的数据包,攻击的请求全都是有效的请求,无法拒绝的请求。服务器可以连接,但是网页就是访问不了,也见不到特别大的异常流量,但是持续时间长,仍能造成服务器无法进行正常连接,危害更大。
查看服务器日志,大量IP访问网站,导致负载居高不下。user-agent也伪装成正常浏览器或者搜索引擎,不能直接通过agent屏蔽。
一、增加nginx防CC限制
在nginx.conf配置的http模块中增加类似如下代码
limit_req_zone $http_x_forwarded_for zone=one:10m rate=3r/s;
limit_conn_zone $http_x_forwarded_for zone=addr:10m;
因为网站使用了cdn,所以限制的时候使用$http_x_forwarded_for参数来获取真实IP。
然后在网站的server模块中添加
limit_req zone=one burst=5 nodelay;
limit_conn addr 3;
nginx防止CC攻击的具体配置不清楚的可以参考这篇文章:http://blog.nbqykj.cn/sysmaint/linux-os/2556.html
添加这个配置后,如果某个IP访问太频繁,打印的日志里面就会出现503错误代码。
二、分割nginx日志
出现大量日志过后,我为了后续分析方便,决定把nginx的正常日志,错误日志和拦截日志分开。
在nginx.conf配置的http模块中增加如下内容:
map $status $normal {
~^2 1;
~^3 1;
default 0;
}
map $status $notfound {
~^4 1;
default 0;
}
map $status $abnormal {
~^5 1;
default 0;
}
定义了三个变量,$normal、$notfound、$abnormal,分别表示正常日志、拦截日志(40X)、系统异常日志。
增server模块中定义日志输出到三个文件
access_log /var/log/nginx/blog.nbqykj.cn.log main if=$normal;
access_log /var/log/nginx/blog.nbqykj.cn-error.log main if=$abnormal;
access_log /var/log/nginx/blog.nbqykj.cn-not-found.log main if=$notfound;
详细配置参考:http://blog.nbqykj.cn/sysmaint/linux-os/2627.html
三、禁止IP
在添加了nginx防cc攻击防护和日志分割后,可以看到在error.log日志中出现了很多503错误的非法IP,接下来就是需要把这些IP通过nginx直接禁止掉。
3.1、在nginx配置中定义真实IP变量$clientRealIp
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
3.2、nginx禁止IP控制脚本
保存在某个路径并赋给他可执行权限,例如我放在:/usr/local/bin/nginx_deny_ctl.sh
#!/bin/bash
NGINX_BIN=/usr/sbin/nginx
DENY_CONF=/etc/nginx/deny_ip
COLOR_RED=$( echo -e "\e[31;49m" )
COLOR_GREEN=$( echo -e "\e[32;49m" )
COLOR_RESET=$( echo -e "\e[0m" )
rep_info() { echo;echo -e "${COLOR_GREEN}$*${COLOR_RESET}";echo; }
rep_error(){ echo;echo -e "${COLOR_RED}$*${COLOR_RESET}";echo;exit 1; }
reload_nginx()
{
$NGINX_BIN -t >/dev/null 2>&1 && \
$NGINX_BIN -s reload && \
return 0
}
pre_check()
{
test -f $NGINX_BIN || rep_error "$NGINX_BIN not found,Plz check and edit."
test -f $DENY_CONF || rep_error "$DENY_CONF not found,Plz check and edit."
MATCH_COUNT=$(show_list | grep -w $1 | wc -l)
return $MATCH_COUNT
}
create_rule()
{
test -f $DENY_CONF/$1.conf && \
rep_error "$DENY_CONF/$1.conf already exist!."
cat >$DENY_CONF/$1.conf<<EOF
if (\$clientRealIp ~* "$1") {
return 403;
break;
}
EOF
test -f $DENY_CONF/$1.conf && \
rep_info "$DENY_CONF/$1.conf create success!" && \
reload_nginx
exit 0
rep_error "$DENY_CONF/$1.conf create failed!" && \
exit 1
}
del_rule()
{
rm -f $DENY_CONF/$1.conf
reload_nginx
exit 0
}
case $1 in
"-a"|"--add" )
create_rule $2;
;;
"-d"|"--del" )
del_rule $2
;;
esac
其中NGINX_BIN=/usr/sbin/nginx、DENY_CONF=/etc/nginx/deny_ip根据实际情况修改,创建 /etc/nginx/deny_ip 目录。
测试是否正常添加和删除
/usr/local/bin/nginx_deny_ctl.sh -a 1.2.3.4
/usr/local/bin/nginx_deny_ctl.sh -d 1.2.3.4
3.3、在网站的模块中增加deny目录
include deny_ip/*.conf;
3.4、配置fail2ban自动过滤禁止
过滤器filter:/etc/fail2ban/filter.d/nginx-bad-ip.conf
[Definition]
failregex = ^.* -.*"(GET|POST|HEAD).*HTTP.*" 503 \d+ ".*" ".*" "(-|<HOST>)"$
ignoreregex =
操作action:/etc/fail2ban/action.d/nginx-deny.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = /usr/local/bin/nginx_deny_ctl.sh -a <ip>
actionunban = /usr/local/bin/nginx_deny_ctl.sh -d <ip>
[Init]
配置jail:/etc/fail2ban/jail.d/nginx.local
[nginx-deny]
enabled = true
filter = nginx-bad-ip
action = nginx-deny
logpath = /var/log/nginx/mail-error.log
maxretry = 1
findtime = 10
bantime = 86400
3.5、开启防护
在重新启动fail2ban和nginx后,观察日志和禁止目录,可以看到fail2ban自动发现了很多异常IP,然后调用action任务加入nginx禁止目录,这些IP再次访问就会直接提示403没有找到。
nginx禁止目录下的IP已经后1685个了。
今天再次观察下服务器的负载,看到攻击者没有达到目的后也就没有攻击了。
4、PHP优化,增加OPCache缓存
服务器维护,接下来就再给PHP安装一个缓存,优化PHP执行的速度,这样可以承受更多的压力负载。
安装opcache模块
yum install php-opcache
[opcache]
opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=100000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1
opcache.fast_shutdown=1
参考网址:http://blog.nbqykj.cn/sysmaint/linux-os/2594.html