WAF代码剖析之init*阶段
字数 1273 2025-08-15 21:32:26

JXWAF初始化阶段代码剖析教学文档

1. 概述

本文详细剖析JXWAF在init阶段的实现原理和行为,包括init_by_lua_fileinit_worker_by_lua_file两个关键阶段。

2. OpenResty初始化阶段

2.1 init_by_lua_file

作用:当Nginx master进程加载配置文件时运行指定的Lua脚本,用于注册全局变量或预加载模块。

配置示例

init_by_lua_file /opt/jxwaf/lualib/resty/jxwaf/init.lua;

2.2 init_worker_by_lua_file

作用:在每个Nginx worker进程启动时调用指定的Lua代码,用于创建定时器、健康检查或重载Nginx。

配置示例

init_worker_by_lua_file /opt/jxwaf/lualib/resty/jxwaf/init_worker.lua;

3. init.lua详细解析

3.1 lua-resty-core性能优化

原理:使用FFI模式重新实现lua-nginx-module的API,大幅提升性能。

性能对比

  • 旧版(1.13.6.2):10亿次base64加密耗时80秒
  • 新版(使用lua-resty-core):10亿次base64加密耗时10秒,性能提升8倍

代码示例

require 'resty.core'
local start = ngx.now()
for _ =1, 1000000000 do 
    ngx.encode_base64('123456')
end
ngx.update_time()
ngx.say(ngx.now() - start)

3.2 WAF初始化流程

主要步骤

  1. 引入WAF模块
  2. 加载配置文件
  3. 初始化配置

代码结构

local waf = require "resty.jxwaf.waf"
local config_path = "/opt/jxwaf/nginx/conf/jxwaf/jxwaf_config.json"
waf.init(config_path)

3.3 waf.lua核心实现

3.3.1 模块定义

local _M = {}  -- 定义模块表
local _config_path = "/opt/jxwaf/nginx/conf/jxwaf/jxwaf_config.json"

3.3.2 初始化函数

function _M.init(config_path)
    -- 读取配置文件
    local init_config_path = config_path or _config_path
    local read_config = assert(io.open(init_config_path,'r+'))
    local raw_config_info = read_config:read('*all')
    read_config:close()
    
    -- 解析JSON配置
    local config_info = cjson.decode(raw_config_info)
    if config_info == nil then
        ngx.log(ngx.ERR,"init fail,can not decode config file")
    end
    
    -- 生成节点UUID
    if not config_info['waf_node_uuid'] then
        local waf_node_uuid = uuid.generate_random()
        config_info['waf_node_uuid'] = waf_node_uuid
        local new_config_info = cjson.encode(config_info)
        local write_config = assert(io.open(init_config_path,'w+'))
        write_config:write(new_config_info)
        write_config:close()
    end
    
    _config_info = config_info
    
    -- IP处理工具初始化
    iputils.enable_lrucache()
    
    -- 启用特权代理进程
    local ok, err = process.enable_privileged_agent()
    if not ok then
        ngx.log(ngx.ERR, "enables privileged agent failed error:", err)
    end
    
    ngx.log(ngx.ALERT,"jxwaf init success,waf node uuid is ".._config_info['waf_node_uuid'])
end

3.3.3 IP白名单实现

init_by_lua_block {
    local iputils = require("resty.iputils")
    iputils.enable_lrucache()  -- 创建全局缓存空间
    local whitelist_ips = {
        "127.0.0.1", 
        "10.10.10.0/24",
    }
    whitelist = iputils.parse_cidrs(whitelist_ips)
}

access_by_lua_block {
    local iputils = require("resty.iputils")
    if not iputils.ip_in_cidrs(ngx.var.remote_addr, whitelist) then
        return ngx.exit(ngx.HTTP_FORBIDDEN)
    end
}

4. init_worker.lua详细解析

4.1 工作进程初始化

local waf = require "resty.jxwaf.waf"
waf.init_worker()

4.2 工作进程处理逻辑

4.2.1 特权代理进程处理

if process.type() == "privileged agent" then
    -- 节点监控
    if _config_info.waf_node_monitor == "true" then
        local monitor_ok,monitor_err = ngx.timer.at(0,_momitor_update)
        if not monitor_ok and monitor_err ~= "process exiting" then
            ngx.log(ngx.ERR, "failed to create the init timer: ", init_err)
        end
    end
    
    -- 全局规则更新
    local init_ok,init_err = ngx.timer.at(0,_global_update_rule)
    if not init_ok and init_err ~= "process exiting" then
        ngx.log(ngx.ERR, "failed to create the init timer: ", init_err)
    end
else
    -- 普通worker进程处理
    local worker_init_ok,worker_init_err = ngx.timer.at(0,_worker_update_rule)
    if not worker_init_ok and worker_init_err ~= "process exiting" then
        ngx.log(ngx.ERR, "failed to create the init timer: ", worker_init_err)
    end
    
    -- 定时更新规则
    local hdl, err = ngx.timer.every(5,_worker_update_rule)
    if err then
        ngx.log(ngx.ERR, "failed to create the worker update timer: ", err)
    end
end

4.2.2 监控更新函数

local function _momitor_update()
    local _update_website = _config_info.waf_monitor_website or "https://update2.jxwaf.com/waf_monitor"
    local httpc = http.new()
    httpc:set_timeouts(5000, 5000, 30000)  -- 设置超时
    
    -- 准备请求参数
    local api_key = _config_info.waf_api_key or ""
    local api_password = _config_info.waf_api_password or ""
    local server_info = _config_info.server_info or ""
    local waf_node_uuid = _config_info.waf_node_uuid or ""
    
    -- 发送监控数据
    local res, err = httpc:request_uri(_update_website, {
        method = "POST",
        body = "api_key="..api_key.."&api_password="..api_password.."&waf_node_uuid="..waf_node_uuid.."&server_info="..server_info,
        headers = {
            ["Content-Type"] = "application/x-www-form-urlencoded",
        }
    })
    
    -- 错误处理
    if not res then
        ngx.log(ngx.ERR,"failed to request: ", err)
        return _update_at(tonumber(_auto_update_period),_momitor_update)
    end
    
    -- 解析响应
    local res_body = cjson.decode(res.body)
    if not res_body then
        ngx.log(ngx.ERR,"init fail,failed to decode resp body")
        return _update_at(tonumber(_auto_update_period),_momitor_update)
    end
    
    if res_body['result'] == false then
        ngx.log(ngx.ERR,"init fail,failed to request, ",res_body['message'])
        return _update_at(tonumber(_auto_update_period),_momitor_update)
    end
    
    -- 更新监控状态
    _waf_node_monitor = res_body['waf_node_monitor'] or _waf_node_monitor
    if _waf_node_monitor == "true" then
        local global_ok, global_err = ngx.timer.at(tonumber(_waf_node_monitor_period),_momitor_update)
        if not global_ok and global_err ~= "process exiting" then
            ngx.log(ngx.ERR, "failed to create the cycle timer: ", global_err)
        end
    end
    
    ngx.log(ngx.ALERT,"monitor report success")
end

4.2.3 全局规则更新

local function _global_update_rule()
    -- 获取规则更新
    local _update_website = _config_info.waf_update_website or "https://update2.jxwaf.com/waf_update"
    local httpc = http.new()
    httpc:set_timeouts(5000, 5000, 30000)
    
    -- 发送请求
    local res, err = httpc:request_uri(_update_website, {
        method = "POST",
        body = "api_key=".._config_info.waf_api_key.."&api_password=".._config_info.waf_api_password.."&waf_node_uuid=".._config_info.waf_node_uuid,
        headers = {
            ["Content-Type"] = "application/x-www-form-urlencoded",
        }
    })
    
    -- 错误处理
    if not res then
        ngx.log(ngx.ERR,"failed to request: ", err)
        return _update_at(tonumber(_auto_update_period),_global_update_rule)
    end
    
    local res_body = cjson.decode(res.body)
    if not res_body then
        ngx.log(ngx.ERR,"init fail,failed to decode resp body")
        return _update_at(tonumber(_auto_update_period),_global_update_rule)
    end
    
    -- 规则更新处理
    if not res_body['no_update'] then
        local tmp_waf_rule = res_body['waf_rule']
        if tmp_waf_rule == nil then
            ngx.log(ngx.ERR,"init fail,can not decode waf rule")
            return _update_at(tonumber(_auto_update_period),_global_update_rule)
        else
            _update_waf_rule = tmp_waf_rule
        end
        
        -- 规则排序
        local table_sort = table.sort
        local function _sort_rules(a,b)
            if a.rule_level == b.rule_level then
                return tonumber(a.rule_id)<tonumber(b.rule_id)
            else
                return tonumber(a.rule_level)>tonumber(b.rule_level)
            end
        end
        
        for k,v in pairs(_update_waf_rule) do
            if type(v['custom_rule_set']) == "table" then
                table_sort(v['custom_rule_set'],_sort_rules)
                _update_waf_rule[k] = v 
            end
        end
    end
    
    -- 加载语义分析防御
    if res_body['jxcheck'] then
        local load_jxcheck = loadstring(ngx.decode_base64(res_body['jxcheck']))()
        if load_jxcheck then
            _jx_check = load_jxcheck
        end
    end
    
    -- 加载CC防护
    if res_body['botcheck'] then
        local load_botcheck = loadstring(ngx.decode_base64(res_body['botcheck']))()
        if load_botcheck then
            _bot_check = load_botcheck
        end
    end
    
    -- 加载人机识别key
    if res_body['bot_auth_key'] then
        local bot_check_info = res_body['bot_auth_key']
        bot_check_standard_info = bot_check_info['standard']
        bot_check_image_info = bot_check_info['image']
        bot_check_slipper_info = bot_check_info['slipper']
        
        local standard_key = {}
        local slipper_key = {}
        local image_key = {}
        
        for k,_ in pairs(bot_check_standard_info) do
            table.insert(standard_key,k)
        end
        bot_check_standard_key = standard_key
        
        for k,_ in pairs(bot_check_slipper_info) do
            table.insert(slipper_key,k)
        end
        bot_check_slipper_key = slipper_key
        
        for k,_ in pairs(bot_check_image_info) do
            table.insert(image_key,k)
        end
        bot_check_image_key = image_key
    end
    
    -- 加载日志配置
    if res_body['log_conf'] then
        _log_conf = res_body['log_conf']
    end
    
    -- 保存到共享内存
    local waf_common_conf = ngx.shared.waf_common_conf
    local md5_succ, md5_err = waf_common_conf:set("md5",res_body['md5'])
    if md5_err then
        ngx.log(ngx.ERR,"init fail,can not set waf_common_conf md5")
        return _update_at(tonumber(_auto_update_period),_global_update_rule)
    end
    
    local res_body_succ, res_body_err = waf_common_conf:set("res_body",res.body)
    if res_body_err then
        ngx.log(ngx.ERR,"init fail,can not set waf_common_conf res_body")
        return _update_at(tonumber(_auto_update_period),_global_update_rule)
    end
    
    _md5 = res_body['md5']
    ngx.log(ngx.ALERT,"global config info md5 is ".._md5..",update config info success")
end

5. 关键知识点总结

  1. 性能优化:使用lua-resty-core的FFI实现可以大幅提升性能(如base64加密性能提升8倍)

  2. 初始化流程

    • 读取并解析配置文件
    • 生成节点UUID
    • 初始化IP处理工具
    • 启用特权代理进程
  3. 特权代理

    • 用于与WEB控制台通信
    • 负责全局规则更新和节点监控
    • 将规则保存在共享内存中供worker进程读取
  4. 规则处理

    • 自定义规则排序(按规则级别和ID)
    • 动态加载语义分析和CC防护代码
    • 人机识别key管理
  5. 错误处理

    • 使用_update_at函数实现错误后的定时重试
    • 详细的错误日志记录
  6. 共享内存

    • 特权代理将规则保存在共享内存中
    • worker进程从共享内存读取规则
    • 使用MD5校验确保规则一致性
  7. 定时任务

    • 使用ngx.timer.atngx.timer.every实现定时更新
    • 支持周期性的健康检查和规则更新

6. 最佳实践建议

  1. 配置检查:确保jxwaf_config.json配置文件路径正确且可读写

  2. 性能调优

    • 启用lua-resty-core
    • 根据实际负载调整定时器间隔
  3. 错误监控

    • 监控Nginx错误日志中的ngx.ERR级别日志
    • 定期检查规则更新是否成功
  4. 安全建议

    • 保护WEB控制台的API key和密码
    • 定期更新节点UUID
  5. 调试技巧

    • 使用ngx.log输出调试信息
    • 可结合Wireshark抓包分析与控制台的通信
JXWAF初始化阶段代码剖析教学文档 1. 概述 本文详细剖析JXWAF在init阶段的实现原理和行为,包括 init_by_lua_file 和 init_worker_by_lua_file 两个关键阶段。 2. OpenResty初始化阶段 2.1 init_ by_ lua_ file 作用 :当Nginx master进程加载配置文件时运行指定的Lua脚本,用于注册全局变量或预加载模块。 配置示例 : 2.2 init_ worker_ by_ lua_ file 作用 :在每个Nginx worker进程启动时调用指定的Lua代码,用于创建定时器、健康检查或重载Nginx。 配置示例 : 3. init.lua详细解析 3.1 lua-resty-core性能优化 原理 :使用FFI模式重新实现lua-nginx-module的API,大幅提升性能。 性能对比 : 旧版(1.13.6.2):10亿次base64加密耗时80秒 新版(使用lua-resty-core):10亿次base64加密耗时10秒,性能提升8倍 代码示例 : 3.2 WAF初始化流程 主要步骤 : 引入WAF模块 加载配置文件 初始化配置 代码结构 : 3.3 waf.lua核心实现 3.3.1 模块定义 3.3.2 初始化函数 3.3.3 IP白名单实现 4. init_ worker.lua详细解析 4.1 工作进程初始化 4.2 工作进程处理逻辑 4.2.1 特权代理进程处理 4.2.2 监控更新函数 4.2.3 全局规则更新 5. 关键知识点总结 性能优化 :使用lua-resty-core的FFI实现可以大幅提升性能(如base64加密性能提升8倍) 初始化流程 : 读取并解析配置文件 生成节点UUID 初始化IP处理工具 启用特权代理进程 特权代理 : 用于与WEB控制台通信 负责全局规则更新和节点监控 将规则保存在共享内存中供worker进程读取 规则处理 : 自定义规则排序(按规则级别和ID) 动态加载语义分析和CC防护代码 人机识别key管理 错误处理 : 使用 _update_at 函数实现错误后的定时重试 详细的错误日志记录 共享内存 : 特权代理将规则保存在共享内存中 worker进程从共享内存读取规则 使用MD5校验确保规则一致性 定时任务 : 使用 ngx.timer.at 和 ngx.timer.every 实现定时更新 支持周期性的健康检查和规则更新 6. 最佳实践建议 配置检查 :确保 jxwaf_config.json 配置文件路径正确且可读写 性能调优 : 启用lua-resty-core 根据实际负载调整定时器间隔 错误监控 : 监控Nginx错误日志中的 ngx.ERR 级别日志 定期检查规则更新是否成功 安全建议 : 保护WEB控制台的API key和密码 定期更新节点UUID 调试技巧 : 使用 ngx.log 输出调试信息 可结合Wireshark抓包分析与控制台的通信