Merge 240a5a12c1
into 314a2f62ec
commit
8d4aaef8f3
79
README.md
79
README.md
|
@ -1,3 +1,34 @@
|
||||||
|
水平太烂,只能在原基础上改改。
|
||||||
|
|
||||||
|
1.原cc功能有点弱,改进后可以提高对攻击和普通访问的限制
|
||||||
|
|
||||||
|
默认同ip触发规则攻击超过10次/5秒,限制该ip访问nginx服务器1800秒。
|
||||||
|
|
||||||
|
同1 ip访问同1地址30次/60秒,限制访问该地址30秒。
|
||||||
|
|
||||||
|
2.日志主要是用于syslog服务器能力。
|
||||||
|
|
||||||
|
1.--保存日志到syslog,可以用nginx设置的日志服务器保存日志。
|
||||||
|
|
||||||
|
logtoserver = "on"
|
||||||
|
|
||||||
|
2.--通过加载socket.lua同时使用其他日志服务器,默认关闭。(这个有点粗糙,有需要的自己改)
|
||||||
|
|
||||||
|
loghack="off"
|
||||||
|
|
||||||
|
3.修改whiteurl,可以针对域名设置白名单。
|
||||||
|
|
||||||
|
--whiteurl start
|
||||||
|
|
||||||
|
site:^www.baidu.com/whiteurl/
|
||||||
|
|
||||||
|
--end
|
||||||
|
|
||||||
|
|
||||||
|
------------------分割线-------------------------------------------------
|
||||||
|
|
||||||
|
---------------lovshell记录----------------------------------------------
|
||||||
|
|
||||||
##ngx_lua_waf
|
##ngx_lua_waf
|
||||||
|
|
||||||
ngx_lua_waf是我刚入职趣游时候开发的一个基于ngx_lua的web应用防火墙。
|
ngx_lua_waf是我刚入职趣游时候开发的一个基于ngx_lua的web应用防火墙。
|
||||||
|
@ -136,3 +167,51 @@ nginx安装路径假设为:/usr/local/nginx/conf/
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
感谢ngx_lua模块的开发者[@agentzh](https://github.com/agentzh/),春哥是我所接触过开源精神最好的人
|
感谢ngx_lua模块的开发者[@agentzh](https://github.com/agentzh/),春哥是我所接触过开源精神最好的人
|
||||||
|
|
||||||
|
|
||||||
|
syntax: captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)
|
||||||
|
syntax: from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)
|
||||||
|
|
||||||
|
Specify options to control how the match operation will be performed. The following option characters are supported:
|
||||||
|
|
||||||
|
a anchored mode (only match from the beginning)
|
||||||
|
|
||||||
|
d enable the DFA mode (or the longest token match semantics).
|
||||||
|
this requires PCRE 6.0+ or else a Lua exception will be thrown.
|
||||||
|
first introduced in ngx_lua v0.3.1rc30.
|
||||||
|
|
||||||
|
D enable duplicate named pattern support. This allows named
|
||||||
|
subpattern names to be repeated, returning the captures in
|
||||||
|
an array-like Lua table. for example,
|
||||||
|
local m = ngx.re.match("hello, world",
|
||||||
|
"(?<named>\w+), (?<named>\w+)",
|
||||||
|
"D")
|
||||||
|
-- m["named"] == {"hello", "world"}
|
||||||
|
this option was first introduced in the v0.7.14 release.
|
||||||
|
this option requires at least PCRE 8.12.
|
||||||
|
|
||||||
|
i case insensitive mode (similar to Perl's /i modifier)
|
||||||
|
|
||||||
|
j enable PCRE JIT compilation, this requires PCRE 8.21+ which
|
||||||
|
must be built with the --enable-jit option. for optimum performance,
|
||||||
|
this option should always be used together with the 'o' option.
|
||||||
|
first introduced in ngx_lua v0.3.1rc30.
|
||||||
|
|
||||||
|
J enable the PCRE Javascript compatible mode. this option was
|
||||||
|
first introduced in the v0.7.14 release. this option requires
|
||||||
|
at least PCRE 8.12.
|
||||||
|
|
||||||
|
m multi-line mode (similar to Perl's /m modifier)
|
||||||
|
|
||||||
|
o compile-once mode (similar to Perl's /o modifier),
|
||||||
|
to enable the worker-process-level compiled-regex cache
|
||||||
|
|
||||||
|
s single-line mode (similar to Perl's /s modifier)
|
||||||
|
|
||||||
|
u UTF-8 mode. this requires PCRE to be built with
|
||||||
|
the --enable-utf8 option or else a Lua exception will be thrown.
|
||||||
|
|
||||||
|
U similar to "u" but disables PCRE's UTF-8 validity check on
|
||||||
|
the subject string. first introduced in ngx_lua v0.8.1.
|
||||||
|
|
||||||
|
x extended mode (similar to Perl's /x modifier)
|
||||||
|
|
24
config.lua
24
config.lua
|
@ -1,23 +1,35 @@
|
||||||
RulePath = "/usr/local/nginx/conf/waf/wafconf/"
|
RulePath = "/etc/nginx/waf/wafconf/"
|
||||||
attacklog = "on"
|
attacklog = "on"
|
||||||
logdir = "/usr/local/nginx/logs/hack/"
|
--保存日志到文件
|
||||||
|
logtofile = "off"
|
||||||
|
logdir = "/var/log/nginx/"
|
||||||
|
--保存日志到syslog,采用nginx设置
|
||||||
|
logtoserver = "on"
|
||||||
|
--通过syslog日志方式提交hack_ip记录到日志服务器
|
||||||
|
loghack="off"
|
||||||
|
------------
|
||||||
UrlDeny="on"
|
UrlDeny="on"
|
||||||
Redirect="on"
|
Redirect="on"
|
||||||
CookieMatch="on"
|
CookieMatch="on"
|
||||||
postMatch="on"
|
postMatch="on"
|
||||||
whiteModule="on"
|
whiteModule="on"
|
||||||
black_fileExt={"php","jsp"}
|
black_fileExt={"php","jsp"}
|
||||||
ipWhitelist={"127.0.0.1"}
|
ipWhitelist={"127.0.0.1","192.168.2.1"}
|
||||||
ipBlocklist={"1.0.0.1"}
|
ipBlocklist={"1.0.0.1"}
|
||||||
CCDeny="off"
|
--违规ip登记,是否限制访问。
|
||||||
CCrate="100/60"
|
--hackrate超过10次/5秒,限制访问1800秒。
|
||||||
|
hackipdeny="on"
|
||||||
|
hackrate="10/60/1800"
|
||||||
|
--cc攻击防范
|
||||||
|
CCDeny="on"
|
||||||
|
CCrate="30/60/30"
|
||||||
html=[[
|
html=[[
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<title>网站防火墙</title>
|
<title>网站防火墙</title>
|
||||||
<style>
|
<style>
|
||||||
p {
|
p {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
}
|
}
|
||||||
ul{ list-style-type:none;}
|
ul{ list-style-type:none;}
|
||||||
li{ list-style-type:none;}
|
li{ list-style-type:none;}
|
||||||
|
|
143
init.lua
143
init.lua
|
@ -1,19 +1,52 @@
|
||||||
require 'config'
|
require 'config'
|
||||||
local match = string.match
|
local match = string.match
|
||||||
local ngxmatch=ngx.re.match
|
--ngx_lua如果是0.9.2以上版本,建议正则过滤函数改为ngx.re.find,匹配效率会提高三倍左右。
|
||||||
|
--因nginx和lua一起的关系,正则表达式使用\d\w\s会出问题,
|
||||||
|
--local ngxmatch=ngx.re.match
|
||||||
|
local ngxmatch=ngx.re.find
|
||||||
local unescape=ngx.unescape_uri
|
local unescape=ngx.unescape_uri
|
||||||
local get_headers = ngx.req.get_headers
|
local get_headers = ngx.req.get_headers
|
||||||
local optionIsOn = function (options) return options == "on" and true or false end
|
local optionIsOn = function (options) return options == "on" and true or false end
|
||||||
|
loghack=optionIsOn(loghack)
|
||||||
|
--载入socket.lua用于发送log到独立syslog服务器。
|
||||||
|
local logger = require "socket"
|
||||||
|
if loghack then
|
||||||
|
|
||||||
|
|
||||||
|
if not logger.initted() then
|
||||||
|
local ok, err = logger.init{
|
||||||
|
--host = '192.168.0.1',
|
||||||
|
host = 'logserver.local',
|
||||||
|
port = 514,
|
||||||
|
sock_type = "udp", --udp协议
|
||||||
|
flush_limit = 1, --立即发送
|
||||||
|
--drop_limit = 5678,
|
||||||
|
pool_size = 100,--连接池大小
|
||||||
|
}
|
||||||
|
if not ok then
|
||||||
|
ngx.log(ngx.ERR, "failed to initialize the logger: ",
|
||||||
|
err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
logpath = logdir
|
logpath = logdir
|
||||||
rulepath = RulePath
|
rulepath = RulePath
|
||||||
|
logtofile = optionIsOn(logtofile)
|
||||||
|
logtoserver = optionIsOn(logtoserver)
|
||||||
UrlDeny = optionIsOn(UrlDeny)
|
UrlDeny = optionIsOn(UrlDeny)
|
||||||
PostCheck = optionIsOn(postMatch)
|
PostCheck = optionIsOn(postMatch)
|
||||||
CookieCheck = optionIsOn(cookieMatch)
|
CookieCheck = optionIsOn(cookieMatch)
|
||||||
WhiteCheck = optionIsOn(whiteModule)
|
WhiteCheck = optionIsOn(whiteModule)
|
||||||
PathInfoFix = optionIsOn(PathInfoFix)
|
PathInfoFix = optionIsOn(PathInfoFix)
|
||||||
attacklog = optionIsOn(attacklog)
|
attacklog = optionIsOn(attacklog)
|
||||||
|
hackipdeny = optionIsOn(hackipdeny)
|
||||||
CCDeny = optionIsOn(CCDeny)
|
CCDeny = optionIsOn(CCDeny)
|
||||||
Redirect=optionIsOn(Redirect)
|
Redirect=optionIsOn(Redirect)
|
||||||
|
local file = io.open('config')
|
||||||
|
|
||||||
function getClientIp()
|
function getClientIp()
|
||||||
IP = ngx.var.remote_addr
|
IP = ngx.var.remote_addr
|
||||||
if IP == nil then
|
if IP == nil then
|
||||||
|
@ -28,19 +61,38 @@ function write(logfile,msg)
|
||||||
fd:flush()
|
fd:flush()
|
||||||
fd:close()
|
fd:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function swrite(msg)
|
||||||
|
--保存警告等级要高于nginx error_log的默认等级。
|
||||||
|
ngx.log(ngx.CRIT,msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
function log(method,url,data,ruletag)
|
function log(method,url,data,ruletag)
|
||||||
if attacklog then
|
if attacklog then
|
||||||
local realIp = getClientIp()
|
local realIp = getClientIp()
|
||||||
local ua = ngx.var.http_user_agent
|
local ua = ngx.var.http_user_agent
|
||||||
local servername=ngx.var.server_name
|
if ua == nil then
|
||||||
local time=ngx.localtime()
|
ua="null"
|
||||||
if ua then
|
|
||||||
line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" \""..ua.."\" \""..ruletag.."\"\n"
|
|
||||||
else
|
|
||||||
line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" - \""..ruletag.."\"\n"
|
|
||||||
end
|
end
|
||||||
|
local servername=ngx.var.host
|
||||||
|
local time=ngx.localtime()
|
||||||
|
if logtofile then
|
||||||
local filename = logpath..'/'..servername.."_"..ngx.today().."_sec.log"
|
local filename = logpath..'/'..servername.."_"..ngx.today().."_sec.log"
|
||||||
|
line=realIp.." ["..time.."]".."\""..method.." "..servername..url.."\""..data.."\""..ua.."\""..ruletag.."\"".."\n"
|
||||||
write(filename,line)
|
write(filename,line)
|
||||||
|
end
|
||||||
|
if logtoserver then
|
||||||
|
line=realIp.."\""..method.." "..servername..url.."\""..data.."\""..ua.."\""..ruletag.."\""
|
||||||
|
--line="lua_waf:"..line
|
||||||
|
swrite(line)
|
||||||
|
end
|
||||||
|
--发送ip到独立syslog服务器。
|
||||||
|
if loghack then local bytes, err = logger.log(getClientIp()) end
|
||||||
|
--只要log记录,说明被攻击,利用denyhackip将ip记录。
|
||||||
|
if hackipdeny then denyhackip(0) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
------------------------------------规则读取函数-------------------------------------------------------------------
|
------------------------------------规则读取函数-------------------------------------------------------------------
|
||||||
|
@ -78,21 +130,35 @@ function whiteurl()
|
||||||
if WhiteCheck then
|
if WhiteCheck then
|
||||||
if wturlrules ~=nil then
|
if wturlrules ~=nil then
|
||||||
for _,rule in pairs(wturlrules) do
|
for _,rule in pairs(wturlrules) do
|
||||||
if ngxmatch(ngx.var.uri,rule,"isjo") then
|
--针对site:开始的进行域名匹配。增加白名单用处。
|
||||||
|
local sitemod,_=string.find(rule,"site:")
|
||||||
|
if sitemod==1 then
|
||||||
|
rule=string.gsub(rule,"site:","",1)
|
||||||
|
--调试whiteurl
|
||||||
|
--if ngx.var.host=='domino.cqhrss.gov.cn' then
|
||||||
|
-- log('debug',ngx.var.uri,"",rule)
|
||||||
|
--end
|
||||||
|
if ngxmatch(ngx.var.host..ngx.var.uri,rule,"isjo") then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if ngxmatch(ngx.var.uri,rule,"isjo") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function fileExtCheck(ext)
|
function fileExtCheck(ext)
|
||||||
local items = Set(black_fileExt)
|
local items = Set(black_fileExt)
|
||||||
ext=string.lower(ext)
|
ext=string.lower(ext)
|
||||||
if ext then
|
if ext then
|
||||||
for rule in pairs(items) do
|
for rule,_ in pairs(items) do
|
||||||
if ngx.re.match(ext,rule,"isjo") then
|
if ngxmatch(ext,rule,"isjo") then
|
||||||
log('POST',ngx.var.request_uri,"-","file attack with ext "..ext)
|
log('POST',ngx.var.request_uri,"-","file attack with ext "..ext)
|
||||||
say_html()
|
say_html()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -184,17 +250,25 @@ end
|
||||||
function denycc()
|
function denycc()
|
||||||
if CCDeny then
|
if CCDeny then
|
||||||
local uri=ngx.var.uri
|
local uri=ngx.var.uri
|
||||||
CCcount=tonumber(string.match(CCrate,'(.*)/'))
|
local m, err = ngx.re.match(CCrate,'([0-9]+)/([0-9]+)/([0-9]+)')
|
||||||
CCseconds=tonumber(string.match(CCrate,'/(.*)'))
|
local CCcount=tonumber(m[1]) --计数器上限
|
||||||
|
local CCseconds=tonumber(m[2]) --计时器
|
||||||
|
local CClimits=tonumber(m[3]) --阻止访问时间
|
||||||
local token = getClientIp()..uri
|
local token = getClientIp()..uri
|
||||||
local limit = ngx.shared.limit
|
local limit = ngx.shared.limit
|
||||||
local req,_=limit:get(token)
|
local req,_=limit:get(token) --计数器当前值
|
||||||
|
|
||||||
if req then
|
if req then
|
||||||
if req > CCcount then
|
if req > CCcount then
|
||||||
ngx.exit(503)
|
ngx.exit(404)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
limit:incr(token,1)
|
if req == CCcount then limit:set(token,CCcount+1,CClimits) end
|
||||||
|
|
||||||
|
limit:incr(token,1)
|
||||||
|
--调试在syslog日志中查看
|
||||||
|
--swrite('计数器:'..token..'当前计数器'..req..'阻止访问时间:'..CClimits)
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
limit:set(token,1,CCseconds)
|
limit:set(token,1,CCseconds)
|
||||||
|
@ -203,6 +277,41 @@ function denycc()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--chk为1表示检测值,不增加,不创建,返回检测结果。
|
||||||
|
function denyhackip(chk)
|
||||||
|
if hackipdeny then
|
||||||
|
|
||||||
|
local m, err = ngx.re.match(hackrate,'([0-9]+)/([0-9]+)/([0-9]+)')
|
||||||
|
local hicount=tonumber(m[1]) --计数器上限
|
||||||
|
local hiseconds=tonumber(m[2]) --计时器
|
||||||
|
local hilimits=tonumber(m[3]) --阻止访问时间
|
||||||
|
local token = "hackip"..getClientIp()
|
||||||
|
local limit = ngx.shared.limit
|
||||||
|
local req,_=limit:get(token) --计数器当前值
|
||||||
|
if req then
|
||||||
|
if req > hicount then
|
||||||
|
ngx.exit(404)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
|
||||||
|
if req == hicount then
|
||||||
|
limit:set(token,hicount+1,hilimits)
|
||||||
|
swrite("ip:"..getClientIp().."因攻击被暂停访问"..hilimits.."秒。")
|
||||||
|
end
|
||||||
|
if chk ~=1 then limit:incr(token,1) end
|
||||||
|
--调试在syslog日志中查看
|
||||||
|
--swrite("计数器:"..token.."检测状态:"..chk.."当前计数器"..req.."阻止访问时间:"..hilimits)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if chk ~=1 then limit:set(token,1,hiseconds) end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
function get_boundary()
|
function get_boundary()
|
||||||
local header = get_headers()["content-type"]
|
local header = get_headers()["content-type"]
|
||||||
if not header then
|
if not header then
|
||||||
|
|
|
@ -0,0 +1,558 @@
|
||||||
|
-- Copyright (C) 2013-2014 Jiale Zhi (calio), CloudFlare Inc.
|
||||||
|
--require "luacov"
|
||||||
|
|
||||||
|
local concat = table.concat
|
||||||
|
local tcp = ngx.socket.tcp
|
||||||
|
local udp = ngx.socket.udp
|
||||||
|
local timer_at = ngx.timer.at
|
||||||
|
local ngx_log = ngx.log
|
||||||
|
local ngx_sleep = ngx.sleep
|
||||||
|
local type = type
|
||||||
|
local pairs = pairs
|
||||||
|
local tostring = tostring
|
||||||
|
local debug = ngx.config.debug
|
||||||
|
|
||||||
|
local DEBUG = ngx.DEBUG
|
||||||
|
local CRIT = ngx.CRIT
|
||||||
|
|
||||||
|
local MAX_PORT = 65535
|
||||||
|
|
||||||
|
|
||||||
|
-- table.new(narr, nrec)
|
||||||
|
local succ, new_tab = pcall(require, "table.new")
|
||||||
|
if not succ then
|
||||||
|
new_tab = function () return {} end
|
||||||
|
end
|
||||||
|
|
||||||
|
local _M = new_tab(0, 5)
|
||||||
|
|
||||||
|
local is_exiting
|
||||||
|
|
||||||
|
if not ngx.config or not ngx.config.ngx_lua_version
|
||||||
|
or ngx.config.ngx_lua_version < 9003 then
|
||||||
|
|
||||||
|
is_exiting = function() return false end
|
||||||
|
|
||||||
|
ngx_log(CRIT, "We strongly recommend you to update your ngx_lua module to "
|
||||||
|
.. "0.9.3 or above. lua-resty-logger-socket will lose some log "
|
||||||
|
.. "messages when Nginx reloads if it works with ngx_lua module "
|
||||||
|
.. "below 0.9.3")
|
||||||
|
else
|
||||||
|
is_exiting = ngx.worker.exiting
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
_M._VERSION = '0.03'
|
||||||
|
|
||||||
|
-- user config
|
||||||
|
local flush_limit = 4096 -- 4KB
|
||||||
|
local drop_limit = 1048576 -- 1MB
|
||||||
|
local timeout = 1000 -- 1 sec
|
||||||
|
local host
|
||||||
|
local port
|
||||||
|
local ssl = false
|
||||||
|
local ssl_verify = true
|
||||||
|
local sni_host
|
||||||
|
local path
|
||||||
|
local max_buffer_reuse = 10000 -- reuse buffer for at most 10000
|
||||||
|
-- times
|
||||||
|
local periodic_flush = nil
|
||||||
|
local need_periodic_flush = nil
|
||||||
|
local sock_type = 'tcp'
|
||||||
|
|
||||||
|
-- internal variables
|
||||||
|
local buffer_size = 0
|
||||||
|
-- 2nd level buffer, it stores logs ready to be sent out
|
||||||
|
local send_buffer = ""
|
||||||
|
-- 1st level buffer, it stores incoming logs
|
||||||
|
local log_buffer_data = new_tab(20000, 0)
|
||||||
|
-- number of log lines in current 1st level buffer, starts from 0
|
||||||
|
local log_buffer_index = 0
|
||||||
|
|
||||||
|
local last_error
|
||||||
|
|
||||||
|
local connecting
|
||||||
|
local connected
|
||||||
|
local exiting
|
||||||
|
local retry_connect = 0
|
||||||
|
local retry_send = 0
|
||||||
|
local max_retry_times = 3
|
||||||
|
local retry_interval = 100 -- 0.1s
|
||||||
|
local pool_size = 10
|
||||||
|
local flushing
|
||||||
|
local logger_initted
|
||||||
|
local counter = 0
|
||||||
|
local ssl_session
|
||||||
|
|
||||||
|
local function _write_error(msg)
|
||||||
|
last_error = msg
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _do_connect()
|
||||||
|
local ok, err, sock
|
||||||
|
|
||||||
|
if not connected then
|
||||||
|
if (sock_type == 'udp') then
|
||||||
|
sock, err = udp()
|
||||||
|
else
|
||||||
|
sock, err = tcp()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not sock then
|
||||||
|
_write_error(err)
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
sock:settimeout(timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- "host"/"port" and "path" have already been checked in init()
|
||||||
|
if host and port then
|
||||||
|
if (sock_type == 'udp') then
|
||||||
|
ok, err = sock:setpeername(host, port)
|
||||||
|
else
|
||||||
|
ok, err = sock:connect(host, port)
|
||||||
|
end
|
||||||
|
elseif path then
|
||||||
|
ok, err = sock:connect("unix:" .. path)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _do_handshake(sock)
|
||||||
|
if not ssl then
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
local session, err = sock:sslhandshake(ssl_session, sni_host or host,
|
||||||
|
ssl_verify)
|
||||||
|
if not session then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
ssl_session = session
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _connect()
|
||||||
|
local err, sock
|
||||||
|
|
||||||
|
if connecting then
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "previous connection not finished")
|
||||||
|
end
|
||||||
|
return nil, "previous connection not finished"
|
||||||
|
end
|
||||||
|
|
||||||
|
connected = false
|
||||||
|
connecting = true
|
||||||
|
|
||||||
|
retry_connect = 0
|
||||||
|
|
||||||
|
while retry_connect <= max_retry_times do
|
||||||
|
sock, err = _do_connect()
|
||||||
|
|
||||||
|
if sock then
|
||||||
|
sock, err = _do_handshake(sock)
|
||||||
|
if sock then
|
||||||
|
connected = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "reconnect to the log server: ", err)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ngx.sleep time is in seconds
|
||||||
|
if not exiting then
|
||||||
|
ngx_sleep(retry_interval / 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
retry_connect = retry_connect + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
connecting = false
|
||||||
|
if not connected then
|
||||||
|
return nil, "try to connect to the log server failed after "
|
||||||
|
.. max_retry_times .. " retries: " .. err
|
||||||
|
end
|
||||||
|
|
||||||
|
return sock
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _prepare_stream_buffer()
|
||||||
|
local packet = concat(log_buffer_data, "", 1, log_buffer_index)
|
||||||
|
send_buffer = send_buffer .. packet
|
||||||
|
|
||||||
|
log_buffer_index = 0
|
||||||
|
counter = counter + 1
|
||||||
|
if counter > max_buffer_reuse then
|
||||||
|
log_buffer_data = new_tab(20000, 0)
|
||||||
|
counter = 0
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "log buffer reuse limit (" .. max_buffer_reuse
|
||||||
|
.. ") reached, create a new \"log_buffer_data\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _do_flush()
|
||||||
|
local ok, err, sock, bytes
|
||||||
|
local packet = send_buffer
|
||||||
|
|
||||||
|
sock, err = _connect()
|
||||||
|
if not sock then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
bytes, err = sock:send(packet)
|
||||||
|
if not bytes then
|
||||||
|
-- "sock:send" always closes current connection on error
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
if debug then
|
||||||
|
ngx.update_time()
|
||||||
|
ngx_log(DEBUG, ngx.now(), ":log flush:" .. bytes .. ":" .. packet)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (sock_type ~= 'udp') then
|
||||||
|
ok, err = sock:setkeepalive(0, pool_size)
|
||||||
|
if not ok then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _need_flush()
|
||||||
|
if buffer_size > 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _flush_lock()
|
||||||
|
if not flushing then
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "flush lock acquired")
|
||||||
|
end
|
||||||
|
flushing = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _flush_unlock()
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "flush lock released")
|
||||||
|
end
|
||||||
|
flushing = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _flush()
|
||||||
|
local err
|
||||||
|
|
||||||
|
-- pre check
|
||||||
|
if not _flush_lock() then
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "previous flush not finished")
|
||||||
|
end
|
||||||
|
-- do this later
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _need_flush() then
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "no need to flush:", log_buffer_index)
|
||||||
|
end
|
||||||
|
_flush_unlock()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start flushing
|
||||||
|
retry_send = 0
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "start flushing")
|
||||||
|
end
|
||||||
|
|
||||||
|
local bytes
|
||||||
|
while retry_send <= max_retry_times do
|
||||||
|
if log_buffer_index > 0 then
|
||||||
|
_prepare_stream_buffer()
|
||||||
|
end
|
||||||
|
|
||||||
|
bytes, err = _do_flush()
|
||||||
|
|
||||||
|
if bytes then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "resend log messages to the log server: ", err)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ngx.sleep time is in seconds
|
||||||
|
if not exiting then
|
||||||
|
ngx_sleep(retry_interval / 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
retry_send = retry_send + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
_flush_unlock()
|
||||||
|
|
||||||
|
if not bytes then
|
||||||
|
local err_msg = "try to send log messages to the log server "
|
||||||
|
.. "failed after " .. max_retry_times .. " retries: "
|
||||||
|
.. err
|
||||||
|
_write_error(err_msg)
|
||||||
|
return nil, err_msg
|
||||||
|
else
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "send " .. bytes .. " bytes")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer_size = buffer_size - #send_buffer
|
||||||
|
send_buffer = ""
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _periodic_flush(premature)
|
||||||
|
if premature then
|
||||||
|
exiting = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if need_periodic_flush or exiting then
|
||||||
|
-- no regular flush happened after periodic flush timer had been set
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "performing periodic flush")
|
||||||
|
end
|
||||||
|
_flush()
|
||||||
|
else
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "no need to perform periodic flush: regular flush "
|
||||||
|
.. "happened before")
|
||||||
|
end
|
||||||
|
need_periodic_flush = true
|
||||||
|
end
|
||||||
|
|
||||||
|
timer_at(periodic_flush, _periodic_flush)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _flush_buffer()
|
||||||
|
local ok, err = timer_at(0, _flush)
|
||||||
|
|
||||||
|
need_periodic_flush = false
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
_write_error(err)
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _write_buffer(msg, len)
|
||||||
|
log_buffer_index = log_buffer_index + 1
|
||||||
|
log_buffer_data[log_buffer_index] = msg
|
||||||
|
|
||||||
|
buffer_size = buffer_size + len
|
||||||
|
|
||||||
|
|
||||||
|
return buffer_size
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.init(user_config)
|
||||||
|
if (type(user_config) ~= "table") then
|
||||||
|
return nil, "user_config must be a table"
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(user_config) do
|
||||||
|
if k == "host" then
|
||||||
|
if type(v) ~= "string" then
|
||||||
|
return nil, '"host" must be a string'
|
||||||
|
end
|
||||||
|
host = v
|
||||||
|
elseif k == "port" then
|
||||||
|
if type(v) ~= "number" then
|
||||||
|
return nil, '"port" must be a number'
|
||||||
|
end
|
||||||
|
if v < 0 or v > MAX_PORT then
|
||||||
|
return nil, ('"port" out of range 0~%s'):format(MAX_PORT)
|
||||||
|
end
|
||||||
|
port = v
|
||||||
|
elseif k == "path" then
|
||||||
|
if type(v) ~= "string" then
|
||||||
|
return nil, '"path" must be a string'
|
||||||
|
end
|
||||||
|
path = v
|
||||||
|
elseif k == "sock_type" then
|
||||||
|
if type(v) ~= "string" then
|
||||||
|
return nil, '"sock_type" must be a string'
|
||||||
|
end
|
||||||
|
if v ~= "tcp" and v ~= "udp" then
|
||||||
|
return nil, '"sock_type" must be "tcp" or "udp"'
|
||||||
|
end
|
||||||
|
sock_type = v
|
||||||
|
elseif k == "flush_limit" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "flush_limit"'
|
||||||
|
end
|
||||||
|
flush_limit = v
|
||||||
|
elseif k == "drop_limit" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "drop_limit"'
|
||||||
|
end
|
||||||
|
drop_limit = v
|
||||||
|
elseif k == "timeout" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "timeout"'
|
||||||
|
end
|
||||||
|
timeout = v
|
||||||
|
elseif k == "max_retry_times" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "max_retry_times"'
|
||||||
|
end
|
||||||
|
max_retry_times = v
|
||||||
|
elseif k == "retry_interval" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "retry_interval"'
|
||||||
|
end
|
||||||
|
-- ngx.sleep time is in seconds
|
||||||
|
retry_interval = v
|
||||||
|
elseif k == "pool_size" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "pool_size"'
|
||||||
|
end
|
||||||
|
pool_size = v
|
||||||
|
elseif k == "max_buffer_reuse" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "max_buffer_reuse"'
|
||||||
|
end
|
||||||
|
max_buffer_reuse = v
|
||||||
|
elseif k == "periodic_flush" then
|
||||||
|
if type(v) ~= "number" or v < 0 then
|
||||||
|
return nil, 'invalid "periodic_flush"'
|
||||||
|
end
|
||||||
|
periodic_flush = v
|
||||||
|
elseif k == "ssl" then
|
||||||
|
if type(v) ~= "boolean" then
|
||||||
|
return nil, '"ssl" must be a boolean value'
|
||||||
|
end
|
||||||
|
ssl = v
|
||||||
|
elseif k == "ssl_verify" then
|
||||||
|
if type(v) ~= "boolean" then
|
||||||
|
return nil, '"ssl_verify" must be a boolean value'
|
||||||
|
end
|
||||||
|
ssl_verify = v
|
||||||
|
elseif k == "sni_host" then
|
||||||
|
if type(v) ~= "string" then
|
||||||
|
return nil, '"sni_host" must be a string'
|
||||||
|
end
|
||||||
|
sni_host = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not (host and port) and not path then
|
||||||
|
return nil, "no logging server configured. \"host\"/\"port\" or "
|
||||||
|
.. "\"path\" is required."
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if (flush_limit >= drop_limit) then
|
||||||
|
return nil, "\"flush_limit\" should be < \"drop_limit\""
|
||||||
|
end
|
||||||
|
|
||||||
|
flushing = false
|
||||||
|
exiting = false
|
||||||
|
connecting = false
|
||||||
|
|
||||||
|
connected = false
|
||||||
|
retry_connect = 0
|
||||||
|
retry_send = 0
|
||||||
|
|
||||||
|
logger_initted = true
|
||||||
|
|
||||||
|
if periodic_flush then
|
||||||
|
if debug then
|
||||||
|
ngx_log(DEBUG, "periodic flush enabled for every "
|
||||||
|
.. periodic_flush .. " seconds")
|
||||||
|
end
|
||||||
|
need_periodic_flush = true
|
||||||
|
timer_at(periodic_flush, _periodic_flush)
|
||||||
|
end
|
||||||
|
|
||||||
|
return logger_initted
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.log(msg)
|
||||||
|
if not logger_initted then
|
||||||
|
return nil, "not initialized"
|
||||||
|
end
|
||||||
|
|
||||||
|
local bytes
|
||||||
|
|
||||||
|
if type(msg) ~= "string" then
|
||||||
|
msg = tostring(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local msg_len = #msg
|
||||||
|
|
||||||
|
if (debug) then
|
||||||
|
ngx.update_time()
|
||||||
|
ngx_log(DEBUG, ngx.now(), ":log message length: " .. msg_len)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- response of "_flush_buffer" is not checked, because it writes
|
||||||
|
-- error buffer
|
||||||
|
if (is_exiting()) then
|
||||||
|
exiting = true
|
||||||
|
_write_buffer(msg, msg_len)
|
||||||
|
_flush_buffer()
|
||||||
|
if (debug) then
|
||||||
|
ngx_log(DEBUG, "Nginx worker is exiting")
|
||||||
|
end
|
||||||
|
bytes = 0
|
||||||
|
elseif (msg_len + buffer_size < flush_limit) then
|
||||||
|
_write_buffer(msg, msg_len)
|
||||||
|
bytes = msg_len
|
||||||
|
elseif (msg_len + buffer_size <= drop_limit) then
|
||||||
|
_write_buffer(msg, msg_len)
|
||||||
|
_flush_buffer()
|
||||||
|
bytes = msg_len
|
||||||
|
else
|
||||||
|
_flush_buffer()
|
||||||
|
if (debug) then
|
||||||
|
ngx_log(DEBUG, "logger buffer is full, this log message will be "
|
||||||
|
.. "dropped")
|
||||||
|
end
|
||||||
|
bytes = 0
|
||||||
|
--- this log message doesn't fit in buffer, drop it
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_error then
|
||||||
|
local err = last_error
|
||||||
|
last_error = nil
|
||||||
|
return bytes, err
|
||||||
|
end
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.initted()
|
||||||
|
return logger_initted
|
||||||
|
end
|
||||||
|
|
||||||
|
_M.flush = _flush
|
||||||
|
|
||||||
|
return _M
|
||||||
|
|
9
waf.lua
9
waf.lua
|
@ -1,8 +1,12 @@
|
||||||
local content_length=tonumber(ngx.req.get_headers()['content-length'])
|
local content_length=tonumber(ngx.req.get_headers()['content-length'])
|
||||||
local method=ngx.req.get_method()
|
local method=ngx.req.get_method()
|
||||||
|
--ngx_lua如果是0.9.2以上版本,建议正则过滤函数改为ngx.re.find,匹配效率会提高三倍左右。
|
||||||
local ngxmatch=ngx.re.match
|
local ngxmatch=ngx.re.match
|
||||||
|
--local ngxmatch=ngx.re.find
|
||||||
if whiteip() then
|
if whiteip() then
|
||||||
elseif blockip() then
|
elseif blockip() then
|
||||||
|
--检测攻击ip是否被拦截。
|
||||||
|
elseif denyhackip(1) then
|
||||||
elseif denycc() then
|
elseif denycc() then
|
||||||
elseif ngx.var.http_Acunetix_Aspect then
|
elseif ngx.var.http_Acunetix_Aspect then
|
||||||
ngx.exit(444)
|
ngx.exit(444)
|
||||||
|
@ -42,7 +46,7 @@ elseif PostCheck then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
size = size + len(data)
|
size = size + len(data)
|
||||||
local m = ngxmatch(data,[[Content-Disposition: form-data;(.+)filename="(.+)\\.(.*)"]],'ijo')
|
local m = ngxmatch(data,[[Content-Disposition: form-data;(.+)filename="(.+)\.(.*)"]],'ijo')
|
||||||
if m then
|
if m then
|
||||||
fileExtCheck(m[3])
|
fileExtCheck(m[3])
|
||||||
filetranslate = true
|
filetranslate = true
|
||||||
|
@ -82,7 +86,8 @@ elseif PostCheck then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
^/123/$
|
^/123/$
|
||||||
|
site:^www\.baidu\.com/whiteurl/
|
||||||
|
|
Loading…
Reference in New Issue