Files
EdgeNode/internal/waf/checkpoints/cc.go

211 lines
4.6 KiB
Go
Raw Normal View History

2020-10-08 15:06:42 +08:00
package checkpoints
import (
2020-11-22 12:11:39 +08:00
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
2020-10-08 15:06:42 +08:00
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
"sync"
2020-11-22 12:11:39 +08:00
"time"
2020-10-08 15:06:42 +08:00
)
2021-07-18 15:51:49 +08:00
// CCCheckpoint ${cc.arg}
2020-10-08 15:06:42 +08:00
// TODO implement more traffic rules
type CCCheckpoint struct {
Checkpoint
2020-11-22 12:11:39 +08:00
cache *ttlcache.Cache
once sync.Once
2020-10-08 15:06:42 +08:00
}
func (this *CCCheckpoint) Init() {
}
func (this *CCCheckpoint) Start() {
2020-11-22 12:11:39 +08:00
if this.cache != nil {
this.cache.Destroy()
2020-10-08 15:06:42 +08:00
}
2020-11-22 12:11:39 +08:00
this.cache = ttlcache.NewCache()
2020-10-08 15:06:42 +08:00
}
func (this *CCCheckpoint) RequestValue(req requests.Request, param string, options maps.Map, ruleId int64) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
2020-10-08 15:06:42 +08:00
value = 0
2020-11-22 12:11:39 +08:00
if this.cache == nil {
2020-10-08 15:06:42 +08:00
this.once.Do(func() {
this.Start()
})
2020-11-22 12:11:39 +08:00
if this.cache == nil {
2020-10-08 15:06:42 +08:00
return
}
}
periodString, ok := options["period"]
if !ok {
return
}
period := types.Int64(periodString)
if period < 1 {
return
}
v, _ := options["userType"]
userType := types.String(v)
v, _ = options["userField"]
userField := types.String(v)
v, _ = options["userIndex"]
userIndex := types.Int(v)
if param == "requests" { // requests
var key = ""
switch userType {
case "ip":
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
case "cookie":
if len(userField) == 0 {
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
} else {
2021-07-18 15:51:49 +08:00
cookie, _ := req.WAFRaw().Cookie(userField)
2020-10-08 15:06:42 +08:00
if cookie != nil {
v := cookie.Value
if userIndex > 0 && len(v) > userIndex {
v = v[userIndex:]
}
key = "USER@" + userType + "@" + userField + "@" + v
}
}
case "get":
if len(userField) == 0 {
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
} else {
2021-07-18 15:51:49 +08:00
v := req.WAFRaw().URL.Query().Get(userField)
2020-10-08 15:06:42 +08:00
if userIndex > 0 && len(v) > userIndex {
v = v[userIndex:]
}
key = "USER@" + userType + "@" + userField + "@" + v
}
case "post":
if len(userField) == 0 {
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
} else {
2021-07-18 15:51:49 +08:00
v := req.WAFRaw().PostFormValue(userField)
2020-10-08 15:06:42 +08:00
if userIndex > 0 && len(v) > userIndex {
v = v[userIndex:]
}
key = "USER@" + userType + "@" + userField + "@" + v
}
case "header":
if len(userField) == 0 {
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
} else {
2021-07-18 15:51:49 +08:00
v := req.WAFRaw().Header.Get(userField)
2020-10-08 15:06:42 +08:00
if userIndex > 0 && len(v) > userIndex {
v = v[userIndex:]
}
key = "USER@" + userType + "@" + userField + "@" + v
}
default:
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
}
if len(key) == 0 {
2021-07-18 15:51:49 +08:00
key = req.WAFRemoteIP()
2020-10-08 15:06:42 +08:00
}
value = this.cache.IncreaseInt64(types.String(ruleId)+"@"+key, int64(1), time.Now().Unix()+period, false)
2020-10-08 15:06:42 +08:00
}
return
}
func (this *CCCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map, ruleId int64) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
2020-10-08 15:06:42 +08:00
if this.IsRequest() {
return this.RequestValue(req, param, options, ruleId)
2020-10-08 15:06:42 +08:00
}
return
}
func (this *CCCheckpoint) ParamOptions() *ParamOptions {
option := NewParamOptions()
option.AddParam("请求数", "requests")
return option
}
func (this *CCCheckpoint) Options() []OptionInterface {
options := []OptionInterface{}
// period
{
option := NewFieldOption("统计周期", "period")
option.Value = "60"
option.RightLabel = "秒"
option.Size = 8
option.MaxLength = 8
option.Validate = func(value string) (ok bool, message string) {
if regexp.MustCompile("^\\d+$").MatchString(value) {
ok = true
return
}
message = "周期需要是一个整数数字"
return
}
options = append(options, option)
}
// type
{
option := NewOptionsOption("用户识别读取来源", "userType")
option.Size = 10
option.SetOptions([]maps.Map{
{
"name": "IP",
"value": "ip",
},
{
"name": "Cookie",
"value": "cookie",
},
{
"name": "URL参数",
"value": "get",
},
{
"name": "POST参数",
"value": "post",
},
{
"name": "HTTP Header",
"value": "header",
},
})
options = append(options, option)
}
// user field
{
option := NewFieldOption("用户识别字段", "userField")
option.Comment = "识别用户的唯一性字段在用户读取来源不是IP时使用"
options = append(options, option)
}
// user value index
{
option := NewFieldOption("字段读取位置", "userIndex")
option.Size = 5
option.MaxLength = 5
option.Comment = "读取用户识别字段的位置从0开始比如user12345的数字ID 12345的位置就是5在用户读取来源不是IP时使用"
options = append(options, option)
}
return options
}
func (this *CCCheckpoint) Stop() {
2020-11-22 12:11:39 +08:00
if this.cache != nil {
this.cache.Destroy()
this.cache = nil
2020-10-08 15:06:42 +08:00
}
}