This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View 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"` // 单表最大行数
}

View 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"`
}

View 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模式
}

View 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"` // 是否自动创建目录
}

View 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 ""
}

View 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"`
}

View 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 ""
}

View 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"
)

View 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"`
}

View 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"`
}

View File

@@ -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
}
}

View 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
}

View 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
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View 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
}

View File

@@ -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))
}

View 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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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("&lt;script&gt;", nil))
t.Log(filter.Do("<script>", nil))
}

View File

@@ -0,0 +1,10 @@
package filterconfigs
// 过滤接口
type FilterInterface interface {
// 初始化
Init() error
// 执行过滤
Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error)
}

View 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
}

View File

@@ -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))
}

View 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
}

View File

@@ -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))
}

View 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
}

View File

@@ -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))
}

View 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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View 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]
}

View 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)
}

View 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}))
}

View File

@@ -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()
}

View 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)}
}

View File

@@ -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
}

View 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内容
}

View File

@@ -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 ""
}

View File

@@ -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
}

View File

@@ -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
)

View File

@@ -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"`
}

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -0,0 +1,5 @@
package firewallconfigs
type HTTPFirewallGoGroupAction struct {
GroupId string `yaml:"groupId" json:"groupId"`
}

View File

@@ -0,0 +1,6 @@
package firewallconfigs
type HTTPFirewallGoSetAction struct {
GroupId string `yaml:"groupId" json:"groupId"`
SetId string `yaml:"setId" json:"setId"`
}

View File

@@ -0,0 +1,4 @@
package firewallconfigs
type Action struct {
}

View File

@@ -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,
}
}

View File

@@ -0,0 +1,4 @@
package firewallconfigs
type HTTPFirewallLogAction struct {
}

View File

@@ -0,0 +1,6 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package firewallconfigs
type HTTPFirewallNotifyAction struct {
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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
)

View File

@@ -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
}

View File

@@ -0,0 +1 @@
package firewallconfigs

View File

@@ -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"` // 被加入的版本号
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) -&gt; ${serverAddr} (Server)</address>
<address>Request ID: ${requestId}</address>
</body>
</html>`,
}
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,6 @@
package firewallconfigs
type HTTPFirewallRuleGroupRef struct {
IsOn bool `yaml:"isOn" json:"isOn"`
GroupId int64 `yaml:"groupId" json:"groupId"`
}

View File

@@ -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\">正则表达式语法 &raquo;</a>。",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
DataType: "regexp",
},
{
Name: "正则不匹配",
Code: HTTPFirewallRuleOperatorNotMatch,
Description: "使用正则表达式不匹配,在头部使用(?i)表示不区分大小写,<a href=\"https://goedge.cn/docs/Appendix/Regexp/Index.md\" target=\"_blank\">正则表达式语法 &raquo;</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 ""
}

View File

@@ -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")
}

View File

@@ -0,0 +1,6 @@
package firewallconfigs
type HTTPFirewallRuleRef struct {
IsOn bool `yaml:"isOn" json:"isOn"`
RuleId int64 `yaml:"ruleId" json:"ruleId"`
}

View File

@@ -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)
}

View File

@@ -0,0 +1,6 @@
package firewallconfigs
type HTTPFirewallRuleSetRef struct {
IsOn bool `yaml:"isOn" json:"isOn"`
SetId int64 `yaml:"setId" json:"setId"`
}

View File

@@ -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
}

View File

@@ -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"))
}

View File

@@ -0,0 +1,4 @@
package firewallconfigs
type OptionInterface interface {
}

View 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,
}
}

View File

@@ -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
}

View 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"` // 过滤器选项
}

View File

@@ -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
}

View 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
}

View 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"` // 目录
}

View File

@@ -0,0 +1,6 @@
package serverconfigs
type FTPServerRef struct {
IsOn bool `yaml:"isOn" json:"isOn"`
FTPServerId int64 `yaml:"ftpServerId" json:"ftpServerId"`
}

View 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
}

View 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