OpenResty(四)lua脚本实现限流

需求

如何控制外部客户端访问API接口的频率,利用lua脚本实现接口限流

创建脚本目录及脚本功能设计

mkdir /usr/local/openresty/nginx/html/lua/waf


waf/

├── access.lua #统一入口脚本

├── config.lua #配置开关

├── init.lua #初始化函数

└── lib.lua #依赖函数

脚本详细

access.lua(统一入口脚本)

require "init"


local function waf_main()

if cc_attack_check() then

else

return

end

end


-- main

waf_main()

config.lua(开关配置)

-- on 开启 off 关闭

-- Define waf switch

config_waf_enable = "on"

-- Define cc switch

config_cc_check = "on"

-- Define cc rate(CCcount/CCseconds)

config_cc_rate = "3/30"

init.lua(初始化)

require 'lib'

require 'config'

--[[

使用了ngx.var.uri,ngx.shared.limit

]]


function cc_attack_check()

if config_cc_check == 'on' then

-- 根据uri + ip生成token来检验

local ATTACK_URI = ngx.var.uri

local CC_TOKEN = get_client_ip()..ATTACK_URI

local limit = ngx.shared.limit

local CCcount=tonumber(string.match(config_cc_rate,'(.*)/'))

local CCseconds=tonumber(string.match(config_cc_rate,'/(.*)'))

local req,_ = limit:get(CC_TOKEN)

-- 超过指定的次数,返回403,否则次数加1

if req then

if req >= CCcount then

log_record('CC_Acttack',ngx.var.request_uri,"-","-")

if config_waf_enable == 'on' then

ngx.exit(403)

end

else

limit:incr(CC_TOKEN, 1)

end

else

limit:set(CC_TOKEN, 1, CCseconds)

end

-- ngx.shared.limit存储token

end

return

end


lib.lua(依赖工具函数)

function get_client_ip()

local headers = ngx.req.get_headers()

local CLIENT_IP = headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr

if CLIENT_IP == nil then

CLIENT_IP = "unknown"

end

return CLIENT_IP

end



function get_user_agent()

local USER_AGENT = ngx.var.http_user_agent

if USER_AGENT == nil then

USER_AGENT = 'unknown'

end

return USER_AGENT

end


function log_record(method, url, data, ruletag)

local cjson = require("cjson")

local io = require 'io'

local LOG_PATH = "/data/logs/"

local CLIENT_IP = get_client_ip()

local USER_AGENT = get_user_agent()

local SERVER_NAME = ngx.var.server_name

local LOCAL_TIME = ngx.localtime()

local log_json_obj = {

client_ip = CLIENT_IP,

local_time = LOCAL_TIME,

server_name = SERVER_NAME,

user_agent = USER_AGENT,

attack_method = method,

req_url = url,

req_data = data,

rule_tag = ruletag,

}

local LOG_LINE = cjson.encode(log_json_obj)

local LOG_NAME = LOG_PATH..'/'..ngx.today().."_waf.log"

local file, err = io.open(LOG_NAME,"aw+")

if file == nil then

return


else

file:write(LOG_LINE.." ")

file:flush()

file:close()

end

end


配置nginx.conf

#user www;

worker_processes 1;

events {

worker_connections 1024;

}


http {

include mime.types;

default_type application/octet-stream;


sendfile on;

keepalive_timeout 65;


lua_shared_dict limit 50m;

lua_package_path "/usr/local/openresty/nginx/html/lua/waf/?.lua;;";

init_by_lua_file "/usr/local/openresty/nginx/html/lua/waf/init.lua";

access_by_lua_file "/usr/local/openresty/nginx/html/lua/waf/access.lua";


server {

listen 80;

location / {

default_type 'text/plain';

content_by_lua_file html/lua/hello.lua;

}

location /log {

default_type 'text/plain';

content_by_lua_file html/lua/get_log.lua;

}

}

}


重启测试

/usr/local/openresty/bin/openresty -s reload

for i in `seq 1 10` ; do curl -I 127.0.0.1/log 2>/dev/null | awk '/^HTTP/{print $2}' ; done

200

200

200

403

403

403

403

403

403

403

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章