电脑疯子技术论坛|电脑极客社区

微信扫一扫 分享朋友圈

已有 2524 人浏览分享

WAF开发之灰度转发

[复制链接]
2524 0
本帖最后由 zhaorong 于 2021-7-5 10:50 编辑

简介

突然心血来潮想写一下基于lua实现灰度转发的文章。
根据前文内容的openresty处理阶段这一环节 假如要实现灰度流量的转发 需要在balancer这
个阶段进行处理这个阶段类似于nginx的upstream作用域。
  1. upstream backend {
  2.   server test.com  test;
  3. }
复制代码

原生的upstream其实是可以实现灰度流量转发的 但是主要策略是基于权重比例来流量的转发无法
实现颗粒度细的流量转发 为此 我使用lua代码实现了四种灰度流量转发策略:
基于权重比例转发;
基于IP地址转发;
基于地区位置转发;
基于HTTP字段转发(通用);

首先需要了解在这个阶段可以支持的操作有什么内容:
  1. #告诉openresty流量要转到什么后端服务器
  2. syntax:ok, err = balancer.set_current_peer(host, port)
  3. #设置尝试错误次数
  4. syntax:ok, err = balancer.set_more_tries(count)
  5. #获取上次失败的原因,我用来剔除失效的后端服务器
  6. syntax:state_name, status_code = balancer.get_last_failure()
  7. #设置超时时间
  8. syntax:ok, err = balancer.set_timeouts(connect_timeout, send_timeout, read_timeout
复制代码

接着根据上面的内容编写一个转发流量到后端的函数
  1. --ip_lists是一个后端服务器列表,比如(192.168.1.2,192.168.1.3,192.168.1.4),port是固定的,
  2. local function forward_server(ip_lists, port)
  3. --设置错误尝试失败次数
  4.     if not ngx.ctx.tries then
  5.         ngx.ctx.tries = 0
  6.     end
  7. #判断后端服务器列表有多少个,确定重试次数
  8.     if ngx.ctx.tries < #ip_lists then
  9.         local set_more_tries_ok, set_more_tries_err = balancer.set_more_tries(1)
  10.         if not set_more_tries_ok then
  11.             ngx.log(ngx.ERR, "failed to set the current peer: ", set_more_tries_err)
  12.         elseif set_more_tries_err then
  13.             ngx.log(ngx.ALERT, "set more tries: ", set_more_tries_err)
  14.         end
  15.     end

  16.     ngx.ctx.tries = ngx.ctx.tries + 1
  17. #确定有效的后端服务器列表
  18.     if not ngx.ctx.ip_lists then
  19.         ngx.ctx.ip_lists = ip_lists
  20.     end
  21. #确定客户端IP指向一个后端服务器,用于保持会话
  22.     local first_count = {}
  23.     table.insert(first_count, string.sub(ngx.var.remote_addr, 1, 1))
  24.     table.insert(first_count, string.sub(ngx.var.remote_addr, -1))
  25.     local ip_count = (tonumber(table.concat(first_count)) % #ngx.ctx.ip_lists) + 1

  26.     local _host = ngx.ctx.ip_lists[ip_count]
  27.     local state_name, state_code = balancer.get_last_failure()
  28. #剔除失效后端服务器
  29.     if state_name == "failed" then
  30.         for k, v in ipairs(ngx.ctx.ip_lists) do
  31.             if v == _host then
  32.                 if not (#ngx.ctx.ip_lists == 1) then
  33.                     table.remove(ngx.ctx.ip_lists, k)
  34.                     ip_count = (string.sub(ngx.var.remote_addr, -1) % #ngx.ctx.ip_lists) + 1
  35.                     _host = ngx.ctx.ip_lists[ip_count]
  36.                 end
  37.             end
  38.         end
  39.     end
  40. #一切没有问题之后,直接流量转发
  41.     local ok, err = balancer.set_current_peer(_host, port)

  42.     if not ok then
  43.         ngx.log(ngx.ERR, "failed to set the current peer: ", err)
  44.     end
  45. end
复制代码

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

基于权重比例转发策略

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

比如{“192.168.1.20”:“20”,“192.168.1.30”:30}
  1. local weight_list = {}
  2. local iplist = {}
  3. iplist['192.168.1.20']=20
  4. iplist['192.168.1.30']=30

  5. for key,value in pairs(iplist) do
  6.     for i=1, value do
  7.         table.insert(weight_list,key)
  8.      end
  9. end

  10. local random_value = math.random(1, #weight_list)
  11. print(random_value)
  12. print(weight_list[random_value])
复制代码

QQ截图20210705103808.png

基于IP比例转发策略
  1. local iputils = require "resty.waf.iputils"
复制代码

基于iputils 库 用于处理客户端IP的地址转发(支持IP地址和网段)
  1. local ip_forward_list={"192.168.1.2","192.168.20.0/24"}  
  2. local ip_list = iputils.parse_cidrs(ip_forward_list)
  3. if iputils.ip_in_cidrs(remote_ip, ip_list) then   --如果IP段灰度策略符合,转发到灰度服务器
  4.     return  forward_server(gray_server, gray_port)
  5. end
复制代码

基于地区灰度转发策略
  1. local geo = require 'resty.waf.maxminddb'

  2. if not geo.initted() then
  3.     geo.init("/opt/GeoLite2-City.mmdb") --需要在该目录设置geo库
  4. end

  5. local res, err = geo.lookup(remote_ip)

  6. if res then
  7.     if res['city'] then
  8.         local city_name = res['city']['names']['zh-CN']
  9.         --查看当前IP所属的城市
  10.         --ngx.log(ngx.ERR,city_name)
  11.         for _, _value in pairs(region_forward_data) do

  12.             local gray_server = _value['gray_server']
  13.             local gray_port = _value['gray_port']

  14.             local region = _value['content'][1]['content']
  15.             if region == city_name then
  16.                 return forward_server(gray_server, gray_port) --如果地区灰度策略符合,转发到灰度服务器
  17.             end
  18.         end
  19.     end
  20. end
复制代码

基于通用配置转发策略

我是用jxwaf里面的处理HTTP字段代码 就不造轮子 具体开发思路可以参考jxwaf的自定义规则功能
  1. local waf = require "resty.waf.waf"
  2. local request = require "resty.waf.request"
  3. local operator = require "resty.waf.operator"
  4. local transform = require "resty.waf.transform"
复制代码

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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

关注

0

粉丝

9021

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Powered by Pcgho! X3.4

© 2008-2022 Pcgho Inc.