WAF防护

waf防护其本质是对非法流量的过滤,其对应的策略有多种。如CC攻击防护,Sql注入,非法参数拼接,伪造请求等,对于这些非正常流量,不能直接打到我们自己的业务服务中。

对此,tl-ops-manage提供了多种waf防护策略,如 多规则过滤,黑白名单。同时对于被waf拦截的请求返回自定义的错误码和内容

规则预热

对于waf逻辑而言,其核心在于规则的匹配,waf作为流量的入口,应该保持高性能,所以规则的加载和匹配不能阻塞请求的分发,在tl-ops-manage中,由于数据都是存在store文件, (或者redis中)。在实际启动后,对于数据的加载每次都要访问磁盘IO,对性能会有极大的影响,所以tl-ops-manage提供了数据同步和预热功能,只需开启 `sync` 插件即可。

开启后会在启动时执行一次数据同步,并将磁盘数据加载进入shared:dict共享内存中。

waf规则

# 代码位置 : conf/tl_ops_manage.conf

location / {
    ...
    rewrite_by_lua_block {
        tlops:tl_ops_process_init_rewrite();
    }
}

# 代码位置 : tl_ops_manage.lua

function _M:tl_ops_process_init_rewrite()
    ...

    -- 启动全局WAF
    m_waf:init_global(ngx.ctx);

    ...
end

可以看到核心逻辑在 init 方法,我们直接进入init方法的核心流程 tl_ops_waf_global_core 查看,核心流程遵循上面说到的waf执行链,如果某个执行链没有通过,也就是返回false,便会返回对应的自定义配置码。

同时可以看到在被waf拦截后,会对请求头写入一个 Tl-Waf-Mode的标志,用于区分/调试等用处

# 代码位置 : waf/tl_ops_waf.lua


-- 全局waf核心流程
function _M:tl_ops_waf_global_core()

    -- 关闭
    if not tl_ops_manage_env.waf.open then
        return true
    end

    local waf = tl_ops_waf_core_ip.tl_ops_waf_core_ip_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_ip)
        tl_ops_err_content:err_content_rewrite_to_waf("g-ip", tl_ops_constant_waf.cache_key.waf_ip)
        return
    end

    waf = tl_ops_waf_core_api.tl_ops_waf_core_api_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_api)
        tl_ops_err_content:err_content_rewrite_to_waf("g-api", tl_ops_constant_waf.cache_key.waf_api)
        return
    end

    waf = tl_ops_waf_core_cc.tl_ops_waf_core_cc_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_cc)
        tl_ops_err_content:err_content_rewrite_to_waf("g-cc", tl_ops_constant_waf.cache_key.waf_cc)
        return
    end

    waf = tl_ops_waf_core_header.tl_ops_waf_core_header_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_header)
        tl_ops_err_content:err_content_rewrite_to_waf("g-header", tl_ops_constant_waf.cache_key.waf_header)
        return
    end

    waf = tl_ops_waf_core_cookie.tl_ops_waf_core_cookie_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_cookie)
        tl_ops_err_content:err_content_rewrite_to_waf("g-cookie", tl_ops_constant_waf.cache_key.waf_cookie)
        return
    end

    waf = tl_ops_waf_core_param.tl_ops_waf_core_param_filter_global_pass()
    if not waf then
        tl_ops_waf_count:tl_ops_waf_count_incr_key(tl_ops_constant_waf.cache_key.req_param)
        tl_ops_err_content:err_content_rewrite_to_waf("g-param", tl_ops_constant_waf.cache_key.waf_param)
        return
    end

    return true
end