255 lines
8.0 KiB
Lua
255 lines
8.0 KiB
Lua
|
|
local skynet = require "skynet"
|
|||
|
|
local oo = require "Class"
|
|||
|
|
local gameCmd = require "GameCmd"
|
|||
|
|
local json =require "json"
|
|||
|
|
local log = require "Log"
|
|||
|
|
local sqlUrl = require "SqlUrl"
|
|||
|
|
local errorInfo = require "ErrorInfo"
|
|||
|
|
local curServerId = tonumber(skynet.getenv "serverId")
|
|||
|
|
local redisKeyUrl = require "RedisKeyUrl"
|
|||
|
|
local clusterServer = require "ClusterServer"
|
|||
|
|
local Monitor = oo.class(clusterServer)
|
|||
|
|
|
|||
|
|
Monitor.per5Second = 5
|
|||
|
|
Monitor.per5SecondIpCount = 100 --每5秒IP最大访问次数
|
|||
|
|
Monitor.iPUnlockTime = 3600 --黑名单IP解锁时间
|
|||
|
|
Monitor.IpOutTime = 1800 --访问IP过期时间
|
|||
|
|
Monitor.MaxIllegalCount = 200 --最大非法数量
|
|||
|
|
Monitor.ResetTriggerTime = 300 --重置触发规则时间为5分钟
|
|||
|
|
|
|||
|
|
--触发类型
|
|||
|
|
Monitor.TriggerType_MsgTooFast = 1 --消息过快
|
|||
|
|
Monitor.TriggerType_Max = 2 --触发最大类型
|
|||
|
|
|
|||
|
|
--[[
|
|||
|
|
触发信息
|
|||
|
|
下面为通用参数,每条信息都必须带,其余参数可以根据条件自己定 比如触发MsgTooFast 的自定义参数为cmds
|
|||
|
|
type 触发类型
|
|||
|
|
funcName 函数名,里面的字符串必须和定的函数名一样,否则不会触发相应功能
|
|||
|
|
count 触发次数,达到一定数量会将IP加入到黑名单里
|
|||
|
|
firstTime 第一次触发时间
|
|||
|
|
]]
|
|||
|
|
Monitor.TriggerInfo =
|
|||
|
|
{
|
|||
|
|
{ type = Monitor.TriggerType_MsgTooFast , funcName = "CheckMsgTooFast" , count = 0 , firstTime = 0 , cmds = {} } --触发消息过快 参数 cmds 保存消息各种信息
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--初始化
|
|||
|
|
function Monitor:Init()
|
|||
|
|
if true then
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
self.ipBlackList = {} --IP黑名单
|
|||
|
|
self.ipList = {} --统计当前IP
|
|||
|
|
|
|||
|
|
log.info("开始同步IP黑名单")
|
|||
|
|
local key = redisKeyUrl.MonitorServerIPBlackListZSet
|
|||
|
|
|
|||
|
|
local ipList = skynet.server.redis:zrange( key , 0 , -1 , "withscores")
|
|||
|
|
ipList = redisKeyUrl:CovertTable(ipList)
|
|||
|
|
local ipCount = 0
|
|||
|
|
for ip, recordTime in pairs( ipList ) do
|
|||
|
|
self.ipBlackList[ ip ] = {}
|
|||
|
|
self.ipBlackList[ ip ].recordTime = recordTime
|
|||
|
|
ipCount = ipCount + 1
|
|||
|
|
end
|
|||
|
|
log.info(string.format("成功同步IP黑名单 数量 %d",ipCount))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--跨天
|
|||
|
|
function Monitor:OnNewDay()
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--1秒Timer
|
|||
|
|
function Monitor:On1SecTimer()
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--5秒Timer
|
|||
|
|
|
|||
|
|
function Monitor:On5SecTimer()
|
|||
|
|
--self:CheckAttack()
|
|||
|
|
--self:CheckIpOutTime()
|
|||
|
|
--self:CheckUnlockIP()
|
|||
|
|
--self:ResetTrigger()
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检测攻击行为
|
|||
|
|
--[[
|
|||
|
|
function Monitor:CheckAttack()
|
|||
|
|
local curTime = skynet.GetTime()
|
|||
|
|
for k, v in pairs( self.ipList ) do
|
|||
|
|
if curTime <= v.record5SecTime + self.per5Second then
|
|||
|
|
if v.record5SecCount >= self.per5SecondIpCount then --在指定的时间内大于阈值就拉黑
|
|||
|
|
self:AddIPBlackList( k )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
]]
|
|||
|
|
|
|||
|
|
--是否为合法IP
|
|||
|
|
function Monitor:IsVaildIP( ip , cmd )
|
|||
|
|
if self.ipBlackList[ ip ] then
|
|||
|
|
return errorInfo.ErrorCode.IPBlackList
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local errorCode = errorInfo.Suc
|
|||
|
|
cmd = tostring(cmd)
|
|||
|
|
local curTime = skynet.GetTime()
|
|||
|
|
if not self.ipList[ ip ] then
|
|||
|
|
self.ipList[ ip ] = {}
|
|||
|
|
|
|||
|
|
--一开始就需要用到该触发器可以在这里加,其它可以在触发功能的地方加都行。
|
|||
|
|
self:AddTrigger( ip , self.TriggerType_MsgTooFast )
|
|||
|
|
else
|
|||
|
|
--对该IP的各种触发检测
|
|||
|
|
for k, v in pairs( self.ipList[ ip ].trigger ) do
|
|||
|
|
errorCode = self[ v.funcName ]( self , ip , cmd )
|
|||
|
|
if errorInfo.Suc ~= errorCode then
|
|||
|
|
return errorCode
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
self.ipList[ ip ].lastRecordTime = curTime --记录最新时间
|
|||
|
|
return errorInfo.Suc
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--添加IP到黑名单
|
|||
|
|
function Monitor:AddIPBlackList( ip )
|
|||
|
|
local key = redisKeyUrl.MonitorServerIPBlackListZSet
|
|||
|
|
skynet.server.redis:zadd( key , skynet.GetTime() , ip )
|
|||
|
|
self.ipBlackList[ ip ] = {}
|
|||
|
|
self.ipBlackList[ ip ].recordTime = skynet.GetTime()
|
|||
|
|
|
|||
|
|
local c2sData = {}
|
|||
|
|
c2sData.sendServerId = curServerId
|
|||
|
|
c2sData.ip = ip
|
|||
|
|
local curServer = skynet.server.serverManage:GetServer()
|
|||
|
|
for k, v in pairs( curServer.clusterInfo ) do
|
|||
|
|
if curServerId ~= v.serverId then
|
|||
|
|
curServer:SendMsgToServer( v.serverId , curServer.Moniter2All_AddIPToBlack , c2sData )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
log.info(string.format("监控服 IP %s 加入黑名单 " , ip ))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检测IP是否能解锁
|
|||
|
|
function Monitor:CheckUnlockIP()
|
|||
|
|
--删除当前时间之前一小时的黑名单IP
|
|||
|
|
local key =redisKeyUrl.MonitorServerIPBlackListZSet
|
|||
|
|
local unlockTime = skynet.GetTime() - self.iPUnlockTime
|
|||
|
|
skynet.server.redis:zremrangebyscore( key , "-inf" , unlockTime )
|
|||
|
|
|
|||
|
|
for ip, v in pairs( self.ipBlackList ) do
|
|||
|
|
if skynet.GetTime() >= v.recordTime + self.iPUnlockTime then
|
|||
|
|
self.ipBlackList[ ip ] = nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检测过期IP
|
|||
|
|
function Monitor:CheckIpOutTime()
|
|||
|
|
local curTime = skynet.GetTime()
|
|||
|
|
for k, v in pairs( self.ipList ) do
|
|||
|
|
if curTime >= v.lastRecordTime + self.IpOutTime then
|
|||
|
|
--删除过期IP
|
|||
|
|
self.ipList[ k ] = nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--增加触发
|
|||
|
|
function Monitor:AddTrigger( ip , triggerType )
|
|||
|
|
triggerType = triggerType or 0
|
|||
|
|
if triggerType <= 0 or triggerType >= self.TriggerType_Max then
|
|||
|
|
log.info(string.format("监控服 IP %s 增加触发 %d 类型失败 " , ip , triggerType ))
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if not self.ipList[ ip ].trigger then
|
|||
|
|
--初始化该IP的触发器
|
|||
|
|
self.ipList[ ip ].trigger = {}
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if not self.ipList[ ip ].trigger[ triggerType ] then
|
|||
|
|
--该IP没有指定触发类型就加进来
|
|||
|
|
for k, v in pairs( self.TriggerInfo ) do
|
|||
|
|
if triggerType == v.type then
|
|||
|
|
self.ipList[ ip ].trigger[ triggerType ] = v
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--重置触发
|
|||
|
|
function Monitor:ResetTrigger()
|
|||
|
|
local curTime = skynet.GetTime()
|
|||
|
|
for k1, v1 in pairs( self.ipList ) do
|
|||
|
|
for k2, v2 in pairs( v1.trigger ) do
|
|||
|
|
if curTime - v2.firstTime >= self.ResetTriggerTime then
|
|||
|
|
--违规信息重置
|
|||
|
|
v2.firstTime = 0
|
|||
|
|
v2.count = 0
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--获取触发
|
|||
|
|
function Monitor:GetTrigger( ip , triggerType )
|
|||
|
|
triggerType = triggerType or 0
|
|||
|
|
if triggerType <= 0 or triggerType >= self.TriggerType_Max then
|
|||
|
|
log.info(string.format("监控服 IP %s 获取触发 %d 类型失败 " , ip , triggerType ))
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
return self.ipList[ ip ].trigger[ triggerType ]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--触发违规
|
|||
|
|
function Monitor:TriggerIllegal( ip , triggerType )
|
|||
|
|
local curTrigger = self:GetTrigger( ip , triggerType )
|
|||
|
|
|
|||
|
|
curTrigger.count = curTrigger.count + 1
|
|||
|
|
if 0 == curTrigger.firstTime then
|
|||
|
|
--记录下首次触发时间
|
|||
|
|
curTrigger.firstTime = skynet.GetTime()
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--5分钟内,触发最大次数,加入黑名单
|
|||
|
|
if curTrigger.count >= self.MaxIllegalCount then
|
|||
|
|
self:AddIPBlackList( ip )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检查是否消息过快
|
|||
|
|
function Monitor:CheckMsgTooFast( ip , cmd )
|
|||
|
|
local curTime = skynet.GetTime()
|
|||
|
|
|
|||
|
|
--获取当前类型的触发器
|
|||
|
|
local triggerType = self.TriggerType_MsgTooFast
|
|||
|
|
local curTrigger = self:GetTrigger( ip , triggerType )
|
|||
|
|
|
|||
|
|
if not curTrigger.cmds[ cmd ] then
|
|||
|
|
--对此命令进行初始化
|
|||
|
|
curTrigger.cmds[ cmd ] = {}
|
|||
|
|
curTrigger.cmds[ cmd ].recordTime = curTime
|
|||
|
|
else
|
|||
|
|
local cmdInfo = curTrigger.cmds[ cmd ]
|
|||
|
|
if curTime - cmdInfo.recordTime <= 0 then --1秒内两次执行相同命令就返回消息过快的错误,然后触发非法类型
|
|||
|
|
self:TriggerIllegal( ip , triggerType )
|
|||
|
|
return errorInfo.ErrorCode.RequestMsgTooFast
|
|||
|
|
else
|
|||
|
|
curTrigger.cmds[ cmd ].recordTime = curTime
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
return errorInfo.Suc
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
skynet.server.monitor = Monitor
|
|||
|
|
return Monitor
|