mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2025-12-06 06:58:54 +08:00
Merge pull request #518 from samwafgo/feat_anticc_condition
feat:add anticc condition
This commit is contained in:
@@ -11,18 +11,35 @@ import (
|
||||
"SamWaf/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WafAntiCCApi struct {
|
||||
}
|
||||
|
||||
func (w *WafAntiCCApi) AddApi(c *gin.Context) {
|
||||
ruleHelper := &utils.RuleHelper{}
|
||||
var req request.WafAntiCCAddReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
// 检查是否启用了前置规则
|
||||
if req.IsEnableRule {
|
||||
// 检查规则内容是否为空
|
||||
if req.RuleContent == "" {
|
||||
response.FailWithMessage("前置规则内容不能为空", c)
|
||||
return
|
||||
}
|
||||
// 检查规则内容是否合法
|
||||
err = ruleHelper.CheckRuleAvailable(req.RuleContent)
|
||||
if err != nil {
|
||||
response.FailWithMessage("前置规则校验失败", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = wafAntiCCService.CheckIsExistApi(req)
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = wafAntiCCService.AddApi(req)
|
||||
@@ -139,9 +156,25 @@ func (w *WafAntiCCApi) DelAntiCCApi(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (w *WafAntiCCApi) ModifyAntiCCApi(c *gin.Context) {
|
||||
ruleHelper := &utils.RuleHelper{}
|
||||
var req request.WafAntiCCEditReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
// 检查是否启用了前置规则
|
||||
if req.IsEnableRule {
|
||||
// 检查规则内容是否为空
|
||||
if req.RuleContent == "" {
|
||||
response.FailWithMessage("前置规则内容不能为空", c)
|
||||
return
|
||||
}
|
||||
// 检查规则内容是否合法
|
||||
err = ruleHelper.CheckRuleAvailable(req.RuleContent)
|
||||
if err != nil {
|
||||
response.FailWithMessage("前置规则校验失败", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = wafAntiCCService.ModifyApi(req)
|
||||
if err != nil {
|
||||
response.FailWithMessage("编辑发生错误", c)
|
||||
|
||||
24
main.go
24
main.go
@@ -24,16 +24,10 @@ import (
|
||||
"SamWaf/wafsnowflake"
|
||||
"SamWaf/waftask"
|
||||
"SamWaf/waftunnelengine"
|
||||
"SamWaf/webplugin"
|
||||
"crypto/tls"
|
||||
"embed"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
dlp "github.com/bytedance/godlp"
|
||||
"github.com/kardianos/service"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/time/rate"
|
||||
"gorm.io/gorm"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
@@ -50,6 +44,11 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
dlp "github.com/bytedance/godlp"
|
||||
"github.com/kardianos/service"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//go:embed exedata/ip2region.xdb
|
||||
@@ -434,18 +433,7 @@ func (m *wafSystenService) run() {
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].Mux.Unlock()
|
||||
break
|
||||
case enums.ChanTypeAnticc:
|
||||
antiCC := msg.Content.(model.AntiCC)
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].Mux.Lock()
|
||||
if antiCC.Id == "" {
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].PluginIpRateLimiter = nil
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].AntiCCBean = antiCC
|
||||
} else {
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].PluginIpRateLimiter = webplugin.NewIPRateLimiter(rate.Limit(msg.Content.(model.AntiCC).Rate), msg.Content.(model.AntiCC).Limit)
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].AntiCCBean = antiCC
|
||||
}
|
||||
|
||||
zlog.Debug("远程配置", zap.Any("Anticc", msg.Content.(model.AntiCC)))
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].Mux.Unlock()
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.ApplyAntiCCConfig(msg.HostCode, msg.Content.(model.AntiCC))
|
||||
break
|
||||
case enums.ChanTypeHttpauth:
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostCode[msg.HostCode]].Mux.Lock()
|
||||
|
||||
@@ -16,6 +16,8 @@ type AntiCC struct {
|
||||
LockIPMinutes int `json:"lock_ip_minutes" gorm:"column:lock_ip_minutes"`
|
||||
LimitMode string `json:"limit_mode" gorm:"column:limit_mode"` // "rate" 或 "window"
|
||||
IPMode string `json:"ip_mode" gorm:"column:ip_mode"` // "nic" 网卡模式 或 "proxy" 代理模式
|
||||
Url string `json:"url"` //保护的url
|
||||
Remarks string `json:"remarks"` //备注
|
||||
Url string `json:"url"`
|
||||
IsEnableRule bool `json:"is_enable_rule" gorm:"column:is_enable_rule"` //是否启动规则
|
||||
RuleContent string `json:"rule_content" gorm:"column:rule_content"` //规则内容
|
||||
Remarks string `json:"remarks"` //备注
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ type WafAntiCCAddReq struct {
|
||||
LockIPMinutes int `json:"lock_ip_minutes" form:"lock_ip_minutes"` //封禁分钟
|
||||
LimitMode string `json:"limit_mode" form:"limit_mode"` // "rate" 或 "window"
|
||||
IPMode string `json:"ip_mode" form:"ip_mode"` // "nic" 网卡模式 或 "proxy" 代理模式
|
||||
IsEnableRule bool `json:"is_enable_rule" form:"is_enable_rule"` //是否启动规则
|
||||
RuleContent string `json:"rule_content" form:"rule_content"` //规则内容
|
||||
Remarks string `json:"remarks" form:"remarks"` //备注
|
||||
}
|
||||
type WafAntiCCSearchReq struct {
|
||||
@@ -31,6 +33,8 @@ type WafAntiCCEditReq struct {
|
||||
LockIPMinutes int `json:"lock_ip_minutes" form:"lock_ip_minutes"` //封禁分钟
|
||||
LimitMode string `json:"limit_mode" form:"limit_mode"` // "rate" 或 "window"
|
||||
IPMode string `json:"ip_mode" form:"ip_mode"` // "nic" 网卡模式 或 "proxy" 代理模式
|
||||
IsEnableRule bool `json:"is_enable_rule" form:"is_enable_rule"` //是否启动规则
|
||||
RuleContent string `json:"rule_content" form:"rule_content"` //规则内容
|
||||
Remarks string `json:"remarks" form:"remarks"` //备注
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ func (receiver *WafAntiCCService) AddApi(req request.WafAntiCCAddReq) error {
|
||||
Remarks: req.Remarks,
|
||||
LimitMode: req.LimitMode,
|
||||
IPMode: req.IPMode,
|
||||
IsEnableRule: req.IsEnableRule,
|
||||
RuleContent: req.RuleContent,
|
||||
}
|
||||
global.GWAF_LOCAL_DB.Create(bean)
|
||||
return nil
|
||||
@@ -64,6 +66,8 @@ func (receiver *WafAntiCCService) ModifyApi(req request.WafAntiCCEditReq) error
|
||||
"UPDATE_TIME": customtype.JsonTime(time.Now()),
|
||||
"LimitMode": req.LimitMode,
|
||||
"IPMode": req.IPMode,
|
||||
"IsEnableRule": req.IsEnableRule,
|
||||
"RuleContent": req.RuleContent,
|
||||
}
|
||||
err := global.GWAF_LOCAL_DB.Model(model.AntiCC{}).Where("id = ?", req.Id).Updates(ipWhiteMap).Error
|
||||
|
||||
|
||||
@@ -26,6 +26,16 @@ func (rulehelper *RuleHelper) InitRuleEngine() {
|
||||
rulehelper.ruleBuilder = builder.NewRuleBuilder(rulehelper.knowledgeLibrary)
|
||||
rulehelper.engine = engine.NewGruleEngine()
|
||||
}
|
||||
func (rulehelper *RuleHelper) LoadRuleString(ruleContent string) error {
|
||||
|
||||
byteArr := pkg.NewBytesResource([]byte(ruleContent))
|
||||
err := rulehelper.ruleBuilder.BuildRuleFromResource("Region", "0.0.1", byteArr)
|
||||
if err != nil {
|
||||
zlog.Error("LoadRule", err)
|
||||
}
|
||||
rulehelper.KnowledgeBase, err = rulehelper.knowledgeLibrary.NewKnowledgeBaseInstance("Region", "0.0.1")
|
||||
return err
|
||||
}
|
||||
func (rulehelper *RuleHelper) LoadRule(ruleconfig model.Rules) error {
|
||||
|
||||
byteArr := pkg.NewBytesResource([]byte(ruleconfig.RuleContent))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package wafenginecore
|
||||
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/enums"
|
||||
"SamWaf/global"
|
||||
"SamWaf/innerbean"
|
||||
@@ -32,15 +33,34 @@ func (waf *WafEngine) CheckCC(r *http.Request, weblogbean *innerbean.WebLog, for
|
||||
// 默认使用网卡模式
|
||||
clientIP = weblogbean.NetSrcIp
|
||||
}
|
||||
|
||||
if !hostTarget.PluginIpRateLimiter.Allow(clientIP) {
|
||||
weblogbean.RISK_LEVEL = 1
|
||||
result.IsBlock = true
|
||||
result.Title = "【局部】触发IP频次访问限制"
|
||||
result.Content = "您的访问被阻止超量了"
|
||||
cacheKey := enums.CACHE_CCVISITBAN_PRE + clientIP
|
||||
//将该IP添加到封禁里
|
||||
global.GCACHE_WAFCACHE.SetWithTTl(cacheKey, 1, time.Duration(hostTarget.AntiCCBean.LockIPMinutes)*time.Minute)
|
||||
isCheckCC := false
|
||||
if hostTarget.AntiCCBean.IsEnableRule {
|
||||
if hostTarget.PluginIpRateLimiter.Rule != nil {
|
||||
if hostTarget.PluginIpRateLimiter.Rule.KnowledgeBase != nil {
|
||||
ruleMatchs, err := hostTarget.PluginIpRateLimiter.Rule.Match("MF", weblogbean)
|
||||
if err == nil {
|
||||
if len(ruleMatchs) > 0 {
|
||||
isCheckCC = true
|
||||
zlog.Debug("CheckCC ruleMatchs: %v", ruleMatchs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isCheckCC = true
|
||||
}
|
||||
if isCheckCC {
|
||||
if !hostTarget.PluginIpRateLimiter.Allow(clientIP) {
|
||||
weblogbean.RISK_LEVEL = 1
|
||||
result.IsBlock = true
|
||||
result.Title = "【局部】触发IP频次访问限制"
|
||||
result.Content = "您的访问被阻止超量了"
|
||||
cacheKey := enums.CACHE_CCVISITBAN_PRE + clientIP
|
||||
//将该IP添加到封禁里
|
||||
global.GCACHE_WAFCACHE.SetWithTTl(cacheKey, 1, time.Duration(hostTarget.AntiCCBean.LockIPMinutes)*time.Minute)
|
||||
return result
|
||||
}
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,13 @@ import (
|
||||
"SamWaf/wafenginecore/wafhttpserver"
|
||||
"SamWaf/wafenginecore/wafwebcache"
|
||||
"SamWaf/wafproxy"
|
||||
"SamWaf/webplugin"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/pires/go-proxyproto"
|
||||
goahocorasick "github.com/samwafgo/ahocorasick"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -38,6 +36,11 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
goahocorasick "github.com/samwafgo/ahocorasick"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type WafEngine struct {
|
||||
@@ -1431,3 +1434,42 @@ func (waf *WafEngine) ClearCcWindowsForIP(ip string) {
|
||||
zap.String("ip", ip),
|
||||
zap.Int("hostCount", hostCount))
|
||||
}
|
||||
func (waf *WafEngine) ApplyAntiCCConfig(hostCode string, antiCC model.AntiCC) {
|
||||
targetKey, ok := waf.HostCode[hostCode]
|
||||
if !ok {
|
||||
zlog.Debug("Anticc reload skip: hostCode not found", zap.String("hostCode", hostCode))
|
||||
return
|
||||
}
|
||||
hostSafe, ok := waf.HostTarget[targetKey]
|
||||
if !ok {
|
||||
zlog.Debug("Anticc reload skip: hostTarget not found", zap.String("targetKey", targetKey))
|
||||
return
|
||||
}
|
||||
|
||||
hostSafe.Mux.Lock()
|
||||
defer hostSafe.Mux.Unlock()
|
||||
|
||||
if antiCC.Id == "" {
|
||||
// 关闭CC防护
|
||||
hostSafe.PluginIpRateLimiter = nil
|
||||
hostSafe.AntiCCBean = antiCC
|
||||
zlog.Debug("Anticc disabled", zap.String("hostCode", hostCode))
|
||||
return
|
||||
}
|
||||
|
||||
// 与初始化逻辑保持一致:支持滑动窗口/平均速率
|
||||
if antiCC.LimitMode == "window" {
|
||||
hostSafe.PluginIpRateLimiter = webplugin.NewWindowIPRateLimiter(antiCC.Rate, antiCC.Limit)
|
||||
} else {
|
||||
hostSafe.PluginIpRateLimiter = webplugin.NewIPRateLimiter(rate.Limit(antiCC.Rate), antiCC.Limit)
|
||||
}
|
||||
if antiCC.IsEnableRule {
|
||||
hostSafe.PluginIpRateLimiter.Rule = &utils.RuleHelper{}
|
||||
hostSafe.PluginIpRateLimiter.Rule.InitRuleEngine()
|
||||
hostSafe.PluginIpRateLimiter.Rule.LoadRuleString(antiCC.RuleContent)
|
||||
}
|
||||
hostSafe.AntiCCBean = antiCC
|
||||
|
||||
zlog.Debug("远程配置", zap.Any("Anticc", antiCC))
|
||||
// ... existing code ...
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
goahocorasick "github.com/samwafgo/ahocorasick"
|
||||
"golang.org/x/time/rate"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
goahocorasick "github.com/samwafgo/ahocorasick"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// 加载全部host
|
||||
@@ -162,6 +163,12 @@ func (waf *WafEngine) LoadHost(inHost model.Hosts) []innerbean.ServerRunTime {
|
||||
zlog.Debug(fmt.Sprintf("初始化CC防护(平均速率模式) 主机%v 时间窗口(秒)%v 最大请求数%v 每秒速率%v",
|
||||
inHost.Host, anticcBean.Rate, anticcBean.Limit, float64(anticcBean.Limit)/float64(anticcBean.Rate)))
|
||||
}
|
||||
if anticcBean.IsEnableRule {
|
||||
pluginIpRateLimiter.Rule = &utils.RuleHelper{}
|
||||
pluginIpRateLimiter.Rule.InitRuleEngine()
|
||||
pluginIpRateLimiter.Rule.LoadRuleString(anticcBean.RuleContent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//查询ip白名单
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package webplugin
|
||||
|
||||
import (
|
||||
"SamWaf/utils"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -26,6 +27,7 @@ type IPRateLimiter struct {
|
||||
mode LimitMode
|
||||
window int // 时间窗口大小(秒)
|
||||
requests map[string][]time.Time // 用于滑动窗口模式记录请求时间
|
||||
Rule *utils.RuleHelper
|
||||
}
|
||||
|
||||
// NewIPRateLimiter 创建一个新的IP限流器
|
||||
|
||||
Reference in New Issue
Block a user