基于 Nginx && Lua 的简易CC防护方案
零、前言
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防护方案的更多相关文章
- 基于nginx+lua+redis高性能api应用实践
基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...
- 基于Nginx进行地图瓦片缓存的方案描述
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 在产品的迭代中,我们完成了移动端瓦片缓存方案和服务端瓦片缓存 ...
- 基于nginx + lua实现的反向代理动态更新
大家都知道,nginx是当前应用非常广泛的web服务器,热度因为他的高并发高性能高可靠性,且轻量级!牛逼的不行,不多说这些. 今天要介绍的是,如何基于nginx和lua脚本,也就是在openresty ...
- 实战:一种在http请求中使用protobuffer+nginx+lua收集打点日志的方案
背景 app打点日志的上报和收集,是互联网公司的基本需求. 一.方案选择 1.1 protobuffer vs json 探究一种以最高效的方式上报和解析打点数据是一个系统性的问题,需要解决的子问题有 ...
- #研发解决方案#基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案
郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...
- 基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案 郑昀 基于杨海波的设计文档(转)
郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...
- 基于nginx+lua简单的灰度发布系统
upstream.conf upstream grey_1 { keepalive 1000; server localhost:8020; } upstream grey_2 { keepalive ...
- 单机闭环 使用Nginx+Lua开发高性能Web应用
[西域骆驼D1532101213]西域骆驼(VANCAMEL)D1532101213 休闲套脚鞋 卡其43[行情 报价 价格 评测]-京东 http://item.jd.com/1856564.htm ...
- 使用NGINX+LUA实现WAF功能 和nginx 防盗链
使用NGINX+LUA实现WAF功能 一.了解WAF 1.1 什么是WAF Web应用防护系统(也称:网站应用级入侵防御系统 .英文:Web Application Firewall,简称: WAF) ...
随机推荐
- STP-19-Port-Channel发现和配置
工程师在给一台交换机上的特定Port-Channel增加多个端口时,有一些配置参数必须相同,如下所示: 使用相同的速率和双工设置: 使用相同的操作模式(Trunk.Access.动态): 若不为T ...
- vue中v-bind绑定样式
近来发现v-bind绑定样式的两个好玩的栗子 可以直接绑定到一个样式对象,让模板更清晰: <div id="app"> <div v-bind:style=&qu ...
- Javascript专题(三)b.各种轮播和细节分析--上下滚动轮播
这一次,我们用原生JS实现上下滚动方式的轮播.顺带学习一下用JS来创建HTML元素. 上一次写的轮播是淡入淡出效果的,相对来说其实是比较简单的. github源码: 上下轮播源码-github A. ...
- JD孔_20160912
1.买的 “航嘉(Huntkey)大白803 8位3米 总控开关 防过载保护 插座/排插/拖” http://item.jd.com/1786149.html#product-detail 2.
- ACdream 1216——Beautiful People——————【二维LIS,nlogn处理】
Beautiful People Special Judge Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (J ...
- 使用PM2守护Node.js应用
PM2简介 PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控.自动重启.负载均衡等,而且使用非常简单. 安装PM2 $ npm install pm2 -g ...
- Java实例学习——企业进销存管理系统(1)
Java实例学习——企业进销存管理系统(1) (本实例为书上实例,我所记录的是我的学习过程) 开始时间:2月12日 完成时间:暂未完成 2月12日—选择企业进销存管理系统 选择企业进销存管理系统这一实 ...
- linux安装jdk7步骤
linux安装jdk7步骤: 1.首先使用命令查看linux系统版本号: lsb_release -a 2.下载对应的jdk版本,笔者使用的是jdk-7u79-linux-x64.tar.gz: 3. ...
- VMware Workstation Pro 14注册码,亲测可用
** VMware Workstation Pro 14注册码 ** 作者网上搜集整理 作者使用的密钥是: AC5XK-0ZD4H-088HP-9NQZV-ZG2R4 亲测可用 以下密钥未测试 CG5 ...
- JS 获取 今日、昨日、本周、本月、本季度、本年、上月、上周、上季度、去年
/** * 日期范围工具类 */ var dateRangeUtil = (function () { /*** * 获得当前时间 */ this.getCurrentDate = function ...