limit_ip_access.lua 4.16 KB
local modname= ...
local M={}
_G[modname]=M
package.loaded[modname]=M

local cjson=require "cjson"
local rate_limit = require "limit_common_flow"
local iptool=require "iptool"
local lrucache = require "resty.lrucache"


local cache=lua_context.mal_ip_cache
local redis_limit_ip=lua_context["redis_limit_ip"]



function M.in_array(value,arr)
  if not value then
   return false
  end
  if not arr then
    return false  
  end 
  for k, v in ipairs(arr) do 
   if value==v then
    return true
   end 
  end
  return false
end

function M:get_req_param(req_param)
  local method = ngx.var.request_method
  if "GET" == method then
    return ngx.req.get_uri_args()[req_param]
  else
    ngx.req.read_body()
    return ngx.req.get_post_args()[req_param]
  end
end

--1:api 2:service
function M:limit_ip_access()
    local limit_ip_config=lua_context.configs["limit_ip_access"]
    local common_config=lua_context.configs["common_conf"]
    -- global switch control 
    if not common_config.lua_golbal_switch then
      return 
    end
    -- is open ip limit access control
    if not limit_ip_config.is_open then
      return 
    end

    local ip=ngx.var.real_ip
    if not ip then
      return 
    end 
  --check is in white ip list
  local white_ips_length=#limit_ip_config.white_ips
  if white_ips_length >0 then
    for i=1,white_ips_length do
      local is_in_white_ips=iptool:pcall_check_ip_in_ipblock(ip,limit_ip_config.white_ips[i],false)
      if is_in_white_ips then
        return 
      end
    end
  end

  -- check ip is in limit ip list
  local is_limit=self.in_array(ip,limit_ip_config.limit_ip_config)

  local limit_type="0"
  -- check ip access is arrive max access
  local ip_qps_limit=limit_ip_config.ip_qps_limit
  if (not is_limit) and ip_qps_limit and ip_qps_limit[1]  and ip_qps_limit[2] then
    if not rate_limit.limit_flow("yh:limit:ip:" .. ip,limit_ip_config.ip_qps_limit[1],limit_ip_config.ip_qps_limit[2]) then
     is_limit=true
     limit_type="1"
    end
  end
  
  -- check method or uri limit
  local req_uri_method
  local method_limit_conf
  if(not is_limit) and limit_ip_config.interface_ip_qps_limit then
  	req_uri_method = self:get_req_param("method")
  	if not req_uri_method then
       req_uri_method = ngx.var.uri
  	end
    method_limit_conf=limit_ip_config.interface_ip_qps_limit[req_uri_method]
  end

  if (not is_limit) and req_uri_method and method_limit_conf then
    if not rate_limit.limit_flow("yh:limit:ip:" .. ip .. ":" .. req_uri_method,method_limit_conf[1],method_limit_conf[2]) then
        is_limit=true
		limit_type="2"
    end
  end
  
 local is_white_method=self.in_array(req_uri_method,limit_ip_config.white_method)
 if is_white_method then
    return
 end

  -- check redis config 
  if not is_limit then 
      
    local res=cache:get("yh:mip:" .. ip)

    if res then
     	is_limit=true
	limit_type="3"
    end 
  end
  	
  if is_limit then
     ngx.log(ngx.ERR, "[LimitIPAccess:ip]:" .. ip .. ",type:" .. limit_type)
     ngx.header["Content-type"]="application/json;charset=utf-8"
     if limit_type=="3" then
       ngx.header["x-yoho-malicode"]="10011"
     end
    ngx.exit(ngx.HTTP_OK)
  end
end

-- malicious ip  
function M:mal_ip()
   local method=self:get_req_param("method")
   local ips=self:get_req_param("ips")
   local expire=self:get_req_param("expire")
   ngx.header["Content-type"]="application/json;charset=utf-8"
   if not method or not ips then
    ngx.say('{"code": 400, "msg": "params error!"}')
	ngx.exit(ngx.HTTP_OK)
   end
   if method == 'publish' then
      local t={}
      t.ips=ips
      t.expire=expire
      redis_limit_ip:cmd("publish","mal_ips",cjson.encode(t))
   end 
   local exists={}
   for ip in string.gmatch(ips,"[^',']+") do
       if method == 'add' then
           local expire= (not expire) and 86400 or expire
            cache:set("yh:mip:" .. ip,"1",expire)
       elseif method == 'del' then
	     cache:delete("yh:mip:" .. ip)
       elseif method == 'exists' then 
           local res=cache:get("yh:mip:" .. ip)
           res= res and true or false
           exists[#exists+1]=tostring(res)
       end
   end
   local body=table.concat(exists,",")
   ngx.say('{"code": 200, "msg": "'.. body ..'"}')
   ngx.exit(ngx.HTTP_OK)
end