1.4.5.2
This commit is contained in:
13
EdgeCommon/pkg/serverconfigs/access_log_queue_config.go
Normal file
13
EdgeCommon/pkg/serverconfigs/access_log_queue_config.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// AccessLogQueueConfig 访问日志队列配置
|
||||
type AccessLogQueueConfig struct {
|
||||
MaxLength int `yaml:"maxLength" json:"maxLength"` // 队列最大长度
|
||||
CountPerSecond int `yaml:"countPerSecond" json:"countPerSecond"` // 每秒写入数量
|
||||
Percent int `yaml:"percent" json:"percent"` // 比例,如果为0-100,默认为100
|
||||
|
||||
EnableAutoPartial bool `yaml:"enableAutoPartial" json:"enableAutoPartial"` // 是否启动自动分表
|
||||
RowsPerTable int64 `yaml:"rowsPerTable" json:"rowsPerTable"` // 单表最大行数
|
||||
}
|
||||
10
EdgeCommon/pkg/serverconfigs/access_log_storage_command.go
Normal file
10
EdgeCommon/pkg/serverconfigs/access_log_storage_command.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// AccessLogCommandStorageConfig 通过命令行存储
|
||||
type AccessLogCommandStorageConfig struct {
|
||||
Command string `yaml:"command" json:"command"`
|
||||
Args []string `yaml:"args" json:"args"`
|
||||
Dir string `yaml:"dir" json:"dir"`
|
||||
}
|
||||
13
EdgeCommon/pkg/serverconfigs/access_log_storage_es.go
Normal file
13
EdgeCommon/pkg/serverconfigs/access_log_storage_es.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// AccessLogESStorageConfig ElasticSearch存储策略
|
||||
type AccessLogESStorageConfig struct {
|
||||
Endpoint string `yaml:"endpoint" json:"endpoint"`
|
||||
Index string `yaml:"index" json:"index"`
|
||||
MappingType string `yaml:"mappingType" json:"mappingType"`
|
||||
Username string `yaml:"username" json:"username"`
|
||||
Password string `yaml:"password" json:"password"`
|
||||
IsDataStream bool `yaml:"isDataStream" json:"isDataStream"` // 是否为Data Stream模式
|
||||
}
|
||||
9
EdgeCommon/pkg/serverconfigs/access_log_storage_file.go
Normal file
9
EdgeCommon/pkg/serverconfigs/access_log_storage_file.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// AccessLogFileStorageConfig 文件存储配置
|
||||
type AccessLogFileStorageConfig struct {
|
||||
Path string `yaml:"path" json:"path"` // 文件路径,支持变量:${year|month|week|day|hour|minute|second}
|
||||
AutoCreate bool `yaml:"autoCreate" json:"autoCreate"` // 是否自动创建目录
|
||||
}
|
||||
85
EdgeCommon/pkg/serverconfigs/access_log_storage_syslog.go
Normal file
85
EdgeCommon/pkg/serverconfigs/access_log_storage_syslog.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
type AccessLogSyslogStorageProtocol = string
|
||||
|
||||
const (
|
||||
AccessLogSyslogStorageProtocolTCP AccessLogSyslogStorageProtocol = "tcp"
|
||||
AccessLogSyslogStorageProtocolUDP AccessLogSyslogStorageProtocol = "udp"
|
||||
AccessLogSyslogStorageProtocolNone AccessLogSyslogStorageProtocol = "none"
|
||||
AccessLogSyslogStorageProtocolSocket AccessLogSyslogStorageProtocol = "socket"
|
||||
)
|
||||
|
||||
type AccessLogSyslogStoragePriority = int
|
||||
|
||||
const (
|
||||
AccessLogSyslogStoragePriorityEmerg AccessLogSyslogStoragePriority = iota
|
||||
AccessLogSyslogStoragePriorityAlert
|
||||
AccessLogSyslogStoragePriorityCrit
|
||||
AccessLogSyslogStoragePriorityErr
|
||||
AccessLogSyslogStoragePriorityWarning
|
||||
AccessLogSyslogStoragePriorityNotice
|
||||
AccessLogSyslogStoragePriorityInfo
|
||||
AccessLogSyslogStoragePriorityDebug
|
||||
)
|
||||
|
||||
var AccessLogSyslogStoragePriorities = []maps.Map{
|
||||
{
|
||||
"name": "[无]",
|
||||
"value": -1,
|
||||
},
|
||||
{
|
||||
"name": "EMERG",
|
||||
"value": AccessLogSyslogStoragePriorityEmerg,
|
||||
},
|
||||
{
|
||||
"name": "ALERT",
|
||||
"value": AccessLogSyslogStoragePriorityAlert,
|
||||
},
|
||||
{
|
||||
"name": "CRIT",
|
||||
"value": AccessLogSyslogStoragePriorityCrit,
|
||||
},
|
||||
{
|
||||
"name": "ERR",
|
||||
"value": AccessLogSyslogStoragePriorityErr,
|
||||
},
|
||||
{
|
||||
"name": "WARNING",
|
||||
"value": AccessLogSyslogStoragePriorityWarning,
|
||||
},
|
||||
{
|
||||
"name": "NOTICE",
|
||||
"value": AccessLogSyslogStoragePriorityNotice,
|
||||
},
|
||||
{
|
||||
"name": "INFO",
|
||||
"value": AccessLogSyslogStoragePriorityInfo,
|
||||
},
|
||||
{
|
||||
"name": "DEBUG",
|
||||
"value": AccessLogSyslogStoragePriorityDebug,
|
||||
},
|
||||
}
|
||||
|
||||
// AccessLogSyslogStorageConfig syslog存储策略
|
||||
type AccessLogSyslogStorageConfig struct {
|
||||
Protocol string `yaml:"protocol" json:"protocol"` // SysLogStorageProtocol*
|
||||
ServerAddr string `yaml:"serverAddr" json:"serverAddr"`
|
||||
ServerPort int `yaml:"serverPort" json:"serverPort"`
|
||||
Socket string `yaml:"socket" json:"socket"` // sock file
|
||||
Tag string `yaml:"tag" json:"tag"`
|
||||
Priority AccessLogSyslogStoragePriority `yaml:"priority" json:"priority"`
|
||||
}
|
||||
|
||||
func FindAccessLogSyslogStoragePriorityName(priority AccessLogSyslogStoragePriority) string {
|
||||
for _, p := range AccessLogSyslogStoragePriorities {
|
||||
if p.GetInt("value") == priority {
|
||||
return p.GetString("name")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
9
EdgeCommon/pkg/serverconfigs/access_log_storage_tcp.go
Normal file
9
EdgeCommon/pkg/serverconfigs/access_log_storage_tcp.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// AccessLogTCPStorageConfig TCP存储策略
|
||||
type AccessLogTCPStorageConfig struct {
|
||||
Network string `yaml:"network" json:"network"` // tcp, unix
|
||||
Addr string `yaml:"addr" json:"addr"`
|
||||
}
|
||||
57
EdgeCommon/pkg/serverconfigs/access_log_storages.go
Normal file
57
EdgeCommon/pkg/serverconfigs/access_log_storages.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
)
|
||||
|
||||
// AccessLogStorageType 访问日志存储类型
|
||||
type AccessLogStorageType = string
|
||||
|
||||
const (
|
||||
AccessLogStorageTypeFile AccessLogStorageType = "file"
|
||||
AccessLogStorageTypeES AccessLogStorageType = "es"
|
||||
AccessLogStorageTypeTCP AccessLogStorageType = "tcp"
|
||||
AccessLogStorageTypeSyslog AccessLogStorageType = "syslog"
|
||||
AccessLogStorageTypeCommand AccessLogStorageType = "command"
|
||||
)
|
||||
|
||||
// FindAllAccessLogStorageTypes 所有存储引擎列表
|
||||
func FindAllAccessLogStorageTypes() []*shared.Definition {
|
||||
return []*shared.Definition{
|
||||
{
|
||||
Name: "文件",
|
||||
Code: AccessLogStorageTypeFile,
|
||||
Description: "将日志存储在磁盘文件中",
|
||||
},
|
||||
{
|
||||
Name: "ElasticSearch",
|
||||
Code: AccessLogStorageTypeES,
|
||||
Description: "将日志存储在ElasticSearch中",
|
||||
},
|
||||
{
|
||||
Name: "TCP Socket",
|
||||
Code: AccessLogStorageTypeTCP,
|
||||
Description: "将日志通过TCP套接字输出",
|
||||
},
|
||||
{
|
||||
Name: "Syslog",
|
||||
Code: AccessLogStorageTypeSyslog,
|
||||
Description: "将日志通过syslog输出,仅支持Linux",
|
||||
},
|
||||
{
|
||||
Name: "命令行输入流",
|
||||
Code: AccessLogStorageTypeCommand,
|
||||
Description: "启动一个命令通过读取stdin接收日志信息",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FindAccessLogStorageTypeName 根据类型查找名称
|
||||
func FindAccessLogStorageTypeName(storageType string) string {
|
||||
for _, m := range FindAllAccessLogStorageTypes() {
|
||||
if m.Code == storageType {
|
||||
return m.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
35
EdgeCommon/pkg/serverconfigs/config_codes.go
Normal file
35
EdgeCommon/pkg/serverconfigs/config_codes.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package serverconfigs
|
||||
|
||||
type ConfigCode = string
|
||||
|
||||
const (
|
||||
ConfigCodeUAM ConfigCode = "uam"
|
||||
ConfigCodeCC ConfigCode = "cc"
|
||||
ConfigCodeHostRedirects ConfigCode = "hostRedirects"
|
||||
ConfigCodeLocations ConfigCode = "locations"
|
||||
ConfigCodeRewrites ConfigCode = "rewrites"
|
||||
ConfigCodeWAF ConfigCode = "waf"
|
||||
ConfigCodeCache ConfigCode = "cache"
|
||||
ConfigCodeAuth ConfigCode = "auth"
|
||||
ConfigCodeReferers ConfigCode = "referers"
|
||||
ConfigCodeUserAgent ConfigCode = "userAgent"
|
||||
ConfigCodeCharset ConfigCode = "charset"
|
||||
ConfigCodeAccessLog ConfigCode = "accessLog"
|
||||
ConfigCodeStat ConfigCode = "stat"
|
||||
ConfigCodeCompression ConfigCode = "compression"
|
||||
ConfigCodeOptimization ConfigCode = "optimization"
|
||||
ConfigCodePages ConfigCode = "pages"
|
||||
ConfigCodeHeaders ConfigCode = "headers"
|
||||
ConfigCodeWebsocket ConfigCode = "websocket"
|
||||
ConfigCodeWebp ConfigCode = "webp"
|
||||
ConfigCodeRoot ConfigCode = "root"
|
||||
ConfigCodeFastcgi ConfigCode = "fastcgi"
|
||||
ConfigCodeRemoteAddr ConfigCode = "remoteAddr"
|
||||
ConfigCodeRequestLimit ConfigCode = "requestLimit"
|
||||
ConfigCodeTraffic ConfigCode = "traffic"
|
||||
ConfigCodeRequestScripts ConfigCode = "requestScripts"
|
||||
ConfigCodeReverseProxy ConfigCode = "reverseProxy"
|
||||
ConfigCodeMultimedia ConfigCode = "multimedia"
|
||||
)
|
||||
8
EdgeCommon/pkg/serverconfigs/ddosconfigs/ip_config.go
Normal file
8
EdgeCommon/pkg/serverconfigs/ddosconfigs/ip_config.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package ddosconfigs
|
||||
|
||||
type IPConfig struct {
|
||||
IP string `json:"ip"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
8
EdgeCommon/pkg/serverconfigs/ddosconfigs/port_config.go
Normal file
8
EdgeCommon/pkg/serverconfigs/ddosconfigs/port_config.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package ddosconfigs
|
||||
|
||||
type PortConfig struct {
|
||||
Port int32 `json:"port"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package ddosconfigs
|
||||
|
||||
func DefaultProtectionConfig() *ProtectionConfig {
|
||||
return &ProtectionConfig{}
|
||||
}
|
||||
|
||||
type ProtectionConfig struct {
|
||||
TCP *TCPConfig `yaml:"tcp" json:"tcp"`
|
||||
}
|
||||
|
||||
func (this *ProtectionConfig) Init() error {
|
||||
// tcp
|
||||
if this.TCP != nil {
|
||||
err := this.TCP.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ProtectionConfig) IsPriorEmpty() bool {
|
||||
if this.TCP != nil && this.TCP.IsPrior {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *ProtectionConfig) IsOn() bool {
|
||||
// tcp
|
||||
if this.TCP != nil && this.TCP.IsOn {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ProtectionConfig) Merge(childConfig *ProtectionConfig) {
|
||||
if childConfig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// tcp
|
||||
if childConfig.TCP != nil && childConfig.TCP.IsPrior {
|
||||
this.TCP = childConfig.TCP
|
||||
}
|
||||
}
|
||||
25
EdgeCommon/pkg/serverconfigs/ddosconfigs/tcp_config.go
Normal file
25
EdgeCommon/pkg/serverconfigs/ddosconfigs/tcp_config.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package ddosconfigs
|
||||
|
||||
type TCPConfig struct {
|
||||
IsPrior bool `json:"isPrior"`
|
||||
IsOn bool `json:"isOn"`
|
||||
MaxConnections int32 `json:"maxConnections"`
|
||||
MaxConnectionsPerIP int32 `json:"maxConnectionsPerIP"`
|
||||
|
||||
// 分钟级速率
|
||||
NewConnectionsMinutelyRate int32 `json:"newConnectionsRate"` // 分钟
|
||||
NewConnectionsMinutelyRateBlockTimeout int32 `json:"newConnectionsRateBlockTimeout"` // 拦截时间
|
||||
|
||||
// 秒级速率
|
||||
NewConnectionsSecondlyRate int32 `json:"newConnectionsSecondlyRate"`
|
||||
NewConnectionsSecondlyRateBlockTimeout int32 `json:"newConnectionsSecondlyRateBlockTimeout"`
|
||||
|
||||
AllowIPList []*IPConfig `json:"allowIPList"`
|
||||
Ports []*PortConfig `json:"ports"`
|
||||
}
|
||||
|
||||
func (this *TCPConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
30
EdgeCommon/pkg/serverconfigs/domain_mismatch_action.go
Normal file
30
EdgeCommon/pkg/serverconfigs/domain_mismatch_action.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package serverconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
const (
|
||||
DomainMismatchActionPage = "page"
|
||||
DomainMismatchActionClose = "close"
|
||||
DomainMismatchActionRedirect = "redirect"
|
||||
)
|
||||
|
||||
type DomainMismatchPageOptions struct {
|
||||
StatusCode int `yaml:"statusCode" json:"statusCode"`
|
||||
ContentHTML string `yaml:"contentHTML" json:"contentHTML"`
|
||||
}
|
||||
|
||||
type DomainMismatchCloseOptions struct {
|
||||
}
|
||||
|
||||
type DomainMismatchRedirectOptions struct {
|
||||
URL string `yaml:"url" json:"url"`
|
||||
}
|
||||
|
||||
type DomainMismatchAction struct {
|
||||
Code string `yaml:"code" json:"code"` // 动作代号
|
||||
Options maps.Map `yaml:"options" json:"options"` // 动作选项
|
||||
}
|
||||
|
||||
func (this *DomainMismatchAction) Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type Base64DecodeFilter struct {
|
||||
}
|
||||
|
||||
func (this *Base64DecodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Base64DecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
output, err = base64.StdEncoding.DecodeString(ToString(input))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBase64DecodeFilter_Do(t *testing.T) {
|
||||
filter := &Base64DecodeFilter{}
|
||||
t.Log(filter.Do("123456", nil))
|
||||
|
||||
encodedString := base64.StdEncoding.EncodeToString([]byte("hello"))
|
||||
t.Log(filter.Do(encodedString, nil))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type Base64EncodeFilter struct {
|
||||
}
|
||||
|
||||
func (this *Base64EncodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Base64EncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
data := ToBytes(input)
|
||||
output = base64.StdEncoding.EncodeToString(data)
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBase64EncodeFilter_Do(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
|
||||
filter := &Base64EncodeFilter{}
|
||||
t.Log(filter.Do("hello", nil))
|
||||
t.Log(filter.Do("=", nil))
|
||||
|
||||
output, goNext, err := filter.Do("123456", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(goNext)
|
||||
|
||||
outputString := output.(string)
|
||||
result, err := base64.StdEncoding.DecodeString(outputString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("origin:", string(result))
|
||||
}
|
||||
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_dec2hex.go
Normal file
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_dec2hex.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type Dec2HexFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *Dec2HexFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *Dec2HexFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
v := types.Int64(input)
|
||||
return fmt.Sprintf("%x", v), true, nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDec2HexFilter_Do(t *testing.T) {
|
||||
filter := &Dec2HexFilter{}
|
||||
err := filter.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(filter.Do("123456", nil))
|
||||
t.Log(filter.Do("1", nil))
|
||||
}
|
||||
21
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_hex2dec.go
Normal file
21
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_hex2dec.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Hex2DecFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *Hex2DecFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *Hex2DecFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
n := new(big.Int)
|
||||
n.SetString(types.String(input), 16)
|
||||
return n.Uint64(), true, nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHex2DecFilter_Do(t *testing.T) {
|
||||
filter := &Hex2DecFilter{}
|
||||
err := filter.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(filter.Do("0e", nil))
|
||||
t.Log(filter.Do("e", nil))
|
||||
|
||||
{
|
||||
result, _, _ := filter.Do("123", nil)
|
||||
t.Logf("%x", result)
|
||||
}
|
||||
|
||||
{
|
||||
md5 := stringutil.Md5("123456")
|
||||
t.Log("md5:", md5)
|
||||
result, _, _ := filter.Do(md5, nil)
|
||||
t.Log(result)
|
||||
t.Logf("%x", result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"html"
|
||||
)
|
||||
|
||||
type HTMLEscapeFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *HTMLEscapeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *HTMLEscapeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
s := types.String(input)
|
||||
return html.EscapeString(s), true, nil
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHTMLEscapeFilter_Do(t *testing.T) {
|
||||
filter := &HTMLEscapeFilter{}
|
||||
t.Log(filter.Do("Hello", nil))
|
||||
t.Log(filter.Do("<script></script>", nil))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"html"
|
||||
)
|
||||
|
||||
type HTMLUnescapeFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *HTMLUnescapeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *HTMLUnescapeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
s := types.String(input)
|
||||
return html.UnescapeString(s), true, nil
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHTMLUnescapeFilter_Do(t *testing.T) {
|
||||
filter := &HTMLUnescapeFilter{}
|
||||
t.Log(filter.Do("Hello", nil))
|
||||
t.Log(filter.Do("<script>", nil))
|
||||
t.Log(filter.Do("<script>", nil))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package filterconfigs
|
||||
|
||||
// 过滤接口
|
||||
type FilterInterface interface {
|
||||
// 初始化
|
||||
Init() error
|
||||
|
||||
// 执行过滤
|
||||
Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error)
|
||||
}
|
||||
16
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_length.go
Normal file
16
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_length.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package filterconfigs
|
||||
|
||||
type LengthFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *LengthFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *LengthFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
output = len(ToBytes(input))
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLengthFilter_Do(t *testing.T) {
|
||||
filter := &LengthFilter{}
|
||||
t.Log(filter.Do("hello", nil))
|
||||
t.Log(filter.Do([]byte("hello"), nil))
|
||||
}
|
||||
23
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_md5.go
Normal file
23
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_md5.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Md5Filter struct {
|
||||
}
|
||||
|
||||
func (this *Md5Filter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Md5Filter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
data := ToBytes(input)
|
||||
m := md5.New()
|
||||
m.Write(data)
|
||||
result := m.Sum(nil)
|
||||
output = fmt.Sprintf("%x", result)
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMd5Filter_Do(t *testing.T) {
|
||||
filter := &Md5Filter{}
|
||||
t.Log(filter.Do("123456", nil))
|
||||
t.Log(filter.Do(nil, nil))
|
||||
t.Log(filter.Do("", nil))
|
||||
t.Log(filter.Do("hello", nil))
|
||||
}
|
||||
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_sha1.go
Normal file
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_sha1.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type Sha1Filter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *Sha1Filter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *Sha1Filter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(types.String(input)))), true, nil
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSha1Filter_Do(t *testing.T) {
|
||||
filter := &Sha1Filter{}
|
||||
t.Log(filter.Do("123456", nil))
|
||||
t.Log(filter.Do("", nil))
|
||||
}
|
||||
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_sha256.go
Normal file
20
EdgeCommon/pkg/serverconfigs/filterconfigs/filter_sha256.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type Sha256Filter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *Sha256Filter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *Sha256Filter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(types.String(input)))), true, nil
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSha256Filter_Do(t *testing.T) {
|
||||
filter := &Sha256Filter{}
|
||||
t.Log(filter.Do("123456", nil))
|
||||
t.Log(filter.Do("", nil))
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UnicodeDecodeFilter struct {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *UnicodeDecodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行过滤
|
||||
func (this *UnicodeDecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
s := types.String(input)
|
||||
result, err := strconv.Unquote("\"" + strings.ReplaceAll(s, "\"", "\\\"") + "\"")
|
||||
if err != nil {
|
||||
return input, true, nil
|
||||
}
|
||||
return result, true, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUnicodeDecodeFilter_Do(t *testing.T) {
|
||||
filter := &UnicodeDecodeFilter{}
|
||||
t.Log(filter.Do(`"\u5947\u5c4f`, nil))
|
||||
t.Log(filter.Do(`"Hello`, nil))
|
||||
t.Log(filter.Do(`真实的存在`, nil))
|
||||
|
||||
t.Logf("%x", "/dashboard")
|
||||
t.Log(filter.Do("\x2f\x64\x61\x73\x68\x62\x6f\x61\x72\x64", nil))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UnicodeEncodeFilter struct {
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *UnicodeEncodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do 执行过滤
|
||||
func (this *UnicodeEncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
var s = types.String(input)
|
||||
var result = strings.Builder{}
|
||||
for _, r := range s {
|
||||
if r < 128 {
|
||||
result.WriteRune(r)
|
||||
} else {
|
||||
result.WriteString("\\u" + strconv.FormatInt(int64(r), 16))
|
||||
}
|
||||
}
|
||||
return result.String(), true, nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUnicodeEncodeFilter_Do(t *testing.T) {
|
||||
filter := &UnicodeEncodeFilter{}
|
||||
t.Log(filter.Do("Hello", nil))
|
||||
t.Log(filter.Do("我是中文", nil))
|
||||
t.Log(filter.Do("我是中文和英文Mixed", nil))
|
||||
t.Log(filter.Do("我有特殊字符|'\"", nil))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package filterconfigs
|
||||
|
||||
import "net/url"
|
||||
|
||||
type URLDecodeFilter struct {
|
||||
}
|
||||
|
||||
func (this *URLDecodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *URLDecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
output, err = url.QueryUnescape(ToString(input))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package filterconfigs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestURLDecodeFilter_Do(t *testing.T) {
|
||||
filter := &URLDecodeFilter{}
|
||||
t.Log(filter.Do("hello", nil))
|
||||
t.Log(filter.Do(url.QueryEscape("/hello/world/?a=b&c=d"), nil))
|
||||
t.Log(filter.Do("/hello/world/?a=b&c=d", nil))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package filterconfigs
|
||||
|
||||
import "net/url"
|
||||
|
||||
type URLEncodeFilter struct {
|
||||
}
|
||||
|
||||
func (this *URLEncodeFilter) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *URLEncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) {
|
||||
output = url.QueryEscape(ToString(input))
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestURLEncodeFilter_Do(t *testing.T) {
|
||||
filter := &URLEncodeFilter{}
|
||||
t.Log(filter.Do("hello", nil))
|
||||
t.Log(filter.Do("/hello/world?a=b&c=中文&d=<symbol>", nil))
|
||||
}
|
||||
35
EdgeCommon/pkg/serverconfigs/filterconfigs/filters.go
Normal file
35
EdgeCommon/pkg/serverconfigs/filterconfigs/filters.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package filterconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/logs"
|
||||
|
||||
func init() {
|
||||
for code, filter := range allFilters {
|
||||
err := filter.Init()
|
||||
if err != nil {
|
||||
logs.Println("[FILTER]init '" + code + "' failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 所有的筛选条件
|
||||
var allFilters = map[string]FilterInterface{
|
||||
"md5": new(Md5Filter),
|
||||
"urlEncode": new(URLEncodeFilter),
|
||||
"urlDecode": new(URLDecodeFilter),
|
||||
"base64Encode": new(Base64EncodeFilter),
|
||||
"base64Decode": new(Base64DecodeFilter),
|
||||
"unicodeEncode": new(UnicodeEncodeFilter),
|
||||
"unicodeDecode": new(UnicodeDecodeFilter),
|
||||
"htmlEscape": new(HTMLEscapeFilter),
|
||||
"htmlUnescape": new(HTMLUnescapeFilter),
|
||||
"length": new(LengthFilter),
|
||||
"hex2dec": new(Hex2DecFilter),
|
||||
"dec2hex": new(Dec2HexFilter),
|
||||
"sha1": new(Sha1Filter),
|
||||
"sha256": new(Sha256Filter),
|
||||
}
|
||||
|
||||
// 查找Filter
|
||||
func FindFilter(code string) FilterInterface {
|
||||
return allFilters[code]
|
||||
}
|
||||
25
EdgeCommon/pkg/serverconfigs/filterconfigs/utils.go
Normal file
25
EdgeCommon/pkg/serverconfigs/filterconfigs/utils.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package filterconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/types"
|
||||
|
||||
// 将输入内容转换为字节
|
||||
func ToBytes(input interface{}) []byte {
|
||||
if input == nil {
|
||||
return []byte{}
|
||||
}
|
||||
var data []byte
|
||||
var ok bool
|
||||
data, ok = input.([]byte)
|
||||
if ok {
|
||||
return data
|
||||
}
|
||||
return []byte(types.String(input))
|
||||
}
|
||||
|
||||
// 将输入内容转换为字符串
|
||||
func ToString(input interface{}) string {
|
||||
if input == nil {
|
||||
return ""
|
||||
}
|
||||
return types.String(input)
|
||||
}
|
||||
9
EdgeCommon/pkg/serverconfigs/filterconfigs/utils_test.go
Normal file
9
EdgeCommon/pkg/serverconfigs/filterconfigs/utils_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package filterconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestToBytes(t *testing.T) {
|
||||
t.Log(ToBytes("hello"))
|
||||
t.Log(ToBytes(123))
|
||||
t.Log(ToBytes([]byte{1, 2, 3}))
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
|
||||
type CaptchaType = string
|
||||
|
||||
const (
|
||||
CaptchaTypeDefault CaptchaType = "default"
|
||||
CaptchaTypeOneClick CaptchaType = "oneClick"
|
||||
CaptchaTypeSlide CaptchaType = "slide"
|
||||
CaptchaTypeGeeTest CaptchaType = "geetest"
|
||||
)
|
||||
|
||||
// FindAllCaptchaTypes Find all captcha types
|
||||
func FindAllCaptchaTypes() []*shared.Definition {
|
||||
return []*shared.Definition{
|
||||
{
|
||||
Code: CaptchaTypeDefault,
|
||||
Name: "验证码",
|
||||
Description: "通过输入验证码来验证人机。",
|
||||
},
|
||||
{
|
||||
Code: CaptchaTypeOneClick,
|
||||
Name: "点击验证",
|
||||
Description: "通过点击界面元素来验证人机。",
|
||||
},
|
||||
{
|
||||
Code: CaptchaTypeSlide,
|
||||
Name: "滑动解锁",
|
||||
Description: "通过滑动方块解锁来验证人机。",
|
||||
},
|
||||
{
|
||||
Code: CaptchaTypeGeeTest,
|
||||
Name: "极验-行为验",
|
||||
Description: "使用极验-行为验提供的人机验证方式。",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCaptchaType() *shared.Definition {
|
||||
var captchaTypes = FindAllCaptchaTypes()
|
||||
if len(captchaTypes) > 0 {
|
||||
return captchaTypes[0]
|
||||
}
|
||||
return &shared.Definition{
|
||||
Code: CaptchaTypeDefault,
|
||||
Name: "验证码",
|
||||
}
|
||||
}
|
||||
|
||||
func FindCaptchaType(code CaptchaType) *shared.Definition {
|
||||
if len(code) == 0 {
|
||||
code = CaptchaTypeDefault
|
||||
}
|
||||
|
||||
for _, t := range FindAllCaptchaTypes() {
|
||||
if t.Code == code {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultCaptchaType()
|
||||
}
|
||||
53
EdgeCommon/pkg/serverconfigs/firewallconfigs/consts.go
Normal file
53
EdgeCommon/pkg/serverconfigs/firewallconfigs/consts.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ipconfigs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
const (
|
||||
GlobalBlackListId int64 = 2_000_000_000
|
||||
GlobalWhiteListId int64 = 2_000_000_001
|
||||
GlobalGreyListId int64 = 2_000_000_002
|
||||
|
||||
DefaultEventLevel = "critical"
|
||||
)
|
||||
|
||||
func FindGlobalListIdWithType(listType ipconfigs.IPListType) int64 {
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
return GlobalBlackListId
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
return GlobalWhiteListId
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
return GlobalGreyListId
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func FindGlobalListNameWithType(listType ipconfigs.IPListType) string {
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
return "全局黑名单"
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
return "全局白名单"
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
return "全局灰名单"
|
||||
}
|
||||
return "全局黑名单"
|
||||
}
|
||||
|
||||
func IsGlobalListId(listId int64) bool {
|
||||
return listId == GlobalBlackListId || listId == GlobalWhiteListId || listId == GlobalGreyListId
|
||||
}
|
||||
|
||||
func FindGlobalListIds() []int64 {
|
||||
return []int64{GlobalBlackListId, GlobalWhiteListId, GlobalGreyListId}
|
||||
}
|
||||
|
||||
func FindGlobalListIdStrings() []string {
|
||||
return []string{types.String(GlobalBlackListId), types.String(GlobalWhiteListId), types.String(GlobalGreyListId)}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
// FirewallActionConfig 防火墙动作配置
|
||||
type FirewallActionConfig struct {
|
||||
Id int64 `yaml:"id" json:"id"` // Id
|
||||
Type string `yaml:"type" json:"type"` // 类型
|
||||
Params maps.Map `yaml:"params" json:"params"` // 参数
|
||||
EventLevel string `yaml:"eventLevel" json:"eventLevel"` // 事件级别
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *FirewallActionConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
112
EdgeCommon/pkg/serverconfigs/firewallconfigs/firewall_actions.go
Normal file
112
EdgeCommon/pkg/serverconfigs/firewallconfigs/firewall_actions.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package firewallconfigs
|
||||
|
||||
type FirewallActionType = string
|
||||
|
||||
const (
|
||||
FirewallActionTypeIPSet FirewallActionType = "ipset"
|
||||
FirewallActionTypeFirewalld FirewallActionType = "firewalld"
|
||||
FirewallActionTypeIPTables FirewallActionType = "iptables"
|
||||
FirewallActionTypeScript FirewallActionType = "script"
|
||||
FirewallActionTypeHTTPAPI FirewallActionType = "httpAPI"
|
||||
FirewallActionTypeHTML FirewallActionType = "html"
|
||||
)
|
||||
|
||||
type FirewallActionTypeDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code FirewallActionType `json:"code"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func FindAllFirewallActionTypes() []*FirewallActionTypeDefinition {
|
||||
return []*FirewallActionTypeDefinition{
|
||||
{
|
||||
Name: "ipset",
|
||||
Code: FirewallActionTypeIPSet,
|
||||
Description: "使用特定的ipset管理IP,可以结合iptables和firewalld等工具一起工作。",
|
||||
},
|
||||
{
|
||||
Name: "firewalld",
|
||||
Code: FirewallActionTypeFirewalld,
|
||||
Description: "使用Firewalld管理IP,非持久保存,reload之后重置规则。",
|
||||
},
|
||||
{
|
||||
Name: "iptables",
|
||||
Code: FirewallActionTypeIPTables,
|
||||
Description: "使用IPTables管理IP,不支持超时时间设定,非持久保存,reload之后重置规则。",
|
||||
},
|
||||
{
|
||||
Name: "自定义脚本",
|
||||
Code: FirewallActionTypeScript,
|
||||
Description: "使用自定义的脚本执行IP操作。",
|
||||
},
|
||||
{
|
||||
Name: "自定义HTTP API",
|
||||
Code: FirewallActionTypeHTTPAPI,
|
||||
Description: "使用自定义的HTTP API执行IP操作。",
|
||||
},
|
||||
{
|
||||
Name: "显示HTML内容",
|
||||
Code: FirewallActionTypeHTML,
|
||||
Description: "显示一段自定义的HTML网页内容",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindFirewallActionTypeName(actionType FirewallActionType) string {
|
||||
for _, a := range FindAllFirewallActionTypes() {
|
||||
if a.Code == actionType {
|
||||
return a.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FirewallActionIPSetConfig struct {
|
||||
Path string `json:"path"` // 命令路径 TODO 暂时不实现
|
||||
|
||||
WhiteName string `json:"whiteName"` // IPSet白名单名称
|
||||
BlackName string `json:"blackName"` // IPSet黑名单名称
|
||||
|
||||
WhiteNameIPv6 string `json:"whiteNameIPv6"` // IPv6白名单名称
|
||||
BlackNameIPv6 string `json:"blackNameIPv6"` // IPv6黑名单名称
|
||||
|
||||
MaxElements int `json:"maxElements"` // 最多IP数量 TODO 暂时不实现
|
||||
AutoAddToIPTables bool `json:"autoAddToIPTables"` // 是否自动创建IPTables规则
|
||||
AutoAddToFirewalld bool `json:"autoAddToFirewalld"` // 是否自动加入到Firewalld
|
||||
|
||||
// TODO 添加需要阻止的端口列表
|
||||
}
|
||||
|
||||
type FirewallActionFirewalldConfig struct {
|
||||
Path string `json:"path"` // 命令路径 TODO 暂时不实现
|
||||
|
||||
// TODO 添加需要阻止的端口列表
|
||||
}
|
||||
|
||||
type FirewallActionIPTablesConfig struct {
|
||||
Path string `json:"path"` // 命令路径 TODO 暂时不实现
|
||||
|
||||
// TODO 添加需要阻止的端口列表
|
||||
}
|
||||
|
||||
type FirewallActionScriptConfig struct {
|
||||
Path string `json:"path"` // 脚本路径
|
||||
Cwd string `json:"cwd"` // 工作目录 TODO 暂时不实现
|
||||
Args []string `json:"args"` // 附加参数 TODO 暂时不实现
|
||||
|
||||
// TODO 添加需要阻止的端口列表
|
||||
}
|
||||
|
||||
// FirewallActionHTTPAPIConfig HTTP API配置
|
||||
type FirewallActionHTTPAPIConfig struct {
|
||||
URL string `json:"url"` // URL路径
|
||||
TimeoutSeconds int `json:"timeoutSeconds"` // 超时时间 TODO 暂时不实现
|
||||
Secret string `json:"secret"` // 认证密钥 TODO 暂时不实现
|
||||
|
||||
// TODO 添加需要阻止的端口列表
|
||||
}
|
||||
|
||||
// FirewallActionHTMLConfig HTML配置
|
||||
type FirewallActionHTMLConfig struct {
|
||||
Content string `json:"content"` // 完整的HTML内容
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package firewallconfigs
|
||||
|
||||
type FirewallEventLevelDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func FindAllFirewallEventLevels() []*FirewallEventLevelDefinition {
|
||||
return []*FirewallEventLevelDefinition{
|
||||
{
|
||||
Name: "调试",
|
||||
Code: "debug",
|
||||
Description: "仅作为调试用途",
|
||||
},
|
||||
{
|
||||
Name: "通知",
|
||||
Code: "notice",
|
||||
Description: "需要通知的事件",
|
||||
},
|
||||
{
|
||||
Name: "警告",
|
||||
Code: "warning",
|
||||
Description: "需要警告的事件",
|
||||
},
|
||||
{
|
||||
Name: "错误",
|
||||
Code: "error",
|
||||
Description: "发生系统错误的事件",
|
||||
},
|
||||
{
|
||||
Name: "严重",
|
||||
Code: "critical",
|
||||
Description: "性质较为严重的事件",
|
||||
},
|
||||
{
|
||||
Name: "致命",
|
||||
Code: "fatal",
|
||||
Description: "对系统有重大影响的事件",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindFirewallEventLevelName(code string) string {
|
||||
for _, level := range FindAllFirewallEventLevels() {
|
||||
if level.Code == code {
|
||||
return level.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
|
||||
// FirewallMode 模式
|
||||
type FirewallMode = string
|
||||
|
||||
const (
|
||||
FirewallModeDefend FirewallMode = "defend" // 防御模式
|
||||
FirewallModeObserve FirewallMode = "observe" // 观察模式
|
||||
FirewallModeBypass FirewallMode = "bypass" // 通过模式
|
||||
)
|
||||
|
||||
func FindAllFirewallModes() []*shared.Definition {
|
||||
return []*shared.Definition{
|
||||
{
|
||||
Name: "防御模式",
|
||||
Description: "执行正常的防御规则和相应动作。",
|
||||
Code: FirewallModeDefend,
|
||||
},
|
||||
{
|
||||
Name: "观察模式",
|
||||
Description: "执行正常的防御规则,但只记录日志,不执行动作。",
|
||||
Code: FirewallModeObserve,
|
||||
},
|
||||
{
|
||||
Name: "通过模式",
|
||||
Description: "不执行任何规则,所有的请求都将会直接通过。",
|
||||
Code: FirewallModeBypass,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FindFirewallMode(code FirewallMode) *shared.Definition {
|
||||
for _, def := range FindAllFirewallModes() {
|
||||
if def.Code == code {
|
||||
return def
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type FirewallScope = string
|
||||
|
||||
const (
|
||||
FirewallScopeGlobal FirewallScope = "global"
|
||||
FirewallScopeServer FirewallScope = "service" // 历史原因,代号为 service 而非 server
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package firewallconfigs
|
||||
|
||||
type AllowScope = string
|
||||
|
||||
const (
|
||||
AllowScopeGroup AllowScope = "group"
|
||||
AllowScopeServer AllowScope = "server"
|
||||
AllowScopeGlobal AllowScope = "global"
|
||||
)
|
||||
|
||||
type HTTPFirewallAllowAction struct {
|
||||
Scope AllowScope `yaml:"scope" json:"scope"`
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPFirewallBlockAction default block action
|
||||
type HTTPFirewallBlockAction struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
StatusCode int `yaml:"statusCode" json:"statusCode"`
|
||||
Body string `yaml:"body" json:"body"` // supports HTML
|
||||
URL string `yaml:"url" json:"url"`
|
||||
Timeout int32 `yaml:"timeout" json:"timeout"` // 最小封禁时长
|
||||
TimeoutMax int32 `yaml:"timeoutMax" json:"timeoutMax"` // 最大封禁时长
|
||||
Scope FirewallScope `yaml:"scope" json:"scope"`
|
||||
|
||||
FailBlockScopeAll bool `yaml:"failBlockScopeAll" json:"failBlockScopeAll"`
|
||||
}
|
||||
|
||||
func NewHTTPFirewallBlockAction() *HTTPFirewallBlockAction {
|
||||
return &HTTPFirewallBlockAction{
|
||||
StatusCode: http.StatusForbidden,
|
||||
Body: "Blocked By WAF",
|
||||
Timeout: 300,
|
||||
FailBlockScopeAll: true,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package firewallconfigs
|
||||
|
||||
type GeeTestConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
CaptchaId string `yaml:"captchaId" json:"captchaId"`
|
||||
CaptchaKey string `yaml:"captchaKey" json:"captchaKey"`
|
||||
}
|
||||
|
||||
type HTTPFirewallCaptchaAction struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
CaptchaType CaptchaType `yaml:"captchaType" json:"captchaType"` // 类型
|
||||
|
||||
Life int32 `yaml:"life" json:"life"` // 有效期
|
||||
MaxFails int `yaml:"maxFails" json:"maxFails"` // 最大失败次数
|
||||
FailBlockTimeout int `yaml:"failBlockTimeout" json:"failBlockTimeout"` // 失败拦截时间
|
||||
FailBlockScopeAll bool `yaml:"failBlockScopeAll" json:"failBlockScopeAll"` // 是否全局有效
|
||||
|
||||
// 验证码相关配置
|
||||
|
||||
CountLetters int8 `yaml:"countLetters" json:"countLetters"` // 字符数量
|
||||
|
||||
UIIsOn bool `yaml:"uiIsOn" json:"uiIsOn"` // 是否使用自定义UI
|
||||
UITitle string `yaml:"uiTitle" json:"uiTitle"` // 消息标题
|
||||
UIPrompt string `yaml:"uiPrompt" json:"uiPrompt"` // 消息提示
|
||||
UIButtonTitle string `yaml:"uiButtonTitle" json:"uiButtonTitle"` // 按钮标题
|
||||
UIShowRequestId bool `yaml:"uiShowRequestId" json:"uiShowRequestId"` // 是否显示请求ID
|
||||
UICss string `yaml:"uiCss" json:"uiCss"` // CSS样式
|
||||
UIFooter string `yaml:"uiFooter" json:"uiFooter"` // 页脚
|
||||
UIBody string `yaml:"uiBody" json:"uiBody"` // 内容轮廓
|
||||
|
||||
CookieId string `yaml:"cookieId" json:"cookieId"` // TODO
|
||||
|
||||
Lang string `yaml:"lang" json:"lang"` // 语言,zh-CN, en-US ... TODO 需要实现,目前是根据浏览器Accept-Language动态获取
|
||||
|
||||
// 极验相关配置
|
||||
// MUST be struct
|
||||
GeeTestConfig GeeTestConfig `yaml:"geeTestConfig" json:"geeTestConfig"`
|
||||
}
|
||||
|
||||
func NewHTTPFirewallCaptchaAction() *HTTPFirewallCaptchaAction {
|
||||
return &HTTPFirewallCaptchaAction{
|
||||
Life: 600,
|
||||
MaxFails: 100,
|
||||
FailBlockTimeout: 3600,
|
||||
FailBlockScopeAll: true,
|
||||
UIShowRequestId: true,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "reflect"
|
||||
|
||||
type HTTPFirewallActionCategory = string
|
||||
|
||||
const (
|
||||
HTTPFirewallActionCategoryBlock HTTPFirewallActionCategory = "block"
|
||||
HTTPFirewallActionCategoryAllow HTTPFirewallActionCategory = "allow"
|
||||
HTTPFirewallActionCategoryVerify HTTPFirewallActionCategory = "verify"
|
||||
)
|
||||
|
||||
// HTTPFirewallActionDefinition action definition
|
||||
type HTTPFirewallActionDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code HTTPFirewallActionString `json:"code"`
|
||||
Description string `json:"description"`
|
||||
Type reflect.Type `json:"type"`
|
||||
Category HTTPFirewallActionCategory `json:"category"`
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallGet302Action struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
Life int32 `yaml:"life" json:"life"`
|
||||
Scope FirewallScope `yaml:"scope" json:"scope"`
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallGoGroupAction struct {
|
||||
GroupId string `yaml:"groupId" json:"groupId"`
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallGoSetAction struct {
|
||||
GroupId string `yaml:"groupId" json:"groupId"`
|
||||
SetId string `yaml:"setId" json:"setId"`
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package firewallconfigs
|
||||
|
||||
type Action struct {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallJavascriptCookieAction struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
Life int32 `yaml:"life" json:"life"` // 有效期
|
||||
MaxFails int `yaml:"maxFails" json:"maxFails"` // 最大失败次数
|
||||
FailBlockTimeout int `yaml:"failBlockTimeout" json:"failBlockTimeout"` // 失败拦截时间
|
||||
Scope string `yaml:"scope" json:"scope"`
|
||||
FailBlockScopeAll bool `yaml:"failBlockScopeAll" json:"failBlockScopeAll"`
|
||||
}
|
||||
|
||||
func NewHTTPFirewallJavascriptCookieAction() *HTTPFirewallJavascriptCookieAction {
|
||||
return &HTTPFirewallJavascriptCookieAction{
|
||||
Life: 600,
|
||||
MaxFails: 100,
|
||||
FailBlockTimeout: 3600,
|
||||
Scope: FirewallScopeServer,
|
||||
FailBlockScopeAll: true,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallLogAction struct {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallNotifyAction struct {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallPost307Action struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
Life int32 `yaml:"life" json:"life"`
|
||||
Scope FirewallScope `yaml:"scope" json:"scope"`
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallRecordIPAction struct {
|
||||
Type string `yaml:"type" json:"type"`
|
||||
IPListId int64 `yaml:"ipListId" json:"ipListId"`
|
||||
Level string `yaml:"level" json:"level"`
|
||||
Timeout int32 `yaml:"timeout" json:"timeout"`
|
||||
Scope FirewallScope `yaml:"scope" json:"scope"`
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallTagAction struct {
|
||||
Tags []string `yaml:"tags" json:"tags"`
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallActionString = string
|
||||
|
||||
const (
|
||||
HTTPFirewallActionLog HTTPFirewallActionString = "log" // allow and log
|
||||
HTTPFirewallActionBlock HTTPFirewallActionString = "block" // block
|
||||
HTTPFirewallActionCaptcha HTTPFirewallActionString = "captcha" // block and show captcha
|
||||
HTTPFirewallActionJavascriptCookie HTTPFirewallActionString = "js_cookie" // Javascript Cookie
|
||||
HTTPFirewallActionNotify HTTPFirewallActionString = "notify" // 告警
|
||||
HTTPFirewallActionGet302 HTTPFirewallActionString = "get_302" // 针对GET的302重定向认证
|
||||
HTTPFirewallActionPost307 HTTPFirewallActionString = "post_307" // 针对POST的307重定向认证
|
||||
HTTPFirewallActionRecordIP HTTPFirewallActionString = "record_ip" // 记录IP
|
||||
HTTPFirewallActionTag HTTPFirewallActionString = "tag" // 标签
|
||||
HTTPFirewallActionPage HTTPFirewallActionString = "page" // 显示页面
|
||||
HTTPFirewallActionRedirect HTTPFirewallActionString = "redirect" // 跳转
|
||||
HTTPFirewallActionAllow HTTPFirewallActionString = "allow" // allow
|
||||
HTTPFirewallActionGoGroup HTTPFirewallActionString = "go_group" // go to next rule group
|
||||
HTTPFirewallActionGoSet HTTPFirewallActionString = "go_set" // go to next rule set
|
||||
)
|
||||
@@ -0,0 +1,101 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var AllActions = []*HTTPFirewallActionDefinition{
|
||||
{
|
||||
Name: "显示网页",
|
||||
Code: HTTPFirewallActionPage,
|
||||
Description: "显示请求被拦截的网页。",
|
||||
Category: HTTPFirewallActionCategoryBlock,
|
||||
},
|
||||
{
|
||||
Name: "阻止",
|
||||
Code: HTTPFirewallActionBlock,
|
||||
Description: "阻止请求并中断当前连接,并自动将当前客户端IP加入到系统黑名单;使用此动作时,请先自行严格测试设置的规则是否正确,避免因错误封禁而导致用户无法正常访问的严重后果!",
|
||||
Category: HTTPFirewallActionCategoryBlock,
|
||||
},
|
||||
{
|
||||
Name: "Captcha人机识别",
|
||||
Code: HTTPFirewallActionCaptcha,
|
||||
Description: "在浏览器使用人机识别机制(比如验证码)来验证客户端。",
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "JS Cookie验证",
|
||||
Code: HTTPFirewallActionJavascriptCookie,
|
||||
Description: "首次访问网站时通过Javascript设置Cookie来验证请求。",
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "记录IP",
|
||||
Code: HTTPFirewallActionRecordIP,
|
||||
Description: "将此IP记录到某个IP名单中。",
|
||||
Category: HTTPFirewallActionCategoryBlock,
|
||||
},
|
||||
{
|
||||
Name: "跳转",
|
||||
Code: HTTPFirewallActionRedirect,
|
||||
Description: "跳转到新的URL。",
|
||||
Category: HTTPFirewallActionCategoryBlock,
|
||||
},
|
||||
{
|
||||
Name: "允许通过",
|
||||
Code: HTTPFirewallActionAllow,
|
||||
Description: "允许跳过规则集正常通过防火墙。",
|
||||
Category: HTTPFirewallActionCategoryAllow,
|
||||
},
|
||||
{
|
||||
Name: "允许并记录日志",
|
||||
Code: HTTPFirewallActionLog,
|
||||
Description: "允许正常通过并记录到日志。",
|
||||
Category: HTTPFirewallActionCategoryAllow,
|
||||
},
|
||||
{
|
||||
Name: "标签",
|
||||
Code: HTTPFirewallActionTag,
|
||||
Description: "为匹配的请求打上标签。",
|
||||
Category: HTTPFirewallActionCategoryAllow,
|
||||
},
|
||||
{
|
||||
Name: "告警",
|
||||
Code: HTTPFirewallActionNotify,
|
||||
Description: "向集群的消息接收人发送消息通知(商业版)。",
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "GET 302",
|
||||
Code: HTTPFirewallActionGet302,
|
||||
Description: "通过302重定向GET请求验证客户端真实性。",
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "POST 307",
|
||||
Code: HTTPFirewallActionPost307,
|
||||
Description: "通过307重定向POST请求验证客户端真实性。",
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "跳到下一个规则分组",
|
||||
Code: HTTPFirewallActionGoGroup,
|
||||
Type: reflect.TypeOf(new(HTTPFirewallGoGroupAction)).Elem(),
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
{
|
||||
Name: "跳到下一个规则集",
|
||||
Code: HTTPFirewallActionGoSet,
|
||||
Type: reflect.TypeOf(new(HTTPFirewallGoSetAction)).Elem(),
|
||||
Category: HTTPFirewallActionCategoryVerify,
|
||||
},
|
||||
}
|
||||
|
||||
func FindActionDefinition(actionCode HTTPFirewallActionString) *HTTPFirewallActionDefinition {
|
||||
for _, def := range AllActions {
|
||||
if def.Code == actionCode {
|
||||
return def
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package firewallconfigs
|
||||
@@ -0,0 +1,28 @@
|
||||
package firewallconfigs
|
||||
|
||||
type KeyValue struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func NewKeyValue(name string, value string) *KeyValue {
|
||||
return &KeyValue{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPFirewallCheckpointDefinition check point definition
|
||||
type HTTPFirewallCheckpointDefinition struct {
|
||||
Name string `json:"name"` // 名称
|
||||
Description string `json:"description"` // 描述
|
||||
Prefix string `json:"prefix"` // 前缀
|
||||
IsRequest bool `json:"isRequest"` // 是否为请求
|
||||
HasParams bool `json:"hasParams"` // 是否有子参数
|
||||
Params []*KeyValue `json:"params"` // 参数
|
||||
Options []OptionInterface `json:"options"` // 选项
|
||||
IsComposed bool `json:"isComposed"` // 是否为组合的checkpoint
|
||||
Priority int `json:"priority"` // 优先级
|
||||
DataType string `json:"dataType"` // 数据类型:number, bool等
|
||||
Version string `json:"version"` // 被加入的版本号
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// AllCheckpoints all check points list
|
||||
var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{
|
||||
{
|
||||
Name: "通用请求报头长度限制",
|
||||
Prefix: "requestGeneralHeaderLength",
|
||||
Description: "通用报头比如Cache-Control、Accept之类的长度限制,防止缓冲区溢出攻击。",
|
||||
IsRequest: true,
|
||||
IsComposed: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "通用响应报头长度限制",
|
||||
Prefix: "responseGeneralHeaderLength",
|
||||
Description: "通用报头比如Cache-Control、Date之类的长度限制,防止缓冲区溢出攻击。",
|
||||
IsRequest: false,
|
||||
IsComposed: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "客户端地址(IP)",
|
||||
Prefix: "remoteAddr",
|
||||
Description: "试图通过分析X-Forwarded-For等报头获取的客户端地址,比如192.168.1.100,存在伪造的可能。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "客户端源地址(IP)",
|
||||
Prefix: "rawRemoteAddr",
|
||||
Description: "直接连接的客户端地址,比如192.168.1.100。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "客户端端口",
|
||||
Prefix: "remotePort",
|
||||
Description: "直接连接的客户端地址端口。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "客户端用户名",
|
||||
Prefix: "remoteUser",
|
||||
Description: "通过BasicAuth登录的客户端用户名。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求URI",
|
||||
Prefix: "requestURI",
|
||||
Description: "包含URL参数的请求URI,类似于 /hello/world?lang=go,不包含域名部分。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求路径",
|
||||
Prefix: "requestPath",
|
||||
Description: "不包含URL参数的请求路径,类似于 /hello/world,不包含域名部分。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求完整URL",
|
||||
Prefix: "requestURL",
|
||||
Description: "完整的请求URL,包含协议、域名、请求路径、参数等,类似于 https://example.com/hello?name=lily 。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求内容长度",
|
||||
Prefix: "requestLength",
|
||||
Description: "请求报头中的Content-Length。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求体内容",
|
||||
Prefix: "requestBody",
|
||||
Description: "通常在POST或者PUT等操作时会附带请求体,最大限制32M。",
|
||||
IsRequest: true,
|
||||
Priority: 5,
|
||||
},
|
||||
{
|
||||
Name: "请求URI和请求体组合",
|
||||
Prefix: "requestAll",
|
||||
Description: "${requestURI}和${requestBody}组合。",
|
||||
IsRequest: true,
|
||||
Priority: 5,
|
||||
},
|
||||
{
|
||||
Name: "请求表单参数",
|
||||
Prefix: "requestForm",
|
||||
Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M。",
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 5,
|
||||
},
|
||||
{
|
||||
Name: "上传文件",
|
||||
Prefix: "requestUpload",
|
||||
Description: "获取POST上传的文件信息,最大请求体限制32M。",
|
||||
Params: []*KeyValue{
|
||||
NewKeyValue("最小文件尺寸", "minSize"),
|
||||
NewKeyValue("最大文件尺寸", "maxSize"),
|
||||
NewKeyValue("扩展名(如.txt)", "ext"),
|
||||
NewKeyValue("原始文件名", "name"),
|
||||
NewKeyValue("表单字段名", "field"),
|
||||
},
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 20,
|
||||
},
|
||||
{
|
||||
Name: "请求JSON参数",
|
||||
Prefix: "requestJSON",
|
||||
Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据。",
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 5,
|
||||
},
|
||||
{
|
||||
Name: "请求方法",
|
||||
Prefix: "requestMethod",
|
||||
Description: "比如GET、POST。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求协议",
|
||||
Prefix: "scheme",
|
||||
Description: "比如http或https。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "HTTP协议版本",
|
||||
Prefix: "proto",
|
||||
Description: "比如HTTP/1.1。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "主机名",
|
||||
Prefix: "host",
|
||||
Description: "比如example.com。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "CNAME",
|
||||
Prefix: "cname",
|
||||
Description: "当前网站服务CNAME,比如38b48e4f.example.com。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "是否为CNAME",
|
||||
Prefix: "isCNAME",
|
||||
Description: "是否为CNAME,值为1(是)或0(否)。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
DataType: "bool",
|
||||
},
|
||||
{
|
||||
Name: "请求来源",
|
||||
Prefix: "refererOrigin",
|
||||
Description: "请求报头中的Referer和Origin值。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
Version: "1.3.2",
|
||||
},
|
||||
{
|
||||
Name: "请求来源Referer",
|
||||
Prefix: "referer",
|
||||
Description: "请求报头中的Referer值。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "客户端信息",
|
||||
Prefix: "userAgent",
|
||||
Description: "比如Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "内容类型",
|
||||
Prefix: "contentType",
|
||||
Description: "请求报头的Content-Type。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "所有cookie组合字符串",
|
||||
Prefix: "cookies",
|
||||
Description: "比如sid=IxZVPFhE&city=beijing&uid=18237。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "单个cookie值",
|
||||
Prefix: "cookie",
|
||||
Description: "单个cookie值。",
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "所有URL参数组合",
|
||||
Prefix: "args",
|
||||
Description: "比如name=lu&age=20。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "单个URL参数值",
|
||||
Prefix: "arg",
|
||||
Description: "单个URL参数值。",
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "所有请求报头内容",
|
||||
Prefix: "headers",
|
||||
Description: "使用换行符(\\n)隔开的报头内容字符串,每行均为\"NAME: VALUE格式\"。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "所有请求报头名称",
|
||||
Prefix: "headerNames",
|
||||
Description: "使用换行符(\\n)隔开的报头名称字符串,每行一个名称。",
|
||||
IsRequest: true,
|
||||
Priority: 100,
|
||||
Version: "1.3.2",
|
||||
},
|
||||
{
|
||||
Name: "单个请求报头值",
|
||||
Prefix: "header",
|
||||
Description: "单个报头值。",
|
||||
IsRequest: true,
|
||||
HasParams: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "请求报头最大长度",
|
||||
Prefix: "headerMaxLength",
|
||||
Description: "最长的请求报头的长度。",
|
||||
IsRequest: true,
|
||||
HasParams: false,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "国家/地区名称",
|
||||
Prefix: "geoCountryName",
|
||||
Description: "当前客户端所处国家/地区名称。",
|
||||
IsRequest: true,
|
||||
HasParams: false,
|
||||
Priority: 90,
|
||||
},
|
||||
{
|
||||
Name: "省份名称",
|
||||
Prefix: "geoProvinceName",
|
||||
Description: "当前客户端所处中国省份名称。",
|
||||
IsRequest: true,
|
||||
HasParams: false,
|
||||
Priority: 90,
|
||||
},
|
||||
{
|
||||
Name: "城市名称",
|
||||
Prefix: "geoCityName",
|
||||
Description: "当前客户端所处中国城市名称。",
|
||||
IsRequest: true,
|
||||
HasParams: false,
|
||||
Priority: 90,
|
||||
},
|
||||
{
|
||||
Name: "ISP名称",
|
||||
Prefix: "ispName",
|
||||
Description: "当前客户端所处ISP名称。",
|
||||
IsRequest: true,
|
||||
HasParams: false,
|
||||
Priority: 90,
|
||||
},
|
||||
{
|
||||
Name: "CC统计",
|
||||
Prefix: "cc2",
|
||||
Description: "对统计对象进行统计。",
|
||||
HasParams: false,
|
||||
IsRequest: true,
|
||||
IsComposed: true,
|
||||
Priority: 10,
|
||||
},
|
||||
{
|
||||
Name: "防盗链",
|
||||
Prefix: "refererBlock",
|
||||
Description: "对统计对象进行统计。",
|
||||
HasParams: false,
|
||||
IsRequest: true,
|
||||
IsComposed: true,
|
||||
Priority: 20,
|
||||
},
|
||||
{
|
||||
Name: "CC统计(旧)",
|
||||
Prefix: "cc",
|
||||
Description: "统计某段时间段内的请求信息(不推荐再使用,请使用新的CC2统计代替)。",
|
||||
HasParams: true,
|
||||
Params: []*KeyValue{
|
||||
NewKeyValue("请求数", "requests"),
|
||||
},
|
||||
Options: []OptionInterface{
|
||||
&FieldOption{
|
||||
Type: "field",
|
||||
Name: "统计周期",
|
||||
Code: "period",
|
||||
Value: "60",
|
||||
IsRequired: false,
|
||||
Size: 8,
|
||||
Comment: "",
|
||||
Placeholder: "",
|
||||
RightLabel: "秒",
|
||||
MaxLength: 8,
|
||||
Validate: func(value string) (ok bool, message string) {
|
||||
if regexp.MustCompile(`^\d+$`).MatchString(value) {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
message = "周期需要是一个整数数字"
|
||||
return
|
||||
},
|
||||
},
|
||||
&OptionsOption{
|
||||
Type: "options",
|
||||
Name: "用户识别读取来源",
|
||||
Code: "userType",
|
||||
Value: "",
|
||||
IsRequired: false,
|
||||
Size: 10,
|
||||
Comment: "",
|
||||
RightLabel: "",
|
||||
Validate: nil,
|
||||
Options: []maps.Map{
|
||||
{
|
||||
"name": "IP",
|
||||
"value": "ip",
|
||||
},
|
||||
{
|
||||
"name": "Cookie",
|
||||
"value": "cookie",
|
||||
},
|
||||
{
|
||||
"name": "URL参数",
|
||||
"value": "get",
|
||||
},
|
||||
{
|
||||
"name": "POST参数",
|
||||
"value": "post",
|
||||
},
|
||||
{
|
||||
"name": "HTTP Header",
|
||||
"value": "header",
|
||||
},
|
||||
},
|
||||
},
|
||||
&FieldOption{
|
||||
Type: "field",
|
||||
Name: "用户识别字段",
|
||||
Code: "userField",
|
||||
Comment: "识别用户的唯一性字段,在用户读取来源不是IP时使用",
|
||||
},
|
||||
&FieldOption{
|
||||
Type: "field",
|
||||
Name: "字段读取位置",
|
||||
Code: "userIndex",
|
||||
Size: 5,
|
||||
MaxLength: 5,
|
||||
Comment: "读取用户识别字段的位置,从0开始,比如user12345的数字ID 12345的位置就是5,在用户读取来源不是IP时使用",
|
||||
},
|
||||
},
|
||||
IsRequest: true,
|
||||
Priority: 10,
|
||||
},
|
||||
{
|
||||
Name: "响应状态码",
|
||||
Prefix: "status",
|
||||
Description: "响应状态码,比如200、404、500。",
|
||||
IsRequest: false,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "响应报头",
|
||||
Prefix: "responseHeader",
|
||||
Description: "响应报头值。",
|
||||
IsRequest: false,
|
||||
HasParams: true,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
Name: "响应内容",
|
||||
Prefix: "responseBody",
|
||||
Description: "响应内容字符串。",
|
||||
IsRequest: false,
|
||||
Priority: 5,
|
||||
},
|
||||
{
|
||||
Name: "响应内容长度",
|
||||
Prefix: "bytesSent",
|
||||
Description: "响应内容长度,通过响应的报头Content-Length获取。",
|
||||
IsRequest: false,
|
||||
Priority: 100,
|
||||
},
|
||||
}
|
||||
|
||||
// FindCheckpointDefinition 查找Checkpoint定义
|
||||
func FindCheckpointDefinition(prefix string) *HTTPFirewallCheckpointDefinition {
|
||||
for _, checkpoint := range AllCheckpoints {
|
||||
if checkpoint.Prefix == prefix {
|
||||
return checkpoint
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckCheckpointIsComposed 判断Checkpoint是否为组合的
|
||||
func CheckCheckpointIsComposed(prefix string) bool {
|
||||
for _, checkpoint := range AllCheckpoints {
|
||||
if checkpoint.Prefix == prefix {
|
||||
return checkpoint.IsComposed
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuleCheckpoint_Markdown(t *testing.T) {
|
||||
var result = []string{}
|
||||
for _, def := range firewallconfigs.AllCheckpoints {
|
||||
def.Description = strings.ReplaceAll(def.Description, "<code-label>", "`")
|
||||
def.Description = strings.ReplaceAll(def.Description, "</code-label>", "`")
|
||||
|
||||
var row = "## " + def.Name + "\n"
|
||||
row += "* 名称:" + def.Name + "\n"
|
||||
row += "* 代号:`${" + def.Prefix + "}`\n"
|
||||
row += "* 描述:" + def.Description + "\n"
|
||||
if len(def.Version) > 0 {
|
||||
row += "* 版本:v" + def.Version + "\n"
|
||||
}
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
fmt.Print(strings.Join(result, "\n") + "\n")
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ipconfigs"
|
||||
)
|
||||
|
||||
// HTTPFirewallInboundConfig HTTP防火墙入口配置
|
||||
type HTTPFirewallInboundConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
GroupRefs []*HTTPFirewallRuleGroupRef `yaml:"groupRefs" json:"groupRefs"`
|
||||
Groups []*HTTPFirewallRuleGroup `yaml:"groups" json:"groups"`
|
||||
|
||||
// 地区相关
|
||||
Region *HTTPFirewallRegionConfig `yaml:"region" json:"region"`
|
||||
|
||||
// IP名单
|
||||
AllowListRef *ipconfigs.IPListRef `yaml:"whiteListRef" json:"whiteListRef"`
|
||||
DenyListRef *ipconfigs.IPListRef `yaml:"blackListRef" json:"blackListRef"`
|
||||
GreyListRef *ipconfigs.IPListRef `yaml:"greyListRef" json:"greyListRef"`
|
||||
|
||||
// 绑定的IP名单
|
||||
PublicAllowListRefs []*ipconfigs.IPListRef `yaml:"publicWhiteListRefs" json:"publicWhiteListRefs"`
|
||||
PublicDenyListRefs []*ipconfigs.IPListRef `yaml:"publicBlackListRefs" json:"publicBlackListRefs"`
|
||||
PublicGreyListRefs []*ipconfigs.IPListRef `yaml:"publicGreyListRefs" json:"publicGreyListRefs"`
|
||||
|
||||
allAllowListRefs []*ipconfigs.IPListRef
|
||||
allDenyListRefs []*ipconfigs.IPListRef
|
||||
allGreyListRefs []*ipconfigs.IPListRef
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *HTTPFirewallInboundConfig) Init() error {
|
||||
for _, group := range this.Groups {
|
||||
err := group.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.Region != nil {
|
||||
err := this.Region.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
this.allAllowListRefs = []*ipconfigs.IPListRef{}
|
||||
if this.AllowListRef != nil {
|
||||
this.allAllowListRefs = append(this.allAllowListRefs, this.AllowListRef)
|
||||
}
|
||||
if len(this.PublicAllowListRefs) > 0 {
|
||||
this.allAllowListRefs = append(this.allAllowListRefs, this.PublicAllowListRefs...)
|
||||
}
|
||||
|
||||
this.allDenyListRefs = []*ipconfigs.IPListRef{}
|
||||
if this.DenyListRef != nil {
|
||||
this.allDenyListRefs = append(this.allDenyListRefs, this.DenyListRef)
|
||||
}
|
||||
if len(this.PublicDenyListRefs) > 0 {
|
||||
this.allDenyListRefs = append(this.allDenyListRefs, this.PublicDenyListRefs...)
|
||||
}
|
||||
|
||||
this.allGreyListRefs = []*ipconfigs.IPListRef{}
|
||||
if this.GreyListRef != nil {
|
||||
this.allGreyListRefs = append(this.allGreyListRefs, this.GreyListRef)
|
||||
}
|
||||
if len(this.PublicGreyListRefs) > 0 {
|
||||
this.allGreyListRefs = append(this.allGreyListRefs, this.PublicGreyListRefs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindGroupWithCode 根据Code查找Group
|
||||
func (this *HTTPFirewallInboundConfig) FindGroupWithCode(code string) *HTTPFirewallRuleGroup {
|
||||
for _, group := range this.Groups {
|
||||
if group.Code == code {
|
||||
return group
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRuleGroup 删除某个分组
|
||||
func (this *HTTPFirewallInboundConfig) RemoveRuleGroup(groupId int64) {
|
||||
groups := []*HTTPFirewallRuleGroup{}
|
||||
refs := []*HTTPFirewallRuleGroupRef{}
|
||||
for _, g := range this.Groups {
|
||||
if g.Id == groupId {
|
||||
continue
|
||||
}
|
||||
groups = append(groups, g)
|
||||
}
|
||||
for _, ref := range this.GroupRefs {
|
||||
if ref.GroupId == groupId {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
this.Groups = groups
|
||||
this.GroupRefs = refs
|
||||
}
|
||||
|
||||
// AddPublicList 绑定公用的IP名单
|
||||
func (this *HTTPFirewallInboundConfig) AddPublicList(listId int64, listType string) {
|
||||
var refs []*ipconfigs.IPListRef
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
refs = this.PublicDenyListRefs
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
refs = this.PublicAllowListRefs
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
refs = this.PublicGreyListRefs
|
||||
}
|
||||
var found = false
|
||||
for _, ref := range refs {
|
||||
if ref.ListId == listId {
|
||||
found = true
|
||||
ref.IsOn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
refs = append(refs, &ipconfigs.IPListRef{
|
||||
IsOn: true,
|
||||
ListId: listId,
|
||||
})
|
||||
}
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
this.PublicDenyListRefs = refs
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
this.PublicAllowListRefs = refs
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
this.PublicGreyListRefs = refs
|
||||
}
|
||||
}
|
||||
|
||||
// RemovePublicList 解绑公用的IP名单
|
||||
func (this *HTTPFirewallInboundConfig) RemovePublicList(listId int64, listType string) {
|
||||
var refs []*ipconfigs.IPListRef
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
refs = this.PublicDenyListRefs
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
refs = this.PublicAllowListRefs
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
refs = this.PublicGreyListRefs
|
||||
}
|
||||
var newRefs = []*ipconfigs.IPListRef{}
|
||||
for _, ref := range refs {
|
||||
if ref.ListId == listId {
|
||||
continue
|
||||
}
|
||||
newRefs = append(newRefs, ref)
|
||||
}
|
||||
switch listType {
|
||||
case ipconfigs.IPListTypeBlack:
|
||||
this.PublicDenyListRefs = newRefs
|
||||
case ipconfigs.IPListTypeWhite:
|
||||
this.PublicAllowListRefs = newRefs
|
||||
case ipconfigs.IPListTypeGrey:
|
||||
this.PublicGreyListRefs = newRefs
|
||||
}
|
||||
}
|
||||
|
||||
// AllAllowListRefs 获取所有允许的IP名单
|
||||
func (this *HTTPFirewallInboundConfig) AllAllowListRefs() []*ipconfigs.IPListRef {
|
||||
return this.allAllowListRefs
|
||||
}
|
||||
|
||||
// AllDenyListRefs 获取所有禁止的IP名单
|
||||
func (this *HTTPFirewallInboundConfig) AllDenyListRefs() []*ipconfigs.IPListRef {
|
||||
return this.allDenyListRefs
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallOutboundConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
GroupRefs []*HTTPFirewallRuleGroupRef `yaml:"groupRefs" json:"groupRefs"`
|
||||
Groups []*HTTPFirewallRuleGroup `yaml:"groups" json:"groups"`
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *HTTPFirewallOutboundConfig) Init() error {
|
||||
for _, group := range this.Groups {
|
||||
err := group.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据Code查找Group
|
||||
func (this *HTTPFirewallOutboundConfig) FindGroupWithCode(code string) *HTTPFirewallRuleGroup {
|
||||
for _, group := range this.Groups {
|
||||
if group.Code == code {
|
||||
return group
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除某个分组
|
||||
func (this *HTTPFirewallOutboundConfig) RemoveRuleGroup(groupId int64) {
|
||||
groups := []*HTTPFirewallRuleGroup{}
|
||||
refs := []*HTTPFirewallRuleGroupRef{}
|
||||
for _, g := range this.Groups {
|
||||
if g.Id == groupId {
|
||||
continue
|
||||
}
|
||||
groups = append(groups, g)
|
||||
}
|
||||
for _, ref := range this.GroupRefs {
|
||||
if ref.GroupId == groupId {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
this.Groups = groups
|
||||
this.GroupRefs = refs
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPFirewallPageAction default page action
|
||||
type HTTPFirewallPageAction struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
|
||||
Status int `yaml:"status" json:"status"`
|
||||
Body string `yaml:"body" json:"body"`
|
||||
}
|
||||
|
||||
func NewHTTPFirewallPageAction() *HTTPFirewallPageAction {
|
||||
return &HTTPFirewallPageAction{
|
||||
Status: http.StatusForbidden,
|
||||
Body: `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
<style>
|
||||
address { line-height: 1.8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>403 Forbidden By WAF</h1>
|
||||
<address>Connection: ${remoteAddr} (Client) -> ${serverAddr} (Server)</address>
|
||||
<address>Request ID: ${requestId}</address>
|
||||
</body>
|
||||
</html>`,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
const DefaultMaxRequestBodySize int64 = 256 << 10
|
||||
|
||||
// HTTPFirewallPolicy 防火墙策略
|
||||
type HTTPFirewallPolicy struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
ServerId int64 `yaml:"serverId" json:"serverId"` // 所属网站ID
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
Inbound *HTTPFirewallInboundConfig `yaml:"inbound" json:"inbound"`
|
||||
Outbound *HTTPFirewallOutboundConfig `yaml:"outbound" json:"outbound"`
|
||||
BlockOptions *HTTPFirewallBlockAction `yaml:"blockOptions" json:"blockOptions"`
|
||||
CaptchaOptions *HTTPFirewallCaptchaAction `yaml:"captchaOptions" json:"captchaOptions"`
|
||||
PageOptions *HTTPFirewallPageAction `yaml:"pageOptions" json:"pageOptions"`
|
||||
Get302Options *HTTPFirewallGet302Action `yaml:"get302Options" json:"get302Options"`
|
||||
Post307Options *HTTPFirewallPost307Action `yaml:"post307Options" json:"post307Options"`
|
||||
JSCookieOptions *HTTPFirewallJavascriptCookieAction `yaml:"jsCookieOptions" json:"jsCookieOptions"`
|
||||
|
||||
Mode FirewallMode `yaml:"mode" json:"mode"`
|
||||
UseLocalFirewall bool `yaml:"useLocalFirewall" json:"useLocalFirewall"`
|
||||
SYNFlood *SYNFloodConfig `yaml:"synFlood" json:"synFlood"`
|
||||
Log *HTTPFirewallPolicyLogConfig `yaml:"log" json:"log"` // 强制记录日志
|
||||
MaxRequestBodySize int64 `yaml:"maxRequestBodySize" json:"maxRequestBodySize"` // 读取的请求最大尺寸
|
||||
DenyCountryHTML string `yaml:"denyCountryHTML" json:"denyCountryHTML"` // 默认地区禁用提示
|
||||
DenyProvinceHTML string `yaml:"denyProvinceHTML" json:"denyProvinceHTML"` // 默认省份禁用提示
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *HTTPFirewallPolicy) Init() error {
|
||||
if this.Inbound != nil {
|
||||
err := this.Inbound.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.Outbound != nil {
|
||||
err := this.Outbound.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.SYNFlood != nil {
|
||||
err := this.SYNFlood.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.Log != nil {
|
||||
err := this.Log.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllRuleGroups 获取所有分组
|
||||
func (this *HTTPFirewallPolicy) AllRuleGroups() []*HTTPFirewallRuleGroup {
|
||||
result := []*HTTPFirewallRuleGroup{}
|
||||
if this.Inbound != nil {
|
||||
result = append(result, this.Inbound.Groups...)
|
||||
}
|
||||
if this.Outbound != nil {
|
||||
result = append(result, this.Outbound.Groups...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FindRuleGroupWithCode 根据代号查找分组
|
||||
func (this *HTTPFirewallPolicy) FindRuleGroupWithCode(code string) *HTTPFirewallRuleGroup {
|
||||
for _, g := range this.AllRuleGroups() {
|
||||
if g.Code == code {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindRuleGroupWithName 根据名称查找分组
|
||||
func (this *HTTPFirewallPolicy) FindRuleGroupWithName(name string) *HTTPFirewallRuleGroup {
|
||||
for _, g := range this.AllRuleGroups() {
|
||||
if g.Name == name {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindRuleGroup 根据ID查找分组
|
||||
func (this *HTTPFirewallPolicy) FindRuleGroup(groupId int64) *HTTPFirewallRuleGroup {
|
||||
for _, g := range this.AllRuleGroups() {
|
||||
if g.Id == groupId {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRuleGroup 删除某个分组
|
||||
func (this *HTTPFirewallPolicy) RemoveRuleGroup(groupId int64) {
|
||||
if this.Inbound != nil {
|
||||
this.Inbound.RemoveRuleGroup(groupId)
|
||||
}
|
||||
if this.Outbound != nil {
|
||||
this.Outbound.RemoveRuleGroup(groupId)
|
||||
}
|
||||
}
|
||||
|
||||
// InboundJSON Inbound JSON
|
||||
func (this *HTTPFirewallPolicy) InboundJSON() ([]byte, error) {
|
||||
if this.Inbound == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
groups := this.Inbound.Groups
|
||||
this.Inbound.Groups = nil
|
||||
defer func() {
|
||||
this.Inbound.Groups = groups
|
||||
}()
|
||||
return json.Marshal(this.Inbound)
|
||||
}
|
||||
|
||||
// OutboundJSON Outbound JSON
|
||||
func (this *HTTPFirewallPolicy) OutboundJSON() ([]byte, error) {
|
||||
if this.Inbound == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
groups := this.Outbound.Groups
|
||||
this.Outbound.Groups = nil
|
||||
defer func() {
|
||||
this.Outbound.Groups = groups
|
||||
}()
|
||||
return json.Marshal(this.Outbound)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
var DefaultHTTPFirewallPolicyLogConfig = &HTTPFirewallPolicyLogConfig{
|
||||
IsOn: true,
|
||||
RequestBody: true,
|
||||
RegionDenying: false,
|
||||
}
|
||||
|
||||
type HTTPFirewallPolicyLogConfig struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
RequestBody bool `yaml:"requestBody" json:"requestBody"` // 是否记录RequestBody
|
||||
RegionDenying bool `yaml:"regionDenying" json:"regionDenying"` // 是否记录区域封禁日志
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallPolicyLogConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package firewallconfigs
|
||||
|
||||
type ServerCaptchaType = string
|
||||
|
||||
const (
|
||||
ServerCaptchaTypeNone ServerCaptchaType = "none" // 不设置表示策略整体配置
|
||||
ServerCaptchaTypeDefault ServerCaptchaType = CaptchaTypeDefault
|
||||
ServerCaptchaTypeOneClick ServerCaptchaType = CaptchaTypeOneClick
|
||||
ServerCaptchaTypeSlide ServerCaptchaType = CaptchaTypeSlide
|
||||
ServerCaptchaTypeGeeTest ServerCaptchaType = CaptchaTypeGeeTest
|
||||
)
|
||||
|
||||
type HTTPFirewallRef struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
FirewallPolicyId int64 `yaml:"firewallPolicyId" json:"firewallPolicyId"`
|
||||
IgnoreGlobalRules bool `yaml:"ignoreGlobalRules" json:"ignoreGlobalRules"` // 忽略系统定义的全局规则
|
||||
|
||||
DefaultCaptchaType ServerCaptchaType `yaml:"defaultCaptchaType" json:"defaultCaptchaType"` // 默认人机识别方式
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRef) Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HTTPFirewallRegionConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
|
||||
AllowCountryIds []int64 `yaml:"allowCountryIds" json:"allowCountryIds"` // 允许的国家/地区
|
||||
DenyCountryIds []int64 `yaml:"denyCountryIds" json:"denyCountryIds"` // 封禁的国家/地区
|
||||
AllowProvinceIds []int64 `yaml:"allowProvinceIds" json:"allowProvinceIds"` // 允许的省或自治区
|
||||
DenyProvinceIds []int64 `yaml:"denyProvinceIds" json:"denyProvinceIds"` // 封禁的省或自治区
|
||||
AllowSearchEngine bool `yaml:"allowSearchEngine" json:"allowSearchEngine"` // 允许搜索引擎
|
||||
|
||||
CountryOnlyURLPatterns []*shared.URLPattern `yaml:"countryOnlyURLPatterns" json:"countryOnlyURLPatterns"` // 仅限的URL
|
||||
CountryExceptURLPatterns []*shared.URLPattern `yaml:"countryExceptURLPatterns" json:"countryExceptURLPatterns"` // 排除的URL
|
||||
CountryHTML string `yaml:"countryHTML" json:"countryHTML"` // 提示HTML
|
||||
|
||||
ProvinceOnlyURLPatterns []*shared.URLPattern `yaml:"provinceOnlyURLPatterns" json:"provinceOnlyURLPatterns"` // 仅限的URL
|
||||
ProvinceExceptURLPatterns []*shared.URLPattern `yaml:"provinceExceptURLPatterns" json:"provinceExceptURLPatterns"` // 排除的URL
|
||||
ProvinceHTML string `yaml:"provinceHTML" json:"provinceHTML"` // 提示HTML
|
||||
|
||||
isNotEmpty bool
|
||||
|
||||
allowCountryIdMap map[int64]bool
|
||||
denyCountryIdMap map[int64]bool
|
||||
allowProvinceIdMap map[int64]bool
|
||||
denyProvinceIdMap map[int64]bool
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) Init() error {
|
||||
// countries and provinces
|
||||
this.isNotEmpty = len(this.AllowCountryIds) > 0 || len(this.AllowProvinceIds) > 0 || len(this.DenyCountryIds) > 0 || len(this.DenyProvinceIds) > 0
|
||||
this.allowCountryIdMap = map[int64]bool{}
|
||||
for _, countryId := range this.AllowCountryIds {
|
||||
this.allowCountryIdMap[countryId] = true
|
||||
}
|
||||
|
||||
this.denyCountryIdMap = map[int64]bool{}
|
||||
for _, countryId := range this.DenyCountryIds {
|
||||
this.denyCountryIdMap[countryId] = true
|
||||
}
|
||||
|
||||
this.CountryHTML = strings.TrimSpace(this.CountryHTML)
|
||||
|
||||
this.allowProvinceIdMap = map[int64]bool{}
|
||||
for _, provinceId := range this.AllowProvinceIds {
|
||||
this.allowProvinceIdMap[provinceId] = true
|
||||
}
|
||||
|
||||
this.denyProvinceIdMap = map[int64]bool{}
|
||||
for _, provinceId := range this.DenyProvinceIds {
|
||||
this.denyProvinceIdMap[provinceId] = true
|
||||
}
|
||||
|
||||
this.ProvinceHTML = strings.TrimSpace(this.ProvinceHTML)
|
||||
|
||||
// url patterns
|
||||
for _, pattern := range this.CountryExceptURLPatterns {
|
||||
err := pattern.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, pattern := range this.CountryOnlyURLPatterns {
|
||||
err := pattern.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, pattern := range this.ProvinceExceptURLPatterns {
|
||||
err := pattern.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, pattern := range this.ProvinceOnlyURLPatterns {
|
||||
err := pattern.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) IsNotEmpty() bool {
|
||||
return this.isNotEmpty
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) IsAllowedCountry(countryId int64, provinceId int64) bool {
|
||||
if len(this.allowCountryIdMap) > 0 {
|
||||
if this.allowCountryIdMap[countryId] {
|
||||
return true
|
||||
}
|
||||
|
||||
// china sub regions
|
||||
if countryId == regionconfigs.RegionChinaId && provinceId > 0 {
|
||||
if this.allowCountryIdMap[regionconfigs.RegionChinaIdHK] && provinceId == regionconfigs.RegionChinaProvinceIdHK {
|
||||
return true
|
||||
}
|
||||
if this.allowCountryIdMap[regionconfigs.RegionChinaIdMO] && provinceId == regionconfigs.RegionChinaProvinceIdMO {
|
||||
return true
|
||||
}
|
||||
if this.allowCountryIdMap[regionconfigs.RegionChinaIdTW] && provinceId == regionconfigs.RegionChinaProvinceIdTW {
|
||||
return true
|
||||
}
|
||||
if this.allowCountryIdMap[regionconfigs.RegionChinaIdMainland] && regionconfigs.CheckRegionProvinceIsInChinaMainland(provinceId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
if len(this.denyCountryIdMap) > 0 {
|
||||
if !this.denyCountryIdMap[countryId] {
|
||||
// china sub regions
|
||||
if countryId == regionconfigs.RegionChinaId && provinceId > 0 {
|
||||
if this.denyCountryIdMap[regionconfigs.RegionChinaIdHK] && provinceId == regionconfigs.RegionChinaProvinceIdHK {
|
||||
return false
|
||||
}
|
||||
if this.denyCountryIdMap[regionconfigs.RegionChinaIdMO] && provinceId == regionconfigs.RegionChinaProvinceIdMO {
|
||||
return false
|
||||
}
|
||||
if this.denyCountryIdMap[regionconfigs.RegionChinaIdTW] && provinceId == regionconfigs.RegionChinaProvinceIdTW {
|
||||
return false
|
||||
}
|
||||
if this.denyCountryIdMap[regionconfigs.RegionChinaIdMainland] && regionconfigs.CheckRegionProvinceIsInChinaMainland(provinceId) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) IsAllowedProvince(countryId int64, provinceId int64) bool {
|
||||
if countryId != regionconfigs.RegionChinaId {
|
||||
return true
|
||||
}
|
||||
if len(this.allowProvinceIdMap) > 0 {
|
||||
return this.allowProvinceIdMap[provinceId]
|
||||
}
|
||||
if len(this.denyProvinceIdMap) > 0 {
|
||||
return !this.denyProvinceIdMap[provinceId]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) MatchCountryURL(url string) bool {
|
||||
// except
|
||||
if len(this.CountryExceptURLPatterns) > 0 {
|
||||
for _, pattern := range this.CountryExceptURLPatterns {
|
||||
if pattern.Match(url) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(this.CountryOnlyURLPatterns) > 0 {
|
||||
for _, pattern := range this.CountryOnlyURLPatterns {
|
||||
if pattern.Match(url) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRegionConfig) MatchProvinceURL(url string) bool {
|
||||
// except
|
||||
if len(this.ProvinceExceptURLPatterns) > 0 {
|
||||
for _, pattern := range this.ProvinceExceptURLPatterns {
|
||||
if pattern.Match(url) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(this.ProvinceOnlyURLPatterns) > 0 {
|
||||
for _, pattern := range this.ProvinceOnlyURLPatterns {
|
||||
if pattern.Match(url) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHTTPFirewallRegionConfig_IsAllowed(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{1, 2, 3}
|
||||
config.DenyCountryIds = []int64{4, 5, 6}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedCountry(1, 1))
|
||||
a.IsFalse(config.IsAllowedCountry(0, 0))
|
||||
a.IsFalse(config.IsAllowedCountry(4, 0))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{1, 2, 3}
|
||||
config.DenyCountryIds = []int64{1, 2, 3}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedCountry(1, 1))
|
||||
a.IsFalse(config.IsAllowedCountry(0, 0))
|
||||
a.IsFalse(config.IsAllowedCountry(4, 0))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{}
|
||||
config.DenyCountryIds = []int64{1, 2, 3}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsFalse(config.IsAllowedCountry(1, 0))
|
||||
a.IsTrue(config.IsAllowedCountry(0, 0))
|
||||
a.IsTrue(config.IsAllowedCountry(4, 0))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowProvinceIds = []int64{1, 2, 3}
|
||||
config.DenyProvinceIds = []int64{4, 5, 6}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedProvince(1, 1))
|
||||
a.IsFalse(config.IsAllowedProvince(1, 0))
|
||||
a.IsFalse(config.IsAllowedProvince(1, 4))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowProvinceIds = []int64{}
|
||||
config.DenyProvinceIds = []int64{4, 5, 6}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedProvince(1, 1))
|
||||
a.IsTrue(config.IsAllowedProvince(1, 3))
|
||||
a.IsFalse(config.IsAllowedProvince(1, 4))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowProvinceIds = []int64{}
|
||||
config.DenyProvinceIds = []int64{}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedProvince(1, 1))
|
||||
a.IsTrue(config.IsAllowedProvince(1, 4))
|
||||
a.IsTrue(config.IsAllowedProvince(1, 4))
|
||||
}
|
||||
|
||||
// the greater China area: Taiwan & Hongkong & Macao
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{
|
||||
regionconfigs.RegionChinaIdHK,
|
||||
regionconfigs.RegionChinaIdMO,
|
||||
regionconfigs.RegionChinaIdTW,
|
||||
//regionconfigs.RegionChinaIdMainland,
|
||||
}
|
||||
config.DenyCountryIds = []int64{}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdHK))
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdTW))
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdMO))
|
||||
a.IsFalse(config.IsAllowedCountry(1, 1))
|
||||
a.IsFalse(config.IsAllowedCountry(1, 0))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{
|
||||
regionconfigs.RegionChinaIdHK,
|
||||
regionconfigs.RegionChinaIdMainland,
|
||||
}
|
||||
config.DenyCountryIds = []int64{}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdHK))
|
||||
a.IsFalse(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdTW))
|
||||
a.IsFalse(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdMO))
|
||||
a.IsTrue(config.IsAllowedCountry(1, 1))
|
||||
a.IsFalse(config.IsAllowedCountry(1, 0))
|
||||
}
|
||||
|
||||
{
|
||||
var config = &firewallconfigs.HTTPFirewallRegionConfig{}
|
||||
config.AllowCountryIds = []int64{}
|
||||
config.DenyCountryIds = []int64{
|
||||
regionconfigs.RegionChinaIdHK,
|
||||
regionconfigs.RegionChinaIdMainland,
|
||||
}
|
||||
err := config.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsFalse(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdHK))
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdTW))
|
||||
a.IsTrue(config.IsAllowedCountry(1, regionconfigs.RegionChinaProvinceIdMO))
|
||||
a.IsFalse(config.IsAllowedCountry(1, 1))
|
||||
a.IsTrue(config.IsAllowedCountry(1, 0))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_HTTPFirewallRegionConfig_Map(b *testing.B) {
|
||||
var m = map[int64]bool{}
|
||||
const max = 50
|
||||
for i := 0; i < max; i++ {
|
||||
m[int64(i)] = true
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = m[int64(i%max)]
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_HTTPFirewallRegionConfig_Slice(b *testing.B) {
|
||||
var m = []int64{}
|
||||
const max = 50
|
||||
for i := 0; i < max; i++ {
|
||||
m = append(m, int64(i))
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var l = int64(i % max)
|
||||
for _, v := range m {
|
||||
if v == l {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var namedParamReg = regexp.MustCompile(`^\${\s*(.+)\s*}$`)
|
||||
|
||||
type HTTPFirewallRule struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Param string `yaml:"param" json:"param"`
|
||||
ParamFilters []*ParamFilter `yaml:"paramFilters" json:"paramFilters"`
|
||||
Operator string `yaml:"operator" json:"operator"`
|
||||
Value string `yaml:"value" json:"value"`
|
||||
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
|
||||
IsComposed bool `yaml:"isComposed" json:"isComposed"`
|
||||
CheckpointOptions map[string]interface{} `yaml:"checkpointOptions" json:"checkpointOptions"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRule) Init() error {
|
||||
// TODO 执行更严谨的校验
|
||||
|
||||
switch this.Operator {
|
||||
case HTTPFirewallRuleOperatorMatch:
|
||||
_, err := regexp.Compile(this.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("regexp validate failed: %w, expression: %s", err, this.Value)
|
||||
}
|
||||
case HTTPFirewallRuleOperatorNotMatch:
|
||||
_, err := regexp.Compile(this.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("regexp validate failed: %w, expression: %s", err, this.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRule) Prefix() string {
|
||||
result := namedParamReg.FindStringSubmatch(this.Param)
|
||||
if len(result) > 0 {
|
||||
param := result[1]
|
||||
pieces := strings.Split(param, ".")
|
||||
return pieces[0]
|
||||
}
|
||||
return this.Param
|
||||
}
|
||||
|
||||
func (this *HTTPFirewallRule) Summary() string {
|
||||
return this.Param + " " + FindRuleOperatorName(this.Operator) + " " + this.Value
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package firewallconfigs
|
||||
|
||||
// HTTPFirewallRuleGroup 规则组
|
||||
type HTTPFirewallRuleGroup struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
Code string `yaml:"code" json:"code"`
|
||||
SetRefs []*HTTPFirewallRuleSetRef `yaml:"setRefs" json:"setRefs"`
|
||||
Sets []*HTTPFirewallRuleSet `yaml:"sets" json:"sets"`
|
||||
IsTemplate bool `yaml:"isTemplate" json:"isTemplate"`
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *HTTPFirewallRuleGroup) Init() error {
|
||||
for _, set := range this.Sets {
|
||||
err := set.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRuleSet 添加规则集
|
||||
func (this *HTTPFirewallRuleGroup) AddRuleSet(ruleSet *HTTPFirewallRuleSet) {
|
||||
this.Sets = append(this.Sets, ruleSet)
|
||||
}
|
||||
|
||||
// FindRuleSet 根据ID查找规则集
|
||||
func (this *HTTPFirewallRuleGroup) FindRuleSet(ruleSetId int64) *HTTPFirewallRuleSet {
|
||||
for _, set := range this.Sets {
|
||||
if set.Id == ruleSetId {
|
||||
return set
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindRuleSetWithCode 根据Code查找规则集
|
||||
func (this *HTTPFirewallRuleGroup) FindRuleSetWithCode(code string) *HTTPFirewallRuleSet {
|
||||
for _, set := range this.Sets {
|
||||
if set.Code == code {
|
||||
return set
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallRuleGroupRef struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
GroupId int64 `yaml:"groupId" json:"groupId"`
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallRuleOperator = string
|
||||
type HTTPFirewallRuleCaseInsensitive = string
|
||||
|
||||
const (
|
||||
HTTPFirewallRuleOperatorGt HTTPFirewallRuleOperator = "gt"
|
||||
HTTPFirewallRuleOperatorGte HTTPFirewallRuleOperator = "gte"
|
||||
HTTPFirewallRuleOperatorLt HTTPFirewallRuleOperator = "lt"
|
||||
HTTPFirewallRuleOperatorLte HTTPFirewallRuleOperator = "lte"
|
||||
HTTPFirewallRuleOperatorEq HTTPFirewallRuleOperator = "eq"
|
||||
HTTPFirewallRuleOperatorNeq HTTPFirewallRuleOperator = "neq"
|
||||
HTTPFirewallRuleOperatorEqString HTTPFirewallRuleOperator = "eq string"
|
||||
HTTPFirewallRuleOperatorNeqString HTTPFirewallRuleOperator = "neq string"
|
||||
HTTPFirewallRuleOperatorMatch HTTPFirewallRuleOperator = "match"
|
||||
HTTPFirewallRuleOperatorNotMatch HTTPFirewallRuleOperator = "not match"
|
||||
HTTPFirewallRuleOperatorWildcardMatch HTTPFirewallRuleOperator = "wildcard match"
|
||||
HTTPFirewallRuleOperatorWildcardNotMatch HTTPFirewallRuleOperator = "wildcard not match"
|
||||
HTTPFirewallRuleOperatorContains HTTPFirewallRuleOperator = "contains"
|
||||
HTTPFirewallRuleOperatorNotContains HTTPFirewallRuleOperator = "not contains"
|
||||
HTTPFirewallRuleOperatorContainsAnyWord HTTPFirewallRuleOperator = "contains any word"
|
||||
HTTPFirewallRuleOperatorContainsAllWords HTTPFirewallRuleOperator = "contains all words"
|
||||
HTTPFirewallRuleOperatorNotContainsAnyWord HTTPFirewallRuleOperator = "not contains any word"
|
||||
HTTPFirewallRuleOperatorPrefix HTTPFirewallRuleOperator = "prefix"
|
||||
HTTPFirewallRuleOperatorSuffix HTTPFirewallRuleOperator = "suffix"
|
||||
HTTPFirewallRuleOperatorContainsAny HTTPFirewallRuleOperator = "contains any"
|
||||
HTTPFirewallRuleOperatorContainsAll HTTPFirewallRuleOperator = "contains all"
|
||||
HTTPFirewallRuleOperatorContainsSQLInjection HTTPFirewallRuleOperator = "contains sql injection"
|
||||
HTTPFirewallRuleOperatorContainsSQLInjectionStrictly HTTPFirewallRuleOperator = "contains sql injection strictly"
|
||||
HTTPFirewallRuleOperatorContainsXSS HTTPFirewallRuleOperator = "contains xss"
|
||||
HTTPFirewallRuleOperatorContainsXSSStrictly HTTPFirewallRuleOperator = "contains xss strictly"
|
||||
HTTPFirewallRuleOperatorHasKey HTTPFirewallRuleOperator = "has key" // has key in slice or map
|
||||
HTTPFirewallRuleOperatorVersionGt HTTPFirewallRuleOperator = "version gt"
|
||||
HTTPFirewallRuleOperatorVersionLt HTTPFirewallRuleOperator = "version lt"
|
||||
HTTPFirewallRuleOperatorVersionRange HTTPFirewallRuleOperator = "version range"
|
||||
|
||||
HTTPFirewallRuleOperatorContainsBinary HTTPFirewallRuleOperator = "contains binary" // contains binary
|
||||
HTTPFirewallRuleOperatorNotContainsBinary HTTPFirewallRuleOperator = "not contains binary" // not contains binary
|
||||
|
||||
// ip
|
||||
|
||||
HTTPFirewallRuleOperatorEqIP HTTPFirewallRuleOperator = "eq ip"
|
||||
HTTPFirewallRuleOperatorInIPList HTTPFirewallRuleOperator = "in ip list"
|
||||
HTTPFirewallRuleOperatorGtIP HTTPFirewallRuleOperator = "gt ip"
|
||||
HTTPFirewallRuleOperatorGteIP HTTPFirewallRuleOperator = "gte ip"
|
||||
HTTPFirewallRuleOperatorLtIP HTTPFirewallRuleOperator = "lt ip"
|
||||
HTTPFirewallRuleOperatorLteIP HTTPFirewallRuleOperator = "lte ip"
|
||||
HTTPFirewallRuleOperatorIPRange HTTPFirewallRuleOperator = "ip range"
|
||||
HTTPFirewallRuleOperatorNotIPRange HTTPFirewallRuleOperator = "not ip range"
|
||||
HTTPFirewallRuleOperatorIPMod10 HTTPFirewallRuleOperator = "ip mod 10"
|
||||
HTTPFirewallRuleOperatorIPMod100 HTTPFirewallRuleOperator = "ip mod 100"
|
||||
HTTPFirewallRuleOperatorIPMod HTTPFirewallRuleOperator = "ip mod"
|
||||
|
||||
HTTPFirewallRuleCaseInsensitiveNone = "none"
|
||||
HTTPFirewallRuleCaseInsensitiveYes = "yes"
|
||||
HTTPFirewallRuleCaseInsensitiveNo = "no"
|
||||
)
|
||||
|
||||
type RuleOperatorDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
CaseInsensitive HTTPFirewallRuleCaseInsensitive `json:"caseInsensitive"` // default caseInsensitive setting
|
||||
DataType string `json:"dataType"`
|
||||
}
|
||||
|
||||
var AllRuleOperators = []*RuleOperatorDefinition{
|
||||
{
|
||||
Name: "正则匹配",
|
||||
Code: HTTPFirewallRuleOperatorMatch,
|
||||
Description: "使用正则表达式匹配,在头部使用(?i)表示不区分大小写,<a href=\"https://goedge.cn/docs/Appendix/Regexp/Index.md\" target=\"_blank\">正则表达式语法 »</a>。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
|
||||
DataType: "regexp",
|
||||
},
|
||||
{
|
||||
Name: "正则不匹配",
|
||||
Code: HTTPFirewallRuleOperatorNotMatch,
|
||||
Description: "使用正则表达式不匹配,在头部使用(?i)表示不区分大小写,<a href=\"https://goedge.cn/docs/Appendix/Regexp/Index.md\" target=\"_blank\">正则表达式语法 »</a>。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
|
||||
DataType: "regexp",
|
||||
},
|
||||
{
|
||||
Name: "通配符匹配",
|
||||
Code: HTTPFirewallRuleOperatorWildcardMatch,
|
||||
Description: "判断是否和指定的通配符匹配,可以在对比值中使用星号通配符(*)表示任意字符。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
|
||||
DataType: "wildcard",
|
||||
},
|
||||
{
|
||||
Name: "通配符不匹配",
|
||||
Code: HTTPFirewallRuleOperatorWildcardNotMatch,
|
||||
Description: "判断是否和指定的通配符不匹配,可以在对比值中使用星号通配符(*)表示任意字符。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
|
||||
DataType: "wildcard",
|
||||
},
|
||||
{
|
||||
Name: "字符串等于",
|
||||
Code: HTTPFirewallRuleOperatorEqString,
|
||||
Description: "使用字符串对比等于。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "字符串不等于",
|
||||
Code: HTTPFirewallRuleOperatorNeqString,
|
||||
Description: "使用字符串对比不等于。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "包含字符串",
|
||||
Code: HTTPFirewallRuleOperatorContains,
|
||||
Description: "包含某个字符串,比如Hello World包含了World。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "不包含字符串",
|
||||
Code: HTTPFirewallRuleOperatorNotContains,
|
||||
Description: "不包含某个字符串,比如Hello字符串中不包含Hi。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "包含任一字符串",
|
||||
Code: HTTPFirewallRuleOperatorContainsAny,
|
||||
Description: "包含字符串列表中的任意一个,比如/hello/world包含/hello和/hi中的/hello,对比值中每行一个字符串。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "strings",
|
||||
},
|
||||
{
|
||||
Name: "包含所有字符串",
|
||||
Code: HTTPFirewallRuleOperatorContainsAll,
|
||||
Description: "包含字符串列表中的所有字符串,比如/hello/world必须包含/hello和/world,对比值中每行一个字符串。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "strings",
|
||||
},
|
||||
{
|
||||
Name: "包含前缀",
|
||||
Code: HTTPFirewallRuleOperatorPrefix,
|
||||
Description: "包含字符串前缀部分,比如/hello前缀会匹配/hello, /hello/world等。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "包含后缀",
|
||||
Code: HTTPFirewallRuleOperatorSuffix,
|
||||
Description: "包含字符串后缀部分,比如/hello后缀会匹配/hello, /hi/hello等。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "包含任一单词",
|
||||
Code: HTTPFirewallRuleOperatorContainsAnyWord,
|
||||
Description: "包含某个独立单词,对比值中每行一个单词,比如mozilla firefox里包含了mozilla和firefox两个单词,但是不包含fire和fox这两个单词。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "strings",
|
||||
},
|
||||
{
|
||||
Name: "包含所有单词",
|
||||
Code: HTTPFirewallRuleOperatorContainsAllWords,
|
||||
Description: "包含所有的独立单词,对比值中每行一个单词,比如mozilla firefox里包含了mozilla和firefox两个单词,但是不包含fire和fox这两个单词。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "strings",
|
||||
},
|
||||
{
|
||||
Name: "不包含任一单词",
|
||||
Code: HTTPFirewallRuleOperatorNotContainsAnyWord,
|
||||
Description: "不包含某个独立单词,对比值中每行一个单词,比如mozilla firefox里包含了mozilla和firefox两个单词,但是不包含fire和fox这两个单词。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "strings",
|
||||
},
|
||||
{
|
||||
Name: "包含SQL注入",
|
||||
Code: HTTPFirewallRuleOperatorContainsSQLInjection,
|
||||
Description: "检测字符串内容是否包含SQL注入。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "none",
|
||||
},
|
||||
{
|
||||
Name: "包含SQL注入-严格模式",
|
||||
Code: HTTPFirewallRuleOperatorContainsSQLInjectionStrictly,
|
||||
Description: "更加严格地检测字符串内容是否包含SQL注入,相对于非严格模式,有一定的误报几率。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "none",
|
||||
},
|
||||
{
|
||||
Name: "包含XSS注入",
|
||||
Code: HTTPFirewallRuleOperatorContainsXSS,
|
||||
Description: "检测字符串内容是否包含XSS注入。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "none",
|
||||
},
|
||||
{
|
||||
Name: "包含XSS注入-严格模式",
|
||||
Code: HTTPFirewallRuleOperatorContainsXSSStrictly,
|
||||
Description: "更加严格地检测字符串内容是否包含XSS注入,相对于非严格模式,此时xml、audio、video等标签也会被匹配。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "none",
|
||||
},
|
||||
{
|
||||
Name: "包含二进制数据",
|
||||
Code: HTTPFirewallRuleOperatorContainsBinary,
|
||||
Description: "包含一组二进制数据。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "不包含二进制数据",
|
||||
Code: HTTPFirewallRuleOperatorNotContainsBinary,
|
||||
Description: "不包含一组二进制数据。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string",
|
||||
},
|
||||
{
|
||||
Name: "数值大于",
|
||||
Code: HTTPFirewallRuleOperatorGt,
|
||||
Description: "使用数值对比大于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "数值大于等于",
|
||||
Code: HTTPFirewallRuleOperatorGte,
|
||||
Description: "使用数值对比大于等于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "数值小于",
|
||||
Code: HTTPFirewallRuleOperatorLt,
|
||||
Description: "使用数值对比小于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "数值小于等于",
|
||||
Code: HTTPFirewallRuleOperatorLte,
|
||||
Description: "使用数值对比小于等于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "数值等于",
|
||||
Code: HTTPFirewallRuleOperatorEq,
|
||||
Description: "使用数值对比等于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "数值不等于",
|
||||
Code: HTTPFirewallRuleOperatorNeq,
|
||||
Description: "使用数值对比不等于,对比值需要是一个数字。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "包含索引",
|
||||
Code: HTTPFirewallRuleOperatorHasKey,
|
||||
Description: "对于一组数据拥有某个键值或者索引。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
|
||||
DataType: "string|number",
|
||||
},
|
||||
{
|
||||
Name: "版本号大于",
|
||||
Code: HTTPFirewallRuleOperatorVersionGt,
|
||||
Description: "对比版本号大于。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "version",
|
||||
},
|
||||
{
|
||||
Name: "版本号小于",
|
||||
Code: HTTPFirewallRuleOperatorVersionLt,
|
||||
Description: "对比版本号小于。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "version",
|
||||
},
|
||||
{
|
||||
Name: "版本号范围",
|
||||
Code: HTTPFirewallRuleOperatorVersionRange,
|
||||
Description: "判断版本号在某个范围内,格式为 起始version1,结束version2。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "versionRange",
|
||||
},
|
||||
{
|
||||
Name: "IP等于",
|
||||
Code: HTTPFirewallRuleOperatorEqIP,
|
||||
Description: "将参数转换为IP进行对比,只能对比单个IP。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ip",
|
||||
},
|
||||
{
|
||||
Name: "在一组IP中",
|
||||
Code: HTTPFirewallRuleOperatorInIPList,
|
||||
Description: "判断参数IP在一组IP内,对比值中每行一个IP。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ips",
|
||||
},
|
||||
{
|
||||
Name: "IP大于",
|
||||
Code: HTTPFirewallRuleOperatorGtIP,
|
||||
Description: "将参数转换为IP进行对比。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ip",
|
||||
},
|
||||
{
|
||||
Name: "IP大于等于",
|
||||
Code: HTTPFirewallRuleOperatorGteIP,
|
||||
Description: "将参数转换为IP进行对比。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ip",
|
||||
},
|
||||
{
|
||||
Name: "IP小于",
|
||||
Code: HTTPFirewallRuleOperatorLtIP,
|
||||
Description: "将参数转换为IP进行对比。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ip",
|
||||
},
|
||||
{
|
||||
Name: "IP小于等于",
|
||||
Code: HTTPFirewallRuleOperatorLteIP,
|
||||
Description: "将参数转换为IP进行对比。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ip",
|
||||
},
|
||||
{
|
||||
Name: "IP范围",
|
||||
Code: HTTPFirewallRuleOperatorIPRange,
|
||||
Description: "IP在某个范围之内,范围格式可以是英文逗号分隔的<code-label>开始IP,结束IP</code-label>,比如<code-label>192.168.1.100,192.168.2.200</code-label>;或者CIDR格式的ip/bits,比如<code-label>192.168.2.1/24</code-label>;或者单个IP。可以填写多行,每行一个IP范围。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ips",
|
||||
},
|
||||
{
|
||||
Name: "不在IP范围",
|
||||
Code: HTTPFirewallRuleOperatorNotIPRange,
|
||||
Description: "IP不在某个范围之内,范围格式可以是英文逗号分隔的<code-label>开始IP,结束IP</code-label>,比如<code-label>192.168.1.100,192.168.2.200</code-label>;或者CIDR格式的ip/bits,比如<code-label>192.168.2.1/24</code-label>;或者单个IP。可以填写多行,每行一个IP范围。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "ips",
|
||||
},
|
||||
{
|
||||
Name: "IP取模10",
|
||||
Code: HTTPFirewallRuleOperatorIPMod10,
|
||||
Description: "对IP参数值取模,除数为10,对比值为余数。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "IP取模100",
|
||||
Code: HTTPFirewallRuleOperatorIPMod100,
|
||||
Description: "对IP参数值取模,除数为100,对比值为余数。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
{
|
||||
Name: "IP取模",
|
||||
Code: HTTPFirewallRuleOperatorIPMod,
|
||||
Description: "对IP参数值取模,对比值格式为:除数,余数,比如10,1。",
|
||||
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
|
||||
DataType: "number",
|
||||
},
|
||||
}
|
||||
|
||||
func FindRuleOperatorName(code string) string {
|
||||
for _, operator := range AllRuleOperators {
|
||||
if operator.Code == code {
|
||||
return operator.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package firewallconfigs_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuleOperator_Markdown(t *testing.T) {
|
||||
var result = []string{}
|
||||
for _, def := range firewallconfigs.AllRuleOperators {
|
||||
def.Description = strings.ReplaceAll(def.Description, "<code-label>", "`")
|
||||
def.Description = strings.ReplaceAll(def.Description, "</code-label>", "`")
|
||||
|
||||
var row = "## " + def.Name + "\n"
|
||||
row += "* 名称:" + def.Name + "\n"
|
||||
row += "* 代号:`" + def.Code + "`\n"
|
||||
row += "* 描述:" + def.Description + "\n"
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
fmt.Print(strings.Join(result, "\n") + "\n")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallRuleRef struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
RuleId int64 `yaml:"ruleId" json:"ruleId"`
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package firewallconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// HTTPFirewallActionConfig 单个动作配置
|
||||
type HTTPFirewallActionConfig struct {
|
||||
Code HTTPFirewallActionString `yaml:"code" json:"code"`
|
||||
Options maps.Map `yaml:"options" json:"options"`
|
||||
}
|
||||
|
||||
// HTTPFirewallRuleSet 规则集定义
|
||||
type HTTPFirewallRuleSet struct {
|
||||
Id int64 `yaml:"id" json:"id"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Code string `yaml:"code" json:"code"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
Connector string `yaml:"connector" json:"connector"`
|
||||
RuleRefs []*HTTPFirewallRuleRef `yaml:"ruleRefs" json:"ruleRefs"`
|
||||
Rules []*HTTPFirewallRule `yaml:"rules" json:"rules"`
|
||||
IgnoreLocal bool `yaml:"ignoreLocal" json:"ignoreLocal"`
|
||||
IgnoreSearchEngine bool `yaml:"ignoreSearchEngine" json:"ignoreSearchEngine"`
|
||||
Level string `yaml:"level" json:"level"` // 威胁等级
|
||||
CVE string `yaml:"cve" json:"cve"` // cve 编号
|
||||
|
||||
Actions []*HTTPFirewallActionConfig `yaml:"actions" json:"actions"`
|
||||
|
||||
//Action string `yaml:"action" json:"action"` // deprecated, v0.2.5
|
||||
//ActionOptions maps.Map `yaml:"actionOptions" json:"actionOptions"` // deprecated, v0.2.5
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *HTTPFirewallRuleSet) Init() error {
|
||||
for _, rule := range this.Rules {
|
||||
err := rule.Init()
|
||||
if err != nil {
|
||||
logs.Println("ERROR", "validate rule '"+rule.Summary()+"' failed: "+err.Error())
|
||||
|
||||
// 这里不阻断执行,因为先前有些用户填写了错误的规则
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRule 添加规则
|
||||
func (this *HTTPFirewallRuleSet) AddRule(rule *HTTPFirewallRule) {
|
||||
this.Rules = append(this.Rules, rule)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package firewallconfigs
|
||||
|
||||
type HTTPFirewallRuleSetRef struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
SetId int64 `yaml:"setId" json:"setId"`
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
type HTTPFirewallRuleConnector = string
|
||||
|
||||
const (
|
||||
HTTPFirewallRuleConnectorAnd = "and"
|
||||
HTTPFirewallRuleConnectorOr = "or"
|
||||
)
|
||||
|
||||
func HTTPFirewallTemplate() *HTTPFirewallPolicy {
|
||||
policy := &HTTPFirewallPolicy{}
|
||||
policy.IsOn = true
|
||||
policy.Inbound = &HTTPFirewallInboundConfig{}
|
||||
policy.Outbound = &HTTPFirewallOutboundConfig{}
|
||||
|
||||
// xss
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "XSS"
|
||||
group.Code = "xss"
|
||||
group.Description = "防跨站脚本攻击(Cross Site Scripting)"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "XSS攻击检测"
|
||||
set.Code = "1010"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestAll}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsXSS,
|
||||
Value: "",
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// upload
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = false
|
||||
group.Name = "文件上传"
|
||||
group.Code = "upload"
|
||||
group.Description = "防止上传可执行脚本文件到服务器"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "上传文件扩展名"
|
||||
set.Code = "2001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestUpload.ext}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `\.(php|jsp|aspx|asp|exe|asa|rb|py)\b`, // TODO more keywords here
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// web shell
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = false
|
||||
group.Name = "Web Shell"
|
||||
group.Code = "webShell"
|
||||
group.Description = "防止远程执行服务器命令"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "Web Shell"
|
||||
set.Code = "3001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestAll}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `\b(eval|system|exec|execute|passthru|shell_exec|phpinfo)\s*\(`, // TODO more keywords here
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// command injection
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = false
|
||||
group.Name = "命令注入"
|
||||
group.Code = "commandInjection"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "命令注入"
|
||||
set.Code = "4001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestURI}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `\b(pwd|ls|ll|whoami|id|net\s+user)\s*$`, // TODO more keywords here
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestBody}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `\b(pwd|ls|ll|whoami|id|net\s+user)\s*$`, // TODO more keywords here
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// path traversal
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "路径穿越"
|
||||
group.Code = "pathTraversal"
|
||||
group.Description = "防止读取网站目录之外的其他系统文件"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "路径穿越"
|
||||
set.Code = "5001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestURI}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `((\.+)(/+)){2,}`,
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// special dirs
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "特殊目录"
|
||||
group.Code = "denyDirs"
|
||||
group.Description = "防止通过Web访问到一些特殊目录"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "特殊目录"
|
||||
set.Code = "6001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestPath}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsAnyWord,
|
||||
Value: "/.git\n/.svn\n/.htaccess\n/.idea\n/.env\n/.vscode",
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// sql injection
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = false
|
||||
group.Name = "SQL注入"
|
||||
group.Code = "sqlInjection"
|
||||
group.Description = "防止SQL注入漏洞"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "SQL注入检测"
|
||||
set.Code = "7010"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${requestAll}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsSQLInjection,
|
||||
Value: "",
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// bot
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "网络爬虫"
|
||||
group.Code = "bot"
|
||||
group.Description = "禁止一些网络爬虫"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = false
|
||||
set.Name = "搜索引擎"
|
||||
set.Code = "20001"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${userAgent}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsAnyWord,
|
||||
Value: "360spider\nadldxbot\nadsbot-google\napplebot\nadmantx\nalexa\nbaidu\nbingbot\nbingpreview\nfacebookexternalhit\ngooglebot\nproximic\nslurp\nsogou\ntwitterbot\nyandex\nspider",
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "爬虫工具"
|
||||
set.Code = "20003"
|
||||
set.Connector = HTTPFirewallRuleConnectorAnd
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${userAgent}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsAnyWord,
|
||||
Value: "python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust",
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${userAgent}",
|
||||
Operator: HTTPFirewallRuleOperatorNotContainsAnyWord,
|
||||
Value: "goedge",
|
||||
IsCaseInsensitive: true,
|
||||
Description: "User-Agent白名单",
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "下载工具"
|
||||
set.Code = "20004"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionTag,
|
||||
Options: maps.Map{
|
||||
"tags": []string{"download"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${userAgent}",
|
||||
Operator: HTTPFirewallRuleOperatorContainsAnyWord,
|
||||
Value: "wget\ncurl",
|
||||
IsCaseInsensitive: true,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "空Agent"
|
||||
set.Code = "20002"
|
||||
set.Connector = HTTPFirewallRuleConnectorOr
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
// 空Agent
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${userAgent}",
|
||||
Operator: HTTPFirewallRuleOperatorEqString,
|
||||
Value: "",
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// cc2
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "CC攻击"
|
||||
group.Description = "Challenge Collapsar,防止短时间大量请求涌入,请谨慎开启和设置"
|
||||
group.Code = "cc2"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "CC单URL请求数"
|
||||
set.Description = "限制单IP在一定时间内对单URL的请求数"
|
||||
set.Code = "8001"
|
||||
set.Connector = HTTPFirewallRuleConnectorAnd
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionBlock,
|
||||
Options: maps.Map{
|
||||
"timeout": 1800,
|
||||
},
|
||||
},
|
||||
}
|
||||
set.IgnoreLocal = true
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${cc2}",
|
||||
Operator: HTTPFirewallRuleOperatorGt,
|
||||
Value: "120",
|
||||
CheckpointOptions: map[string]interface{}{
|
||||
"keys": []string{"${remoteAddr}", "${requestPath}"},
|
||||
"period": "60",
|
||||
"threshold": 120,
|
||||
"enableFingerprint": true,
|
||||
},
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "CC请求数"
|
||||
set.Description = "限制单IP在一定时间内的总体请求数"
|
||||
set.Code = "8002"
|
||||
set.Connector = HTTPFirewallRuleConnectorAnd
|
||||
set.IgnoreLocal = true
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionBlock,
|
||||
Options: maps.Map{
|
||||
"timeout": 1800,
|
||||
},
|
||||
},
|
||||
}
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${cc2}",
|
||||
Operator: HTTPFirewallRuleOperatorGt,
|
||||
Value: "1200",
|
||||
CheckpointOptions: map[string]interface{}{
|
||||
"keys": []string{"${remoteAddr}"},
|
||||
"period": "60",
|
||||
"threshold": 1200,
|
||||
"enableFingerprint": true,
|
||||
},
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "随机URL攻击"
|
||||
set.Description = "限制用户使用随机URL访问网站"
|
||||
set.Code = "8003"
|
||||
set.Connector = HTTPFirewallRuleConnectorAnd
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${args}",
|
||||
Operator: HTTPFirewallRuleOperatorMatch,
|
||||
Value: `^[0-9a-zA-Z_\-.]{12,}$`,
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// custom
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "防盗链"
|
||||
group.Description = "防止第三方网站引用本站资源。"
|
||||
group.Code = "referer"
|
||||
group.IsTemplate = true
|
||||
|
||||
{
|
||||
var set = &HTTPFirewallRuleSet{}
|
||||
set.IsOn = true
|
||||
set.Name = "防盗链"
|
||||
set.Description = "防止第三方网站引用本站资源"
|
||||
set.Code = "9001"
|
||||
set.Connector = HTTPFirewallRuleConnectorAnd
|
||||
set.Actions = []*HTTPFirewallActionConfig{
|
||||
{
|
||||
Code: HTTPFirewallActionPage,
|
||||
Options: maps.Map{"status": 403, "body": ""},
|
||||
},
|
||||
}
|
||||
|
||||
set.AddRule(&HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Param: "${refererBlock}",
|
||||
Operator: HTTPFirewallRuleOperatorEq,
|
||||
Value: "0",
|
||||
CheckpointOptions: map[string]interface{}{
|
||||
"allowEmpty": true,
|
||||
"allowSameDomain": true,
|
||||
"allowDomains": []string{"*"},
|
||||
},
|
||||
IsCaseInsensitive: false,
|
||||
})
|
||||
|
||||
group.AddRuleSet(set)
|
||||
}
|
||||
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
// custom
|
||||
{
|
||||
var group = &HTTPFirewallRuleGroup{}
|
||||
group.IsOn = true
|
||||
group.Name = "自定义规则分组"
|
||||
group.Description = "我的自定义规则分组,可以将自定义的规则放在这个分组下"
|
||||
group.Code = "custom"
|
||||
group.IsTemplate = true
|
||||
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
|
||||
}
|
||||
|
||||
return policy
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package firewallconfigs_test
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserAgentMatch_PHP(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
var expr = `python|pycurl|http-client|httpclient|apachebench|nethttp|http_request|java|perl|ruby|scrapy|php\b|rust`
|
||||
var reg = regexp.MustCompile(expr)
|
||||
a.IsTrue(reg.MatchString("php"))
|
||||
a.IsTrue(reg.MatchString("php/5.0"))
|
||||
a.IsFalse(reg.MatchString("php110"))
|
||||
}
|
||||
4
EdgeCommon/pkg/serverconfigs/firewallconfigs/option.go
Normal file
4
EdgeCommon/pkg/serverconfigs/firewallconfigs/option.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package firewallconfigs
|
||||
|
||||
type OptionInterface interface {
|
||||
}
|
||||
24
EdgeCommon/pkg/serverconfigs/firewallconfigs/option_field.go
Normal file
24
EdgeCommon/pkg/serverconfigs/firewallconfigs/option_field.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package firewallconfigs
|
||||
|
||||
// attach option
|
||||
type FieldOption struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Value string `json:"value"` // default value
|
||||
IsRequired bool `json:"isRequired"`
|
||||
Size int `json:"size"`
|
||||
Comment string `json:"comment"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
RightLabel string `json:"rightLabel"`
|
||||
MaxLength int `json:"maxLength"`
|
||||
Validate func(value string) (ok bool, message string) `json:"-"`
|
||||
}
|
||||
|
||||
func NewFieldOption(name string, code string) *FieldOption {
|
||||
return &FieldOption{
|
||||
Type: "field",
|
||||
Name: name,
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
type OptionsOption struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Value string `json:"value"` // default value
|
||||
IsRequired bool `json:"isRequired"`
|
||||
Size int `json:"size"`
|
||||
Comment string `json:"comment"`
|
||||
RightLabel string `json:"rightLabel"`
|
||||
Validate func(value string) (ok bool, message string) `json:"-"`
|
||||
Options []maps.Map `json:"options"`
|
||||
}
|
||||
|
||||
func NewOptionsOption(name string, code string) *OptionsOption {
|
||||
return &OptionsOption{
|
||||
Type: "options",
|
||||
Name: name,
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *OptionsOption) SetOptions(options []maps.Map) {
|
||||
this.Options = options
|
||||
}
|
||||
10
EdgeCommon/pkg/serverconfigs/firewallconfigs/param_filter.go
Normal file
10
EdgeCommon/pkg/serverconfigs/firewallconfigs/param_filter.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package firewallconfigs
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
// 对参数的过滤器
|
||||
type ParamFilter struct {
|
||||
Code string `yaml:"code" json:"code"` // 过滤器编号
|
||||
Name string `yaml:"name" json:"name"` // 名称
|
||||
Options maps.Map `yaml:"options" json:"options"` // 过滤器选项
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package firewallconfigs
|
||||
|
||||
// SYNFloodConfig Syn flood防护设置
|
||||
type SYNFloodConfig struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
MinAttempts int32 `yaml:"minAttempts" json:"minAttempts"` // 最小尝试次数/分钟
|
||||
TimeoutSeconds int32 `yaml:"timeoutSeconds" json:"timeoutSeconds"` // 拦截超时时间
|
||||
IgnoreLocal bool `yaml:"ignoreLocal" json:"ignoreLocal"` // 忽略本地IP
|
||||
}
|
||||
|
||||
func NewSYNFloodConfig() *SYNFloodConfig {
|
||||
return &SYNFloodConfig{
|
||||
IsOn: false,
|
||||
MinAttempts: 10,
|
||||
TimeoutSeconds: 1800,
|
||||
IgnoreLocal: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SYNFloodConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
28
EdgeCommon/pkg/serverconfigs/follow_protocol_config.go
Normal file
28
EdgeCommon/pkg/serverconfigs/follow_protocol_config.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package serverconfigs
|
||||
|
||||
// FollowProtocolConfig 协议跟随配置
|
||||
type FollowProtocolConfig struct {
|
||||
IsPrior bool `yaml:"isPrior" json:"isPrior"` // 是否覆盖父级配置
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||
HTTP struct {
|
||||
Port int `yaml:"port" json:"port"` // 端口
|
||||
FollowPort bool `yaml:"followPort" json:"followPort"` // 跟随端口
|
||||
} `yaml:"http" json:"http"` // HTTP配置
|
||||
HTTPS struct {
|
||||
Port int `yaml:"port" json:"port"` // 端口
|
||||
FollowPort bool `yaml:"followPort" json:"followPort"` // 跟随端口
|
||||
} `yaml:"https" json:"https"` // HTTPS配置
|
||||
}
|
||||
|
||||
func NewFollowProtocolConfig() *FollowProtocolConfig {
|
||||
var p = &FollowProtocolConfig{}
|
||||
p.HTTP.FollowPort = true
|
||||
p.HTTPS.FollowPort = true
|
||||
return p
|
||||
}
|
||||
|
||||
func (this *FollowProtocolConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
8
EdgeCommon/pkg/serverconfigs/ftp_server_config.go
Normal file
8
EdgeCommon/pkg/serverconfigs/ftp_server_config.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package serverconfigs
|
||||
|
||||
// FTP源站配置
|
||||
type FTPServerConfig struct {
|
||||
Username string `yaml:"username" json:"username"` // 用户名
|
||||
Password string `yaml:"password" json:"password"` // 密码
|
||||
Dir string `yaml:"dir" json:"dir"` // 目录
|
||||
}
|
||||
6
EdgeCommon/pkg/serverconfigs/ftp_server_ref.go
Normal file
6
EdgeCommon/pkg/serverconfigs/ftp_server_ref.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package serverconfigs
|
||||
|
||||
type FTPServerRef struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
FTPServerId int64 `yaml:"ftpServerId" json:"ftpServerId"`
|
||||
}
|
||||
112
EdgeCommon/pkg/serverconfigs/global_server_config.go
Normal file
112
EdgeCommon/pkg/serverconfigs/global_server_config.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package serverconfigs
|
||||
|
||||
type LnRequestSchedulingMethod = string
|
||||
|
||||
const (
|
||||
LnRequestSchedulingMethodRandom LnRequestSchedulingMethod = "random"
|
||||
LnRequestSchedulingMethodURLMapping LnRequestSchedulingMethod = "urlMapping"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTCPPortRangeMin = 10000
|
||||
DefaultTCPPortRangeMax = 40000
|
||||
)
|
||||
|
||||
func NewGlobalServerConfig() *GlobalServerConfig {
|
||||
var config = &GlobalServerConfig{}
|
||||
|
||||
config.HTTPAll.SupportsLowVersionHTTP = true
|
||||
config.HTTPAll.EnableServerAddrVariable = false
|
||||
config.HTTPAll.LnRequestSchedulingMethod = LnRequestSchedulingMethodURLMapping
|
||||
|
||||
config.HTTPAccessLog.IsOn = true
|
||||
config.HTTPAccessLog.EnableRequestHeaders = true
|
||||
config.HTTPAccessLog.EnableResponseHeaders = true
|
||||
config.HTTPAccessLog.EnableCookies = true
|
||||
config.HTTPAccessLog.EnableServerNotFound = true
|
||||
|
||||
config.Log.RecordServerError = false
|
||||
|
||||
config.Performance.AutoWriteTimeout = false
|
||||
config.Performance.AutoReadTimeout = false
|
||||
config.Stat.Upload.MaxCities = 32
|
||||
config.Stat.Upload.MaxProviders = 32
|
||||
config.Stat.Upload.MaxSystems = 64
|
||||
config.Stat.Upload.MaxBrowsers = 64
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// GlobalServerConfig 全局的服务配置
|
||||
type GlobalServerConfig struct {
|
||||
HTTPAll struct {
|
||||
MatchDomainStrictly bool `yaml:"matchDomainStrictly" json:"matchDomainStrictly"` // 是否严格匹配域名
|
||||
AllowMismatchDomains []string `yaml:"allowMismatchDomains" json:"allowMismatchDomains"` // 允许的不匹配的域名
|
||||
AllowNodeIP bool `yaml:"allowNodeIP" json:"allowNodeIP"` // 允许IP直接访问
|
||||
NodeIPShowPage bool `yaml:"nodeIPShowPage" json:"nodeIPShowPage"` // 访问IP地址是否显示页面
|
||||
NodeIPPageHTML string `yaml:"nodeIPPageHTML" json:"nodeIPPageHTML"` // 访问IP地址页面内容
|
||||
DefaultDomain string `yaml:"defaultDomain" json:"defaultDomain"` // 默认的域名
|
||||
DomainMismatchAction *DomainMismatchAction `yaml:"domainMismatchAction" json:"domainMismatchAction"` // 不匹配时采取的动作
|
||||
|
||||
SupportsLowVersionHTTP bool `yaml:"supportsLowVersionHTTP" json:"supportsLowVersionHTTP"` // 是否启用低版本HTTP
|
||||
MatchCertFromAllServers bool `yaml:"matchCertFromAllServers" json:"matchCertFromAllServers"` // 从所有服务中匹配证书(不要轻易开启!)
|
||||
ForceLnRequest bool `yaml:"forceLnRequest" json:"forceLnRequest"` // 强制从Ln请求内容
|
||||
LnRequestSchedulingMethod LnRequestSchedulingMethod `yaml:"lnRequestSchedulingMethod" json:"lnRequestSchedulingMethod"` // Ln请求调度方法
|
||||
ServerName string `yaml:"serverName" json:"serverName"` // Server名称
|
||||
EnableServerAddrVariable bool `yaml:"enableServerAddrVariable" json:"enableServerAddrVariable"` // 是否支持${serverAddr}变量
|
||||
XFFMaxAddresses int `yaml:"xffMaxAddresses" json:"xffMaxAddresses"` // XFF中最多的地址数
|
||||
|
||||
DomainAuditingIsOn bool `yaml:"domainAuditingIsOn" json:"domainAuditingIsOn"` // 域名是否需要审核
|
||||
DomainAuditingPrompt string `yaml:"domainAuditingPrompt" json:"domainAuditingPrompt"` // 域名审核的提示
|
||||
|
||||
RequestOriginsWithEncodings bool `yaml:"requestOriginsWithEncodings" json:"requestOriginsWithEncodings"` // 使用使用压缩编码回源
|
||||
} `yaml:"httpAll" json:"httpAll"` // HTTP统一配置
|
||||
|
||||
TCPAll struct {
|
||||
PortRangeMin int `yaml:"portRangeMin" json:"portRangeMin"` // 最小端口
|
||||
PortRangeMax int `yaml:"portRangeMax" json:"portRangeMax"` // 最大端口
|
||||
DenyPorts []int `yaml:"denyPorts" json:"denyPorts"` // 禁止使用的端口
|
||||
} `yaml:"tcpAll" json:"tcpAll"`
|
||||
|
||||
HTTPAccessLog struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用此功能
|
||||
EnableRequestHeaders bool `yaml:"enableRequestHeaders" json:"enableRequestHeaders"` // 记录请求Header
|
||||
CommonRequestHeadersOnly bool `yaml:"commonRequestHeadersOnly" json:"commonRequestHeadersOnly"` // 只保留通用Header
|
||||
EnableResponseHeaders bool `yaml:"enableResponseHeaders" json:"enableResponseHeaders"` // 记录响应Header
|
||||
EnableCookies bool `yaml:"enableCookies" json:"enableCookies"` // 记录Cookie
|
||||
EnableServerNotFound bool `yaml:"enableServerNotFound" json:"enableServerNotFound"` // 记录服务找不到的日志
|
||||
} `yaml:"httpAccessLog" json:"httpAccessLog"` // 访问日志配置
|
||||
|
||||
Stat struct {
|
||||
Upload struct {
|
||||
MaxCities int16 `yaml:"maxCities" json:"maxCities"` // 最大区域数量
|
||||
MaxProviders int16 `yaml:"maxProviders" json:"maxProviders"` // 最大运营商数量
|
||||
MaxSystems int16 `yaml:"maxSystems" json:"maxSystems"` // 最大操作系统数量
|
||||
MaxBrowsers int16 `yaml:"maxBrowsers" json:"maxBrowsers"` // 最大浏览器数量
|
||||
} `yaml:"upload" json:"upload"` // 上传相关设置
|
||||
} `yaml:"stat" json:"stat"` // 统计相关配置
|
||||
|
||||
Performance struct {
|
||||
Debug bool `yaml:"debug" json:"debug"` // Debug模式
|
||||
AutoWriteTimeout bool `yaml:"autoWriteTimeout" json:"autoWriteTimeout"` // 是否自动写超时
|
||||
AutoReadTimeout bool `yaml:"autoReadTimeout" json:"autoReadTimeout"` // 是否自动读超时
|
||||
} `yaml:"performance" json:"performance"` // 性能
|
||||
|
||||
Log struct {
|
||||
RecordServerError bool `yaml:"recordServerError" json:"recordServerError"` // 记录服务错误到运行日志
|
||||
} `yaml:"log" json:"log"` // 运行日志配置
|
||||
}
|
||||
|
||||
func (this *GlobalServerConfig) Init() error {
|
||||
// 未找到域名时的动作
|
||||
if this.HTTPAll.DomainMismatchAction != nil {
|
||||
err := this.HTTPAll.DomainMismatchAction.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
32
EdgeCommon/pkg/serverconfigs/health_check_config.go
Normal file
32
EdgeCommon/pkg/serverconfigs/health_check_config.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
const HealthCheckHeaderName = "X-Edge-Health-Check-Key"
|
||||
|
||||
// HealthCheckConfig 健康检查设置
|
||||
type HealthCheckConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||
URL string `yaml:"url" json:"url"` // 读取的URL
|
||||
Interval *shared.TimeDuration `yaml:"interval" json:"interval"` // 检测周期
|
||||
StatusCodes []int `yaml:"statusCodes" json:"statusCodes"` // 返回的状态码要求
|
||||
Timeout *shared.TimeDuration `yaml:"timeout" json:"timeout"` // 超时时间
|
||||
CountTries int64 `yaml:"countTries" json:"countTries"` // 尝试次数
|
||||
TryDelay *shared.TimeDuration `yaml:"tryDelay" json:"tryDelay"` // 尝试间隔
|
||||
FailActions []maps.Map `yaml:"failActions" json:"failActions"` // 失败采取的动作 TODO
|
||||
RecoverActions []maps.Map `yaml:"recoverActions" json:"recoverActions"` // 恢复采取的动作 TODO
|
||||
AutoDown bool `yaml:"autoDown" json:"autoDown"` // 是否自动下线
|
||||
CountUp int `yaml:"countUp" json:"countUp"` // 连续在线认定次数
|
||||
CountDown int `yaml:"countDown" json:"countDown"` // 连续离线认定次数
|
||||
UserAgent string `yaml:"userAgent" json:"userAgent"` // 发起请求用的UserAgent
|
||||
OnlyBasicRequest bool `yaml:"onlyBasicRequest" json:"onlyBasicRequest"` // 只做基础的请求,不处理WAF、反向代理等
|
||||
AccessLogIsOn bool `yaml:"accessLogIsOn" json:"accessLogIsOn"` // 是否关闭访问日志
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *HealthCheckConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user