From 22b945ec402b6149142e236dcdb14bae4b957fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A5=A5=E8=B6=85?= Date: Mon, 6 Mar 2023 21:49:11 +0800 Subject: [PATCH] =?UTF-8?q?5=E7=A7=92=E7=9B=BE=E6=94=AF=E6=8C=81=E4=BE=8B?= =?UTF-8?q?=E5=A4=96URL=E5=92=8C=E9=99=90=E5=88=B6URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/serverconfigs/http_cc_config.go | 47 ++++++++++ pkg/serverconfigs/http_web_config.go | 11 ++- .../shared/http_header_config.go | 13 +++ pkg/serverconfigs/shared/url_pattern.go | 66 +++++++++++++ pkg/serverconfigs/shared/url_pattern_test.go | 93 +++++++++++++++++++ pkg/serverconfigs/uam_config.go | 43 +++++++++ 6 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 pkg/serverconfigs/http_cc_config.go create mode 100644 pkg/serverconfigs/shared/url_pattern.go create mode 100644 pkg/serverconfigs/shared/url_pattern_test.go diff --git a/pkg/serverconfigs/http_cc_config.go b/pkg/serverconfigs/http_cc_config.go new file mode 100644 index 0000000..64da728 --- /dev/null +++ b/pkg/serverconfigs/http_cc_config.go @@ -0,0 +1,47 @@ +// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package serverconfigs + +import "strings" + +// HTTPCCConfig HTTP CC防护配置 +type HTTPCCConfig struct { + IsPrior bool `yaml:"isPrior" json:"isPrior"` // 是否覆盖父级 + IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用 + WithRequestPath bool `yaml:"withRequestPath" json:"withRequestPath"` // 根据URL路径区分请求 + PeriodSeconds int32 `yaml:"periodSeconds" json:"periodSeconds"` // 计算周期 + MaxRequests int32 `yaml:"maxRequests" json:"maxRequests"` // 请求数最大值 + MaxConnections int32 `yaml:"maxConnections" json:"maxConnections"` // 连接数最大值 + IgnoreCommonFiles bool `yaml:"ignoreCommonFiles" json:"ignoreCommonFiles"` // 忽略常用文件,如CSS、JS等 + IgnoreCommonAgents bool `yaml:"ignoreCommonAgents" json:"ignoreCommonAgents"` // 忽略常见搜索引擎等 + Action string `yaml:"action" json:"action"` // 动作,比如block、captcha等 + + fullKey string +} + +func NewHTTPCCConfig() *HTTPCCConfig { + return &HTTPCCConfig{ + WithRequestPath: false, + PeriodSeconds: 10, + MaxRequests: 60, + MaxConnections: 10, + IgnoreCommonFiles: false, + IgnoreCommonAgents: true, + Action: "captcha", + } +} + +func (this *HTTPCCConfig) Init() error { + // 组合Key + var keys = []string{"${remoteAddr}"} + if this.WithRequestPath { + keys = append(keys, "${requestPath}") + } + this.fullKey = strings.Join(keys, "@") + + return nil +} + +func (this *HTTPCCConfig) Key() string { + return this.fullKey +} diff --git a/pkg/serverconfigs/http_web_config.go b/pkg/serverconfigs/http_web_config.go index d477e91..6c66706 100644 --- a/pkg/serverconfigs/http_web_config.go +++ b/pkg/serverconfigs/http_web_config.go @@ -49,8 +49,9 @@ type HTTPWebConfig struct { RequestLimit *HTTPRequestLimitConfig `yaml:"requestLimit" json:"requestLimit"` // 并发请求限制 RequestScripts *HTTPRequestScriptsConfig `yaml:"requestScripts" json:"requestScripts"` // HTTP请求相关脚本 - // UAM + // UAM, CC ... UAM *UAMConfig `yaml:"uam" json:"uam"` + CC *UAMConfig `yaml:"cc" json:"cc"` } func (this *HTTPWebConfig) Init() error { @@ -284,6 +285,14 @@ func (this *HTTPWebConfig) Init() error { } } + // cc + if this.CC != nil { + err := this.CC.Init() + if err != nil { + return err + } + } + // user agent if this.UserAgent != nil { err := this.UserAgent.Init() diff --git a/pkg/serverconfigs/shared/http_header_config.go b/pkg/serverconfigs/shared/http_header_config.go index 65d9bc5..ddf19ae 100644 --- a/pkg/serverconfigs/shared/http_header_config.go +++ b/pkg/serverconfigs/shared/http_header_config.go @@ -103,3 +103,16 @@ func (this *HTTPHeaderConfig) Init() error { func (this *HTTPHeaderConfig) HasVariables() bool { return this.hasVariables } + +// Match 判断是否匹配状态码 +func (this *HTTPHeaderConfig) Match(statusCode int) bool { + if !this.IsOn { + return false + } + + if this.Status == nil { + return false + } + + return this.Status.Match(statusCode) +} diff --git a/pkg/serverconfigs/shared/url_pattern.go b/pkg/serverconfigs/shared/url_pattern.go new file mode 100644 index 0000000..6dcd237 --- /dev/null +++ b/pkg/serverconfigs/shared/url_pattern.go @@ -0,0 +1,66 @@ +// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package shared + +import ( + "errors" + "regexp" + "strings" +) + +type URLPatternType = string + +const ( + URLPatternTypeWildcard URLPatternType = "wildcard" // 通配符 + URLPatternTypeRegexp URLPatternType = "regexp" // 正则表达式 +) + +type URLPattern struct { + Type URLPatternType `yaml:"type" json:"type"` + Pattern string `yaml:"pattern" json:"pattern"` + + reg *regexp.Regexp +} + +func (this *URLPattern) Init() error { + if len(this.Pattern) == 0 { + return nil + } + + switch this.Type { + case URLPatternTypeWildcard: + // 只支持星号 + var pieces = strings.Split(this.Pattern, "*") + for index, piece := range pieces { + pieces[index] = regexp.QuoteMeta(piece) + } + reg, err := regexp.Compile("(?i)" /** 大小写不敏感 **/ + strings.Join(pieces, "(.*)")) + if err != nil { + return err + } + this.reg = reg + case URLPatternTypeRegexp: + var pattern = this.Pattern + if !strings.HasPrefix(pattern, "(?i)") { // 大小写不敏感 + pattern = "(?i)" + pattern + } + reg, err := regexp.Compile(pattern) + if err != nil { + return errors.New("compile '" + pattern + "' failed: " + err.Error()) + } + this.reg = reg + } + + return nil +} + +func (this *URLPattern) Match(url string) bool { + if len(this.Pattern) == 0 && len(url) == 0 { + return true + } + + if this.reg != nil { + return this.reg.MatchString(url) + } + return false +} diff --git a/pkg/serverconfigs/shared/url_pattern_test.go b/pkg/serverconfigs/shared/url_pattern_test.go new file mode 100644 index 0000000..1e7fa43 --- /dev/null +++ b/pkg/serverconfigs/shared/url_pattern_test.go @@ -0,0 +1,93 @@ +// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package shared_test + +import ( + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" + "github.com/iwind/TeaGo/assert" + "testing" +) + +func TestURLPattern_Match(t *testing.T) { + var a = assert.NewAssertion(t) + + type unitTest struct { + patternType string + pattern string + url string + result bool + } + + for _, ut := range []*unitTest{ + { + patternType: "wildcard", + pattern: "*", + url: "https://example.com", + result: true, + }, + { + patternType: "wildcard", + pattern: "https://example*", + url: "https://example.com", + result: true, + }, + { + patternType: "wildcard", + pattern: "*com", + url: "https://example.com", + result: true, + }, + { + patternType: "wildcard", + pattern: "*COM", + url: "https://example.com", + result: true, + }, + { + patternType: "wildcard", + pattern: "http://*", + url: "https://example.com", + result: false, + }, + { + patternType: "regexp", + pattern: ".*", + url: "https://example.com", + result: true, + }, + { + patternType: "regexp", + pattern: "^https://.*", + url: "https://example.com", + result: true, + }, + { + patternType: "regexp", + pattern: "^https://.*EXAMPLE.COM", + url: "https://example.com", + result: true, + }, + { + patternType: "regexp", + pattern: "(?i)https://.*EXAMPLE.COM/\\d+", + url: "https://example.com/123456", + result: true, + }, + { + patternType: "regexp", + pattern: "(?i)https://.*EXAMPLE.COM/\\d+$", + url: "https://example.com/123456/789", + result: false, + }, + } { + var p = &shared.URLPattern{ + Type: ut.patternType, + Pattern: ut.pattern, + } + err := p.Init() + if err != nil { + t.Fatal(err) + } + a.IsTrue(p.Match(ut.url) == ut.result) + } +} diff --git a/pkg/serverconfigs/uam_config.go b/pkg/serverconfigs/uam_config.go index 5fbd060..01a5987 100644 --- a/pkg/serverconfigs/uam_config.go +++ b/pkg/serverconfigs/uam_config.go @@ -2,12 +2,55 @@ package serverconfigs +import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" + // UAMConfig UAM配置 type UAMConfig struct { IsPrior bool `yaml:"isPrior" json:"isPrior"` IsOn bool `yaml:"isOn" json:"isOn"` + + OnlyURLPatterns []*shared.URLPattern `yaml:"onlyURLPatterns" json:"onlyURLPatterns"` // 仅限的URL + ExceptURLPatterns []*shared.URLPattern `yaml:"exceptURLPatterns" json:"exceptURLPatterns"` // 排除的URL } func (this *UAMConfig) Init() error { + // only urls + for _, pattern := range this.OnlyURLPatterns { + err := pattern.Init() + if err != nil { + return err + } + } + + // except urls + for _, pattern := range this.ExceptURLPatterns { + err := pattern.Init() + if err != nil { + return err + } + } + return nil } + +func (this *UAMConfig) MatchURL(url string) bool { + // except + if len(this.ExceptURLPatterns) > 0 { + for _, pattern := range this.ExceptURLPatterns { + if pattern.Match(url) { + return false + } + } + } + + if len(this.OnlyURLPatterns) > 0 { + for _, pattern := range this.OnlyURLPatterns { + if pattern.Match(url) { + return true + } + } + return false + } + + return true +}