CC攻击,DDoS的一种,针对URL进行攻击,持续时间长,危害大。

Nginx防CC限制

nginx.confhttp模块添加如下代码(定义触发条件):

1
2
3
# CC防御
limit_req_zone $binary_remote_addr zone=one:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

若使用CDN将$binary_remote_addr更改为$http_x_forwarded_for

在网站的server模块添加如下代码(定义达到触发条件时nginx所要执行的动作):

1
2
limit_req zone=one burst=20 nodelay;
limit_conn addr 15;

ngx_http_limit_req_module模块是通过漏桶原理来限制单位时间内的请求数,一旦单位时间内请求数超过限制,就会返回503错误。

ngx_http_limit_conn_module模块则是限制IP连接数

效果:某ip访问过于频繁,打印的日志里会出现503错误

分割Nginx日志

为后续分析方便,把正常日志、错误日志、拦截日志分开

nginx.confhttp模块添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
log_format  main  '[$time_local] "$request" $status $body_bytes_sent "$remote_addr"';

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模块添加如下代码:

1
2
3
access_log /data/wwwlogs/www.log combined if=$normal;
access_log /data/wwwlogs/www-error.log main if=$abnormal;
access_log /data/wwwlogs/www-not-found.log combined if=$notfound;

定义日志输出到三个文件

禁止IP

添加完防CC限制和日志分割后,可以再error.log中看到出现许多503错误,接下来就需要将那些日志中的IP ban掉

添加真实IP变量

nginx.confhttp模块添加如下代码:

1
2
3
4
map $http_x_forwarded_for  $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}

新增禁止IP脚本

将下列代码放在/usr/local/bin/nginx_deny_ctl.sh中,并赋予可执行权限

NGINX_BIN和DENY_CONF需要根据实际情况填写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/bash

NGINX_BIN=/usr/local/nginx/bin/nginx
DENY_CONF=/usr/local/nginx/conf/deny_conf

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 444;
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

测试是否正常添加删除IP:

1
2
/usr/local/bin/nginx_deny_ctl.sh -a 1.2.3.4
/usr/local/bin/nginx_deny_ctl.sh -d 1.2.3.4

增添deny目录

在网站的server模块添加如下代码:

1
include deny_ip/*.conf;

根据实际情况填写

配置fail2ban进行自动过滤禁止

过滤器filter:/etc/fail2ban/filter.d/nginx-bad-ip.conf

1
2
3
4
5
[Definition]

failregex = ^.* "(GET|POST|HEAD).*HTTP.*" (502|503) \d+ "(-|<HOST>)"$

ignoreregex =

操作action:/etc/fail2ban/action.d/nginx-deny.conf

1
2
3
4
5
6
7
[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

1
2
3
4
5
6
7
8
[nginx-deny]
enabled = true
filter = nginx-bad-ip
action = nginx-deny
logpath = /data/wwwlogs/www-error.log
maxretry = 1
findtime = 100
bantime = 86400

logpath需要配置为网站错误日志路径

重启服务

重新启动Nginx和fail2ban服务,打一发CC,静候效果。

我的阈值随便配的,请自行琢磨适合自己的阈值。