零、前言

1.CC攻击简述

CC攻击(Challenge Collapsar)是常见网站应用层攻击的一种,目的是消耗服务器资源,降低业务响应效率;极端情况会让站点无法正常提供服务;

2.本文要点

旨在描述,通过ngx_lua模块开发并集成基于令牌桶算法的简易IP限速功能,实现CC攻击的防护;

3.本文面向的人群

有一定的运维、开发基础的人群;

一、服务部署

0.环境

a.系统

CentOS Linux release 7.5.1804 (Core);

b.资源存放目录

mkdir /root/ngx_lua

c.要求

各种编译安装相关依赖和报错请google解决;

d.NGX_LUA官文

https://github.com/openresty/lua-nginx-module#installation

e.准备

cd /root/ngx_lua

1.Lua

wget http://www.lua.org/ftp/lua-5.3.4.tar.gz

tar zxf lua-5.3.4.tar.gz

cd lua-5.3.4

make linux test

cd ..

2.LuaJIT 2.1

wget http://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz

tar zxvf LuaJIT-2.1.0-beta3.tar.gz

cd LuaJIT-2.1.0-beta3

#指定安装目录

make PREFIX=/usr/local/luajit2

make install PREFIX=/usr/local/luajit2

cd ..

3.NDK

wget https://github.com/simplresty/ngx_devel_kit/archive/v0.3.1rc1.tar.gz

tar zxvf v0.3.1rc1.tar.gz

4.LUA_NGX

wget https://github.com/openresty/lua-nginx-module/archive/v0.10.13.tar.gz

tar zxvf v0.10.13.tar.gz

5.LUA_RESTY_REDIS

wget -O "lua-resty-redis-master.zip" https://codeload.github.com/openresty/lua-resty-redis/zip/master

unzip lua-resty-redis-master.zip

cd lua-resty-redis-master

make install PREFIX=/usr/local/lua-redis

cd ..

5.REDIS

wget http://download.redis.io/releases/redis-4.0.9.tar.gz

tar zxvf redis-4.0.9.tar.gz

cd redis-4.0.9

#复制配置文件模板

cp redis.conf /etc/

#编译安装

make PREFIX=/usr/local/redis

make install PREFIX=/usr/local/redis

#尝试运行,可以考虑打包为后台服务或托管给supervisor,本文略;

cd /usr/local/redis/bin

./redis-server /etc/redis.conf

6.Nginx

#添加NGINX 用户

useradd -s /sbin/nologin www

#下载、解压并进入目录

wget http://nginx.org/download/nginx-1.13.12.tar.gz

tar zxvf nginx-1.13.12.tar.gz

cd nginx-1.13.12

# 增加环境变量

export LUAJIT_LIB=/usr/local/luajit2/lib

export LUAJIT_INC=/usr/local/luajit2/include/luajit-2.1

# 编译安装

./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_gzip_static_module --with-http_sub_module  --with-ld-opt="-Wl,-rpath,/usr/local/luajit2/lib" --add-dynamic-module=/root/ngx_lua/ngx_devel_kit-0.3.1rc1 --add-dynamic-module=/root/ngx_lua/lua-nginx-module-0.10.13

make && make test

# 编辑主配置文件使其支持NGX_LUA

vim /usr/local/nginx/conf/nginx.conf

# 指定为其创建的用户

user www www;

# 指定进程数及将进程绑定至CPU核心;

worker_processes  auto;

worker_cpu_affinity auto;

pid        logs/nginx.pid;

# 打开文件数

worker_rlimit_nofile    65535;

# 此处加载LUA相关模块

load_module modules/ndk_http_module.so;

load_module modules/ngx_http_lua_module.so;

events {

use epoll;

worker_connections  65535;

accept_mutex off;

multi_accept on;

}

http {

include       mime.types;

default_type  application/octet-stream;

server_names_hash_bucket_size       128;

client_header_buffer_size   64k;

large_client_header_buffers 4       32k;

client_max_body_size        512m;

# lua redis 依赖包

lua_package_path "/usr/local/lua-redis/lib/lua/?.lua;;";

sendfile  on;

keepalive_timeout  60;

server_tokens       off;

log_format access '$remote_addr - $remote_user [$time_local] "$request" '

'$status $body_bytes_sent "$http_referer" '

'"$http_user_agent" "$http_x_forwarded_for"';

include conf.d/*.conf;

}

:wq

nginx -t && nginx

二、开发LUA响应体及建立VHOST

1.建立lua脚本存放目录

mkdir /usr/local/nginx/conf/lua

2.开发用于响应内容的lua脚本

vim /usr/local/nginx/conf/lua/content.lua

--获取请求的HEADER

local headers = ngx.req.get_headers()

--依次通过x_real_ip,x_forwarded_for,remote_addr获取客户端IP

local clientip = headers["X-Real-IP"]

if clientip == nil then

clientip = headers["x_forwarded_for"]

end

if clientip == nil then

clientip = ngx.var.remote_addr

end

--指定响应内容

ngx.say("Your Ip Adress is ",clientip,", WelCome!")

:wq

3.搭建用于测试的VHOST

a.新建配置文件

vim /usr/local/nginx/conf/conf.d/luatest.conf

server

{

#指定监听端口及主机名

listen 80;

server_name www.knownsec.com;

#建立测试地址

location /lua_test

{

# 指定响应的默认MIME类型

default_type "text/html";

# 通过lua响应内容

content_by_lua_file conf/lua/index.lua;

}

error_log  /home/log/ngx/error.log;

access_log  /home/log/ngx/access access;

}

:wq

b.测试并重载配置

nginx -t

nginx -s reload

4.测试访问

curl --resolve www.knownsec.com:80:192.168.0.196 http://www.knownsec.com/lua_test

Your Ip Adress is 192.168.0.196, WelCome!

四、IP限速实现原理

1.请求处理过程

a.NGINX的请求处理过程一共划分为11个阶段,分别是:post-read、server-rewrite、find-config、rewrite、post-rewrite、 preaccess、access、post-access、try-files、content、log.(参考:https://github.com/nginx/nginx/blob/master/src/http/ngx_http_core_module.h)

b.在nginx官方文档(参考:https://www.nginx.com/resources/wiki/modules/lua/)中,可处理阶段均有相应的lua指令;就本文的目的而言,访问限速控制处于access阶段,所以需要使用的指令为access_by_lua;

c.为了方便调试和管理,可以使用access_by_lua_file指令直接加载指定路径下的lua文件来对access过程进行处理;

2.基于令牌桶算法的逻辑

a.令牌桶算法可控制请求的数量,并允许突发大量请求的情况。

b.当用户请求Nginx时,判断该location是否需要限制流量;

c.若需要,则检查当前IP是否已有令牌桶,若无则使用setex往redis中放入令牌桶,并指定的令牌数量及“桶”过期时间;设定单位时间内允许访问次数,比如1分钟允许10次,则令牌数量为9(当前请求算作首次消耗),过期时间60s

d.若已有令牌桶,并且令牌数量大于0,则使用decr使其值减1(消耗令牌)并放行;

e.若令牌数量为0,则拦截;

3.代码实现

vim /usr/local/lua-redis/lib/lua/LimitRate.lua

--加载REDIS模块

local r_md = require "resty.redis"

--获取当前请求的HEADER

local headers = ngx.req.get_headers()

--指定REDIS的地址和端口

local redis_ip = "127.0.0.1"

local redis_port = "9600"

--建立redis实例

local redis = r_md:new()

--指定单位时间

local qtrange = 60

--允许访问的次数

local qcount = 10

--尝试根据HTTP头遂级获取IP

local clientip = headers["X-Real-IP"]

if clientip == nil then

clientip = headers["x_forwarded_for"]

end

if clientip == nil then

clientip = ngx.var.remote_addr

end

--释放redis连接的函数

local function redis_close(red)

--释放连接,使用set_keepalive指令将当前连接放入当前进程的连接池中待用,需要指定连接池的大小及其中各个连接的空闲超时时间;

local pool_max_idle_time = 10000 --毫秒

local pool_size = 100 --连接池大小

local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)

if not ok then

ngx_log(ngx_ERR, "set redis keepalive error : ", err)

end

end

--指定所有REDIS操作的超时时间,其中包含了连接超时

redis:set_timeout(1000)

建立连接,若异常则释放连接;

local ok, wrong = redis:connect(redis_ip,redis_port)

if not ok then

redis_close(redis)

end

--建立限速类

LimitIpRate = {}

--限速方法,令牌桶限速逻辑实现

function LimitIpRate:is_limited()

--尝试获取当前IP的令牌桶,令牌桶命名为“x.x.x.x|pool”

local res, err = redis:get(clientip.."|pool")

if not res then

ngx.log(ngx.ERR,"lua error: ",err)

end

--如果res不为空并且类型为string,则代表获取到了对应的令牌桶

if type(res) == "string" then

--可用令牌数量为0则拦截

if tonumber(res) == 0 then

ngx.exit(ngx.HTTP_FORBIDDEN)

--反之放行并让令牌数量减少1

else

add,err = redis:decr(clientip.."|pool")

if not add then

ngx.log(ngx.ERR,"lua error: ",err)

end

end

--如果res不为空并且类型为userdata,则代表redis中没有对应令牌桶

elseif type(res) == "userdata" then

--往redis中放入指定名称、过期时间、值的键值对

ini, err = redis:setex(clientip.."|pool", qtrange, (qcount-1))

if not ini then

ngx.log(ngx.ERR,"lua error: ",err)

end

end

end

--调用限速方法

LimitIpRate.is_limited()

:wq

4.将LimitRate.lua集成进NGINX配置文件

a.编辑配置文件,加入指令

vim /usr/local/nginx/conf/conf.d/luatest.conf

server

{

#指定监听端口及主机名

listen 80;

server_name www.knownsec.com;

#建立测试地址

location /lua_test

{

# 指定响应的默认MIME类型

default_type "text/html";

# 通过lua对access进行过滤

access_by_lua_file "conf/lua/LimitRate.lua";

# 通过lua返回响应内容

content_by_lua_file conf/lua/index.lua;

}

error_log  /home/log/ngx/error.log;

access_log  /home/log/ngx/access access;

}

:wq

b.测试并重载配置

nginx -t

nginx -s reload

5.限速测试

for i in {1..12}; do curl -s --resolve www.knownsec.com:80:192.168.0.196 http://www.knownsec.com/lua_test -o /dev/null -w %{http_code};echo ;done

200

200

200

200

200

200

200

200

200

200

403

403

五、总结

a.在需要的location使用ngx_lua指定加载LimitRate.lua,并指定单位时间和单位时间中允许的请求次数;则可实现对该location请求的IP限速;

b.截止目前,仅是简单地描述了如何实现IP限速控制,还需要结合更多的业务环境来开发不同的需求,才能逐渐构建相对成熟的CC防护体系;

c.攻防之根本即为攻防双方对成本的投入;

d.若需成熟解决方案,可选择抗D保——攻击打不死,专接防不住;

基于 Nginx && Lua 的简易CC防护方案的更多相关文章

  1. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  2. 基于Nginx进行地图瓦片缓存的方案描述

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 在产品的迭代中,我们完成了移动端瓦片缓存方案和服务端瓦片缓存 ...

  3. 基于nginx + lua实现的反向代理动态更新

    大家都知道,nginx是当前应用非常广泛的web服务器,热度因为他的高并发高性能高可靠性,且轻量级!牛逼的不行,不多说这些. 今天要介绍的是,如何基于nginx和lua脚本,也就是在openresty ...

  4. 实战:一种在http请求中使用protobuffer+nginx+lua收集打点日志的方案

    背景 app打点日志的上报和收集,是互联网公司的基本需求. 一.方案选择 1.1 protobuffer vs json 探究一种以最高效的方式上报和解析打点数据是一个系统性的问题,需要解决的子问题有 ...

  5. #研发解决方案#基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案

    郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...

  6. 基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案 郑昀 基于杨海波的设计文档(转)

    郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...

  7. 基于nginx+lua简单的灰度发布系统

    upstream.conf upstream grey_1 { keepalive 1000; server localhost:8020; } upstream grey_2 { keepalive ...

  8. 单机闭环 使用Nginx+Lua开发高性能Web应用

    [西域骆驼D1532101213]西域骆驼(VANCAMEL)D1532101213 休闲套脚鞋 卡其43[行情 报价 价格 评测]-京东 http://item.jd.com/1856564.htm ...

  9. 使用NGINX+LUA实现WAF功能 和nginx 防盗链

    使用NGINX+LUA实现WAF功能 一.了解WAF 1.1 什么是WAF Web应用防护系统(也称:网站应用级入侵防御系统 .英文:Web Application Firewall,简称: WAF) ...

随机推荐

  1. Java基础笔记(十六)——继承

    继承 提取出一些共性特征,作为父类,子类就可以继承父类的这些开放成员,子类再添加自己独有的属性和方法.如果再有类具有这些共同特征,也可继承这个父类. 特点:1.利于代码复用     2.缩短开发周期 ...

  2. JMeter - 后处理器/脚本语言 - 比较

    当我们使用JMeter / Response数据处理进行密集负载测试时,我们可能会非常小心我们选择的后处理器/脚本语言的类型.在这篇文章中,我想说明这些后处理器/脚本语言如何影响测试的整体性能. 我们 ...

  3. 11-散列3 QQ帐户的申请与登陆 (25 分)

    实现QQ新帐户申请和老帐户登陆的简化版功能.最大挑战是:据说现在的QQ号码已经有10位数了. 输入格式: 输入首先给出一个正整数N(≤),随后给出N行指令.每行指令的格式为:“命令符(空格)QQ号码( ...

  4. java——集合、泛型、ArrayList、LinkedList、foreach循环、模拟ktv点歌系统

    集合:一系列特殊的类,这些类可以存储任意类型的对象,长度可变,集合类都在java.util包中. 但是集合记不住对象的类型,当把对象从集合中取出时这个对象的编译类型就变成了Object类型.这样在取元 ...

  5. 还不知道如何使用 IDEA ?教你三招快速掌握 IDEA

    前言 IntelliJ IDEA 是一个非常强大的 IDE,拥有许多功能.在 IDEA 中大部分功能都可以用快捷键去完成,如果掌握了大部分快捷键,可以只使用键盘开发了. ps: 最近正在练习快捷键,准 ...

  6. 将本地代码添加到github

    首先在github上创建一个仓库. 第一步:建立本地仓库 git init 关联远程仓库 git remote add origin https://github.com/tshua/***.git ...

  7. HDU 5505——GT and numbers——————【素数】

    GT and numbers Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)To ...

  8. <linux下内置命令和外部命令>

    Linux下内置命令和外部命令 1.linux的命令可以分为内部命令和外部命令: 内部命令在系统启动时就调入内存,是常驻内存的,所以执行效率高. 而外部命令是系统的软件功能,用户需要时才从硬盘中读入内 ...

  9. @b.windows.last.use

    @b.windows.last.use @b.windows.first.use be_true  一般用在step文件中

  10. 关于Arduino项目的构建思想-转自openbook开源杂志