WAF开发之灰度转发

VSole2021-07-02 17:01:02

简介

突然心血来潮想写一下基于lua实现灰度转发的文章。

根据前文内容的openresty处理阶段这一环节,假如要实现灰度流量的转发,需要在balancer这个阶段进行处理。这个阶段类似于nginx的upstream作用域

upstream backend {  server test.com  test;}

原生的upstream其实是可以实现灰度流量转发的,但是主要策略是基于权重比例来流量的转发,无法实现颗粒度细的流量转发,为此,我使用lua代码实现了四种灰度流量转发策略:

基于权重比例转发;
基于IP地址转发;
基于地区位置转发;
基于HTTP字段转发(通用);

首先需要了解在这个阶段可以支持的操作有什么内容:

#告诉openresty流量要转到什么后端服务器syntax:ok, err = balancer.set_current_peer(host, port)
#设置尝试错误次数syntax:ok, err = balancer.set_more_tries(count)
#获取上次失败的原因,我用来剔除失效的后端服务器syntax:state_name, status_code = balancer.get_last_failure()
#设置超时时间syntax:ok, err = balancer.set_timeouts(connect_timeout, send_timeout, read_timeout

接着根据上面的内容编写一个转发流量到后端的函数

--ip_lists是一个后端服务器列表,比如(192.168.1.2,192.168.1.3,192.168.1.4),port是固定的,local function forward_server(ip_lists, port)--设置错误尝试失败次数    if not ngx.ctx.tries then        ngx.ctx.tries = 0    end#判断后端服务器列表有多少个,确定重试次数    if ngx.ctx.tries < #ip_lists then        local set_more_tries_ok, set_more_tries_err = balancer.set_more_tries(1)        if not set_more_tries_ok then            ngx.log(ngx.ERR, "failed to set the current peer: ", set_more_tries_err)        elseif set_more_tries_err then            ngx.log(ngx.ALERT, "set more tries: ", set_more_tries_err)        end    end
    ngx.ctx.tries = ngx.ctx.tries + 1#确定有效的后端服务器列表    if not ngx.ctx.ip_lists then        ngx.ctx.ip_lists = ip_lists    end#确定客户端IP指向一个后端服务器,用于保持会话    local first_count = {}    table.insert(first_count, string.sub(ngx.var.remote_addr, 1, 1))    table.insert(first_count, string.sub(ngx.var.remote_addr, -1))    local ip_count = (tonumber(table.concat(first_count)) % #ngx.ctx.ip_lists) + 1
    local _host = ngx.ctx.ip_lists[ip_count]    local state_name, state_code = balancer.get_last_failure()#剔除失效后端服务器    if state_name == "failed" then        for k, v in ipairs(ngx.ctx.ip_lists) do            if v == _host then                if not (#ngx.ctx.ip_lists == 1) then                    table.remove(ngx.ctx.ip_lists, k)                    ip_count = (string.sub(ngx.var.remote_addr, -1) % #ngx.ctx.ip_lists) + 1                    _host = ngx.ctx.ip_lists[ip_count]                end            end        end    end#一切没有问题之后,直接流量转发    local ok, err = balancer.set_current_peer(_host, port)
    if not ok then        ngx.log(ngx.ERR, "failed to set the current peer: ", err)    end
end

最后聊聊四个灰度转发策略的编写

基于权重比例转发策略

基于权重比例比较简单,就是使用随机数落到那一个后端服务器里面。

比如{“192.168.1.20”:“20”,“192.168.1.30”:30}

local weight_list = {}local iplist = {}iplist['192.168.1.20']=20iplist['192.168.1.30']=30
for key,value in pairs(iplist) do    for i=1, value do        table.insert(weight_list,key)     endend
local random_value = math.random(1, #weight_list)print(random_value)print(weight_list[random_value])

基于IP比例转发策略

local iputils = require "resty.waf.iputils"

基于iputils 库,用于处理客户端IP的地址转发(支持IP地址和网段)

local ip_forward_list={"192.168.1.2","192.168.20.0/24"}local ip_list = iputils.parse_cidrs(ip_forward_list)
if iputils.ip_in_cidrs(remote_ip, ip_list) then   --如果IP段灰度策略符合,转发到灰度服务器    return  forward_server(gray_server, gray_port)end

基于地区灰度转发策略

local geo = require 'resty.waf.maxminddb'
if not geo.initted() then    geo.init("/opt/GeoLite2-City.mmdb") --需要在该目录设置geo库end
local res, err = geo.lookup(remote_ip)
if res then    if res['city'] then        local city_name = res['city']['names']['zh-CN']        --查看当前IP所属的城市        --ngx.log(ngx.ERR,city_name)        for _, _value in pairs(region_forward_data) do
            local gray_server = _value['gray_server']            local gray_port = _value['gray_port']
            local region = _value['content'][1]['content']            if region == city_name then                return forward_server(gray_server, gray_port) --如果地区灰度策略符合,转发到灰度服务器            end        end    endend

基于通用配置转发策略

我是用jxwaf里面的处理HTTP字段代码,就不造轮子,具体开发思路可以参考jxwaf的自定义规则功能

local waf = require "resty.waf.waf"local request = require "resty.waf.request"local operator = require "resty.waf.operator"local transform = require "resty.waf.transform"

PS:有人对CC防御的模块开发有兴趣吗?

waf
本作品采用《CC 协议》,转载必须注明作者和本文链接
0x00 简介WAFNinja 简介WAFNinja 是一款采用Python编写的命令行工具。它通过自动化步骤来帮助渗透测试者来绕过WAF,而这些步骤对于绕过输入验证来说是必需的。
WAF指纹识别工具
2022-04-11 06:18:47
原理发送正常的 HTTP 请求并分析响应;这确定了许多 WAF 解决方案。如果不成功,则发送多个HTTP 请求,并使用简单的逻辑来示例就是WAF
WAF绕过工具
2022-03-02 06:34:03
Nemesida WAF 团队 ( nemesida-waf.com ) 的 WAF Bypass 是一个开源工具 (Python3),用于使用预定义的有效负载检查任何 WAF 的误报/误报数量(如果需要,可以更改有效负载集)。使用前关闭禁用模式。 为内部需求开发的脚本,包括用于测试 Nemesis WAF 和 Nemesida WAF Free,但您可以使用它来测试任何 WAF
如果流量都没有经过WAFWAF当然无法拦截攻击请求。当前多数云WAF架构,例如百度云加速、阿里云盾等,通过更改DNS解析,把流量引入WAF集群,流量经过检测后转发请求到源站。如图,dict.com接入接入WAF后,dict.com的DNS解析结果指向WAF集群,用户的请求将发送给WAF集群,WAF集群经过检测认为非攻击请求再转发给源站。
WAF分为非嵌入型WAF和嵌入型WAF,非嵌入型指的是硬WAF、云WAF、虚拟机WAF之类的;嵌入型指的是web容器模块类型WAF、代码层WAF
Bypass安全狗MySQL注入
一般的做法,是解绑域名,再到web服务上绑定该域名。也就是说,规则引擎分为两块,对请求过滤和对响应过滤,而对请求过滤分为两大步,网络层过滤和应用层过滤。
WAF分类及绕过思路
2021-11-26 05:17:12
WAF分为非嵌入型WAF和嵌入型WAF,非嵌入型指的是硬WAF、云WAF、虚拟机WAF之类的;嵌入型指的是web容器模块类型WAF、代码层WAF
WAF CDN 的识别方法
2021-11-22 07:37:57
CDN 是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。
VSole
网络安全专家