freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

WAF开发之灰度转发
2021-06-24 22:41:46
所属地 广东省

简介

突然心血来潮想写一下基于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']=20
iplist['192.168.1.30']=30

for key,value in pairs(iplist) do
    for i=1, value do
        table.insert(weight_list,key)
     end
end

local random_value = math.random(1, #weight_list)
print(random_value)
print(weight_list[random_value])

1624545216_60d497c0bf54ee74728ed.png!small?1624545217314

基于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
    end
end

基于通用配置转发策略

我是用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开发
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录