# 组件分享

# ipset_block组件

# 组件说明

在CC攻击场景下,当攻击请求的QPS超过了节点的处理QPS,这个时候WAF拦截CC攻击请求的同时,也会因为负载过高而无法正常处理业务请求。

所以需要在网络层直接对持续攻击的IP进行封禁,避免应用层负载过高的情况。组件会负责将需要封禁的IP通过HTTP请求发送到节点的jxwaf_ipset_block程序。

jxwaf_ipset_block会将接受到的攻击IP,调用本地的ipset进行封禁。

# 代码分析

-- 初始化模块
local _M = {}
_M.version = "jxwaf4"

-- 引入依赖
local http = require "resty.jxwaf.http" -- 用于创建HTTP请求的自定义模块
local cjson = require "cjson.safe" -- 安全的JSON编解码模块
local unify_action = require "resty.jxwaf.unify_action" -- 统一行动处理的自定义模块

-- 功能:发送IP封禁请求到远端服务器
local function send_ipset_block(period,ip_ban_server,black_ip,ip_ban_auth)
  -- 构造IP封禁服务端点的URL
  local ip_ban_website = "http://"..ip_ban_server.."/banip"
  local httpc = http.new()
  httpc:set_timeouts(10000, 10000, 10000) -- 设置HTTP超时
  -- 准备请求主体
  local body_data = {
    network_block_ip =  black_ip,
    auth = ip_ban_auth
  }
  -- 发送POST请求
  local res, err = httpc:request_uri( ip_ban_website , {
    method = "POST",
    headers = {
      ["Content-Type"] = "application/json;charset=UTF-8",
    },
    body = cjson.encode(body_data)
  })
  -- 错误处理
  if not res then
    ngx.log(ngx.ERR,"send http failed to request: ", err)
    return 
  end
  if res.status ~= 200 then
    ngx.log(ngx.ERR, "IP ban request returned non-200 response: ", res.status)
    return
  end
  -- 解析响应体
  local res_body = cjson.decode(res.body)
  if not res_body then
    ngx.log(ngx.ERR,"send  fail,failed to decode resp body")
    ngx.log(ngx.ERR,res.body)
    return
  end
  -- 检查结果
  if res_body['result'] ~= true then
    ngx.log(ngx.ERR,"result ~= true " )
    ngx.log(ngx.ERR,res.body)
    return
  end
  return true
end

-- 功能:检查请求是否符合封禁条件
function _M.check(conf_data)
  -- 确保配置数据存在
  if conf_data == nil then
    return 
  end
  -- 验证和提取必要配置
  local jxwaf_ipset_node =  conf_data['jxwaf_ipset_node']
  if type(jxwaf_ipset_node) ~= 'table'  then
    return
  end
  local auth = conf_data['auth']
  if type(auth) ~= 'string'  then
    return
  end
  local flow_ip_region_block = conf_data['flow_ip_region_block']
  if type(flow_ip_region_block) ~= 'boolean'  then
    ngx.log(ngx.ERR,type(flow_ip_region_block))
    return
  end
  local flow_rule_protection = conf_data['flow_rule_protection']
  if type(flow_rule_protection) ~= 'table'  then
    return
  end
  local flow_engine_protection = conf_data['flow_engine_protection']
  if type(flow_engine_protection) ~= 'table'  then
    return
  end
  
  -- 获取请求处理结果
  local flow_ip_region_block_result = ngx.ctx.flow_ip_region_block_result
  local flow_rule_protection_result = ngx.ctx.flow_rule_protection_result
  local flow_engine_protection_result = ngx.ctx.flow_engine_protection_result
  
  -- 检查是否触发封禁条件
  local check_result 
  if  flow_ip_region_block == true and flow_ip_region_block_result then
    check_result = true
  end
  for _,v in ipairs(flow_rule_protection) do 
    if flow_rule_protection_result[v] then
      check_result = true
    end
  end
  for _,v in ipairs(flow_engine_protection) do 
    if flow_engine_protection_result[v] then
      check_result = true
    end
  end
  
  -- 如果触发封禁条件
  if check_result then
    local src_ip = ngx.ctx.src_ip or ngx.var.remote_addr -- 获取来源IP
    -- 对每个IP设置发送封禁请求
    for _,v in ipairs(jxwaf_ipset_node) do
        -- ngx.timer.at的调用,第一个参数为0表示立即执行,第二个参数是要调用的函数及其参数
        local ok, err = ngx.timer.at(0, send_ipset_block, v, src_ip, auth)
        if not ok then
            -- 如果创建定时器失败,记录错误(除非进程正在退出)
            if err ~= "process exiting" then
            ngx.log(ngx.ERR, "failed to create the send send_ipset_block http timer: ", err)
            end
        end
    end
    -- 更新和记录行为日志
    local waf_log = ngx.ctx.waf_log 
    waf_log['waf_action'] = "ipset_block" -- 行为标记为IP封禁
    ngx.ctx.waf_log = waf_log
    -- 调用统一行动处理模块执行拒绝响应
    unify_action.reject_response()
    -- 返回true表示请求被拦截并处理
    return true
  end
  -- 如果没有触发封禁条件,则不执行任何操作
  return 
end

-- 将模块返回,使其功能可被外部调用
return _M

# 组件部署说明

JXWAF控制台 -> 防护管理 -> 分析组件 -> 新建组件

# 组件名称

ipset_block

# 组件描述

ipset封禁组件

# CODE

bG9jYWwgX00gPSB7fQpfTS52ZXJzaW9uID0gImp4d2FmNCIKbG9jYWwgaHR0cCA9IHJlcXVpcmUgInJlc3R5Lmp4d2FmLmh0dHAiCmxvY2FsIGNqc29uID0gcmVxdWlyZSAiY2pzb24uc2FmZSIKbG9jYWwgdW5pZnlfYWN0aW9uID0gcmVxdWlyZSAicmVzdHkuanh3YWYudW5pZnlfYWN0aW9uIgoKbG9jYWwgZnVuY3Rpb24gc2VuZF9pcHNldF9ibG9jayhwZXJpb2QsaXBfYmFuX3NlcnZlcixibGFja19pcCxpcF9iYW5fYXV0aCkKICBsb2NhbCBpcF9iYW5fd2Vic2l0ZSA9ICJodHRwOi8vIi4uaXBfYmFuX3NlcnZlci4uIi9iYW5pcCIKICBsb2NhbCBodHRwYyA9IGh0dHAubmV3KCkKICBodHRwYzpzZXRfdGltZW91dHMoMTAwMDAsIDEwMDAwLCAxMDAwMCkKICBsb2NhbCBib2R5X2RhdGEgPSB7CiAgICBuZXR3b3JrX2Jsb2NrX2lwID0gIGJsYWNrX2lwLAogICAgYXV0aCA9IGlwX2Jhbl9hdXRoCiAgfQogIGxvY2FsIHJlcywgZXJyID0gaHR0cGM6cmVxdWVzdF91cmkoIGlwX2Jhbl93ZWJzaXRlICwgewogICAgbWV0aG9kID0gIlBPU1QiLAogICAgaGVhZGVycyA9IHsKICAgICAgWyJDb250ZW50LVR5cGUiXSA9ICJhcHBsaWNhdGlvbi9qc29uO2NoYXJzZXQ9VVRGLTgiLAogICAgfSwKICAgIGJvZHkgPSBjanNvbi5lbmNvZGUoYm9keV9kYXRhKQogIH0pCiAgaWYgbm90IHJlcyB0aGVuCiAgICBuZ3gubG9nKG5neC5FUlIsInNlbmQgaHR0cCBmYWlsZWQgdG8gcmVxdWVzdDogIiwgZXJyKQogICAgcmV0dXJuIAogIGVuZAogIGlmIHJlcy5zdGF0dXMgfj0gMjAwIHRoZW4KICAgIG5neC5sb2cobmd4LkVSUiwgIklQIGJhbiByZXF1ZXN0IHJldHVybmVkIG5vbi0yMDAgcmVzcG9uc2U6ICIsIHJlcy5zdGF0dXMpCiAgICByZXR1cm4KICBlbmQKICBsb2NhbCByZXNfYm9keSA9IGNqc29uLmRlY29kZShyZXMuYm9keSkKICBpZiBub3QgcmVzX2JvZHkgdGhlbgogICAgbmd4LmxvZyhuZ3guRVJSLCJzZW5kICBmYWlsLGZhaWxlZCB0byBkZWNvZGUgcmVzcCBib2R5IikKICAgIG5neC5sb2cobmd4LkVSUixyZXMuYm9keSkKICAgIHJldHVybgogIGVuZAogIGlmIHJlc19ib2R5WydyZXN1bHQnXSB+PSB0cnVlIHRoZW4KICAgIG5neC5sb2cobmd4LkVSUiwicmVzdWx0IH49IHRydWUgIiApCiAgICBuZ3gubG9nKG5neC5FUlIscmVzLmJvZHkpCiAgICByZXR1cm4KICBlbmQKICByZXR1cm4gdHJ1ZQplbmQKCmZ1bmN0aW9uIF9NLmNoZWNrKGNvbmZfZGF0YSkKICBpZiBjb25mX2RhdGEgPT0gbmlsIHRoZW4KICAgIHJldHVybiAKICBlbmQKCiAgbG9jYWwganh3YWZfaXBzZXRfbm9kZSA9ICBjb25mX2RhdGFbJ2p4d2FmX2lwc2V0X25vZGUnXQogIGlmIHR5cGUoanh3YWZfaXBzZXRfbm9kZSkgfj0gJ3RhYmxlJyAgdGhlbgogICAgcmV0dXJuCiAgZW5kCgogIGxvY2FsIGF1dGggPSBjb25mX2RhdGFbJ2F1dGgnXQogIGlmIHR5cGUoYXV0aCkgfj0gJ3N0cmluZycgIHRoZW4KICAgIHJldHVybgogIGVuZAoKICBsb2NhbCBmbG93X2lwX3JlZ2lvbl9ibG9jayA9IGNvbmZfZGF0YVsnZmxvd19pcF9yZWdpb25fYmxvY2snXQogIGlmIHR5cGUoZmxvd19pcF9yZWdpb25fYmxvY2spIH49ICdib29sZWFuJyAgdGhlbgogICAgbmd4LmxvZyhuZ3guRVJSLHR5cGUoZmxvd19pcF9yZWdpb25fYmxvY2spKQogICAgcmV0dXJuCiAgZW5kCgogIGxvY2FsIGZsb3dfcnVsZV9wcm90ZWN0aW9uID0gY29uZl9kYXRhWydmbG93X3J1bGVfcHJvdGVjdGlvbiddCiAgaWYgdHlwZShmbG93X3J1bGVfcHJvdGVjdGlvbikgfj0gJ3RhYmxlJyAgdGhlbgogICAgcmV0dXJuCiAgZW5kCiAgbG9jYWwgZmxvd19lbmdpbmVfcHJvdGVjdGlvbiA9IGNvbmZfZGF0YVsnZmxvd19lbmdpbmVfcHJvdGVjdGlvbiddCiAgaWYgdHlwZShmbG93X2VuZ2luZV9wcm90ZWN0aW9uKSB+PSAndGFibGUnICB0aGVuCiAgICByZXR1cm4KICBlbmQKCiAgbG9jYWwgZmxvd19pcF9yZWdpb25fYmxvY2tfcmVzdWx0ID0gbmd4LmN0eC5mbG93X2lwX3JlZ2lvbl9ibG9ja19yZXN1bHQKICBsb2NhbCBmbG93X3J1bGVfcHJvdGVjdGlvbl9yZXN1bHQgPSBuZ3guY3R4LmZsb3dfcnVsZV9wcm90ZWN0aW9uX3Jlc3VsdAogIGxvY2FsIGZsb3dfZW5naW5lX3Byb3RlY3Rpb25fcmVzdWx0ID0gbmd4LmN0eC5mbG93X2VuZ2luZV9wcm90ZWN0aW9uX3Jlc3VsdAogIGxvY2FsIGNoZWNrX3Jlc3VsdCAKICBpZiAgZmxvd19pcF9yZWdpb25fYmxvY2sgPT0gdHJ1ZSBhbmQgZmxvd19pcF9yZWdpb25fYmxvY2tfcmVzdWx0IHRoZW4KICAgIGNoZWNrX3Jlc3VsdCA9IHRydWUKICBlbmQKCiAgZm9yIF8sdiBpbiBpcGFpcnMoZmxvd19ydWxlX3Byb3RlY3Rpb24pIGRvIAogICAgaWYgZmxvd19ydWxlX3Byb3RlY3Rpb25fcmVzdWx0W3ZdIHRoZW4KICAgICAgY2hlY2tfcmVzdWx0ID0gdHJ1ZQogICAgZW5kCiAgZW5kCgogIGZvciBfLHYgaW4gaXBhaXJzKGZsb3dfZW5naW5lX3Byb3RlY3Rpb24pIGRvIAogICAgaWYgZmxvd19lbmdpbmVfcHJvdGVjdGlvbl9yZXN1bHRbdl0gdGhlbgogICAgICBjaGVja19yZXN1bHQgPSB0cnVlCiAgICBlbmQKICBlbmQKCiAgaWYgY2hlY2tfcmVzdWx0IHRoZW4KICAgIGxvY2FsIHNyY19pcCA9IG5neC5jdHguc3JjX2lwIG9yIG5neC52YXIucmVtb3RlX2FkZHIKICAgIGZvciBfLHYgaW4gaXBhaXJzKGp4d2FmX2lwc2V0X25vZGUpIGRvCiAgICAgIGxvY2FsIG9rLCBlcnIgPSBuZ3gudGltZXIuYXQoMCxzZW5kX2lwc2V0X2Jsb2NrLHYsc3JjX2lwLGF1dGgpCiAgICAgIGlmIG5vdCBvayB0aGVuCiAgICAgICAgaWYgZXJyIH49ICJwcm9jZXNzIGV4aXRpbmciIHRoZW4KICAgICAgICAgIG5neC5sb2cobmd4LkVSUiwgImZhaWxlZCB0byBjcmVhdGUgdGhlIHNlbmQgc2VuZF9pcHNldF9ibG9jayBodHRwIHRpbWVyOiAiLCBlcnIpCiAgICAgICAgZW5kCiAgICAgIGVuZAogICAgZW5kCiAgICBsb2NhbCB3YWZfbG9nID0gbmd4LmN0eC53YWZfbG9nIAogICAgd2FmX2xvZ1snd2FmX2FjdGlvbiddID0gImlwc2V0X2Jsb2NrIgogICAgbmd4LmN0eC53YWZfbG9nID0gd2FmX2xvZwogICAgdW5pZnlfYWN0aW9uLnJlamVjdF9yZXNwb25zZSgpCiAgICByZXR1cm4gdHJ1ZQogIGVuZAogIHJldHVybiAKZW5kCgpyZXR1cm4gX00=

说明:

需要写入经过BASE64编码后的代码,直接复制上面已经编码好的字符串即可。

# 默认配置

{"jxwaf_ipset_node":["1.1.1.1:6677","2.2.2.2:6677"],"auth":"aaaa","flow_ip_region_block":true,"flow_rule_protection":["test_rule"],"flow_engine_protection":["high_freq_cc_rate_check","high_freq_cc_count_check"]}

说明:

  • jxwaf_ipset_node

部署了jxwaf_ipset_block的节点服务器地址,默认是6677端口

  • auth

鉴权字段,在jxwaf_ipset_block程序启动会指定auth的值

  • flow_ip_region_block

是否对IP区域封禁的匹配结果开启封禁,值为 true 或 false。

  • flow_rule_protection

是否对流量防护规则的匹配结果开启封禁,值为数组类型,写入具体的流量防护规则名称。

  • flow_engine_protection

是否对流量防护引擎的匹配结果开启封禁,值为数组类型,写入具体的流量防护模块名称。

具体值为:

high_freq_cc_rate_check

高频CC攻击防护-IP请求频率检测

high_freq_cc_count_check

高频CC攻击防护-IP请求次数检测

slow_cc_ip_count_check

慢速CC攻击防护-请求IP数量检测

slow_cc_domain_check

慢速CC攻击防护-回源保护机制

emergency_mode_check

无差别紧急防护