实现WAF策略部分功能

This commit is contained in:
刘祥超
2020-10-06 21:02:21 +08:00
parent 0a3c46ac0f
commit 88d4aa183a
33 changed files with 3086 additions and 252 deletions

View File

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

View File

@@ -0,0 +1,8 @@
package firewallconfigs
// url client configure
type HTTPFirewallBlockAction struct {
StatusCode int `yaml:"statusCode" json:"statusCode"`
Body string `yaml:"body" json:"body"` // supports HTML
URL string `yaml:"url" json:"url"`
}

View File

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

View File

@@ -0,0 +1,11 @@
package firewallconfigs
import "reflect"
// action definition
type HTTPFirewallActionDefinition struct {
Name string
Code HTTPFirewallActionString
Description string
Type reflect.Type
}

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,5 @@
package firewallconfigs
type Action struct {
}

View File

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

View File

@@ -0,0 +1,12 @@
package firewallconfigs
type HTTPFirewallActionString = string
const (
HTTPFirewallActionLog = "log" // allow and log
HTTPFirewallActionBlock = "block" // block
HTTPFirewallActionCaptcha = "captcha" // block and show captcha
HTTPFirewallActionAllow = "allow" // allow
HTTPFirewallActionGoGroup = "go_group" // go to next rule group
HTTPFirewallActionGoSet = "go_set" // go to next rule set
)

View File

@@ -0,0 +1,43 @@
package firewallconfigs
import (
"reflect"
)
var AllActions = []*HTTPFirewallActionDefinition{
{
Name: "阻止",
Code: HTTPFirewallActionBlock,
},
{
Name: "允许通过",
Code: HTTPFirewallActionAllow,
},
{
Name: "允许并记录日志",
Code: HTTPFirewallActionLog,
},
{
Name: "Captcha验证码",
Code: HTTPFirewallActionCaptcha,
},
{
Name: "跳到下一个规则分组",
Code: HTTPFirewallActionGoGroup,
Type: reflect.TypeOf(new(HTTPFirewallGoGroupAction)).Elem(),
},
{
Name: "跳到下一个规则集",
Code: HTTPFirewallActionGoSet,
Type: reflect.TypeOf(new(HTTPFirewallGoSetAction)).Elem(),
},
}
func FindActionName(action HTTPFirewallActionString) string {
for _, def := range AllActions {
if def.Code == action {
return def.Name
}
}
return ""
}

View File

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

View File

@@ -0,0 +1,28 @@
package firewallconfigs
type HTTPFirewallInboundConfig struct {
IsOn bool `yaml:"isOn" json:"isOn"`
GroupRefs []*HTTPFirewallRuleGroupRef `yaml:"groupRefs" json:"groupRefs"`
Groups []*HTTPFirewallRuleGroup `yaml:"groups" json:"groups"`
}
// 初始化
func (this *HTTPFirewallInboundConfig) Init() error {
for _, group := range this.Groups {
err := group.Init()
if err != nil {
return err
}
}
return nil
}
// 根据Code查找Group
func (this *HTTPFirewallInboundConfig) FindGroupWithCode(code string) *HTTPFirewallRuleGroup {
for _, group := range this.Groups {
if group.Code == code {
return group
}
}
return nil
}

View File

@@ -0,0 +1,28 @@
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
}

View File

@@ -0,0 +1,51 @@
package firewallconfigs
// 防火墙策略
type HTTPFirewallPolicy 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"`
Inbound *HTTPFirewallInboundConfig `yaml:"inbound" json:"inbound"`
Outbound *HTTPFirewallOutboundConfig `yaml:"outbound" json:"outbound"`
}
// 初始化
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
}
}
return nil
}
// 获取所有分组
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
}
// 根据代号获取分组
func (this *HTTPFirewallPolicy) FindRuleGroupWithCode(code string) *HTTPFirewallRuleGroup {
for _, g := range this.AllRuleGroups() {
if g.Code == code {
return g
}
}
return nil
}

View File

@@ -1,4 +1,4 @@
package serverconfigs
package firewallconfigs
type HTTPFirewallRef struct {
IsPrior bool `yaml:"isPrior" json:"isPrior"`

View File

@@ -0,0 +1,16 @@
package firewallconfigs
type HTTPFirewallRule struct {
Id int64 `yaml:"id" json:"id"`
IsOn bool `yaml:"isOn" json:"isOn"`
Param string `yaml:"param" json:"param"`
Operator string `yaml:"operator" json:"operator"`
Value string `yaml:"value" json:"value"`
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
CheckpointOptions map[string]interface{} `yaml:"checkpointOptions" json:"checkpointOptions"`
Description string `yaml:"description" json:"description"`
}
func (this *HTTPFirewallRule) Init() error {
return nil
}

View File

@@ -0,0 +1,28 @@
package firewallconfigs
// 规则组
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"`
}
// 初始化
func (this *HTTPFirewallRuleGroup) Init() error {
for _, set := range this.Sets {
err := set.Init()
if err != nil {
return err
}
}
return nil
}
// 添加规则集
func (this *HTTPFirewallRuleGroup) AddRuleSet(ruleSet *HTTPFirewallRuleSet) {
this.Sets = append(this.Sets, ruleSet)
}

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,219 @@
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"
HTTPFirewallRuleOperatorContains HTTPFirewallRuleOperator = "contains"
HTTPFirewallRuleOperatorNotContains HTTPFirewallRuleOperator = "not contains"
HTTPFirewallRuleOperatorPrefix HTTPFirewallRuleOperator = "prefix"
HTTPFirewallRuleOperatorSuffix HTTPFirewallRuleOperator = "suffix"
HTTPFirewallRuleOperatorHasKey HTTPFirewallRuleOperator = "has key" // has key in slice or map
HTTPFirewallRuleOperatorVersionGt HTTPFirewallRuleOperator = "version gt"
HTTPFirewallRuleOperatorVersionLt HTTPFirewallRuleOperator = "version lt"
HTTPFirewallRuleOperatorVersionRange HTTPFirewallRuleOperator = "version range"
// ip
HTTPFirewallRuleOperatorEqIP HTTPFirewallRuleOperator = "eq ip"
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
Code string
Description string
CaseInsensitive HTTPFirewallRuleCaseInsensitive // default caseInsensitive setting
}
var AllRuleOperators = []*RuleOperatorDefinition{
{
Name: "数值大于",
Code: HTTPFirewallRuleOperatorGt,
Description: "使用数值对比大于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "数值大于等于",
Code: HTTPFirewallRuleOperatorGte,
Description: "使用数值对比大于等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "数值小于",
Code: HTTPFirewallRuleOperatorLt,
Description: "使用数值对比小于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "数值小于等于",
Code: HTTPFirewallRuleOperatorLte,
Description: "使用数值对比小于等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "数值等于",
Code: HTTPFirewallRuleOperatorEq,
Description: "使用数值对比等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "数值不等于",
Code: HTTPFirewallRuleOperatorNeq,
Description: "使用数值对比不等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNone,
},
{
Name: "字符串等于",
Code: HTTPFirewallRuleOperatorEqString,
Description: "使用字符串对比等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "字符串不等于",
Code: HTTPFirewallRuleOperatorNeqString,
Description: "使用字符串对比不等于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "正则匹配",
Code: HTTPFirewallRuleOperatorMatch,
Description: "使用正则表达式匹配,在头部使用(?i)表示不区分大小写,<a href=\"http://teaos.cn/doc/regexp/Regexp.md\" target=\"_blank\">正则表达式语法 &raquo;</a>",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
},
{
Name: "正则不匹配",
Code: HTTPFirewallRuleOperatorNotMatch,
Description: "使用正则表达式不匹配,在头部使用(?i)表示不区分大小写,<a href=\"http://teaos.cn/doc/regexp/Regexp.md\" target=\"_blank\">正则表达式语法 &raquo;</a>",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveYes,
},
{
Name: "包含字符串",
Code: HTTPFirewallRuleOperatorContains,
Description: "包含某个字符串",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "不包含字符串",
Code: HTTPFirewallRuleOperatorNotContains,
Description: "不包含某个字符串",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "包含前缀",
Code: HTTPFirewallRuleOperatorPrefix,
Description: "包含某个前缀",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "包含后缀",
Code: HTTPFirewallRuleOperatorSuffix,
Description: "包含某个后缀",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "包含索引",
Code: HTTPFirewallRuleOperatorHasKey,
Description: "对于一组数据拥有某个键值或者索引",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "版本号大于",
Code: HTTPFirewallRuleOperatorVersionGt,
Description: "对比版本号大于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "版本号小于",
Code: HTTPFirewallRuleOperatorVersionLt,
Description: "对比版本号小于",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "版本号范围",
Code: HTTPFirewallRuleOperatorVersionRange,
Description: "判断版本号在某个范围内格式为version1,version2",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP等于",
Code: HTTPFirewallRuleOperatorEqIP,
Description: "将参数转换为IP进行对比",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP大于",
Code: HTTPFirewallRuleOperatorGtIP,
Description: "将参数转换为IP进行对比",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP大于等于",
Code: HTTPFirewallRuleOperatorGteIP,
Description: "将参数转换为IP进行对比",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP小于",
Code: HTTPFirewallRuleOperatorLtIP,
Description: "将参数转换为IP进行对比",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP小于等于",
Code: HTTPFirewallRuleOperatorLteIP,
Description: "将参数转换为IP进行对比",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP范围",
Code: HTTPFirewallRuleOperatorIPRange,
Description: "IP在某个范围之内范围格式可以是英文逗号分隔的ip1,ip2或者CIDR格式的ip/bits",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "不在IP范围",
Code: HTTPFirewallRuleOperatorNotIPRange,
Description: "IP不在某个范围之内范围格式可以是英文逗号分隔的ip1,ip2或者CIDR格式的ip/bits",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP取模10",
Code: HTTPFirewallRuleOperatorIPMod10,
Description: "对IP参数值取模除数为10对比值为余数",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP取模100",
Code: HTTPFirewallRuleOperatorIPMod100,
Description: "对IP参数值取模除数为100对比值为余数",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
{
Name: "IP取模",
Code: HTTPFirewallRuleOperatorIPMod,
Description: "对IP参数值取模对比值格式为除数,余数比如10,1",
CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo,
},
}

View File

@@ -0,0 +1,19 @@
package firewallconfigs
import (
"fmt"
"strings"
"testing"
)
func TestRuleOperator_Markdown(t *testing.T) {
result := []string{}
for _, def := range AllRuleOperators {
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,33 @@
package firewallconfigs
import "github.com/iwind/TeaGo/maps"
// 规则集定义
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"`
Action string `yaml:"action" json:"action"`
ActionOptions maps.Map `yaml:"actionOptions" json:"actionOptions"`
}
// 初始化
func (this *HTTPFirewallRuleSet) Init() error {
for _, rule := range this.Rules {
err := rule.Init()
if err != nil {
return err
}
}
return nil
}
// 添加规则
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,511 @@
package firewallconfigs
type HTTPFirewallRuleConnector = string
const (
HTTPFirewallRuleConnectorAnd = "and"
HTTPFirewallRuleConnectorOr = "or"
)
func HTTPFirewallTemplate() *HTTPFirewallPolicy {
policy := &HTTPFirewallPolicy{}
policy.IsOn = true
policy.Inbound = &HTTPFirewallInboundConfig{}
policy.Outbound = &HTTPFirewallOutboundConfig{}
// black list
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = false
group.Name = "白名单"
group.Code = "whiteList"
group.Description = "在此名单中的IP地址可以直接跳过防火墙设置"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "IP白名单"
set.Code = "9001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionAllow
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `127\.0\.0\.1|0\.0\.0\.0`,
IsCaseInsensitive: false,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// black list
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = false
group.Name = "黑名单"
group.Code = "blackList"
group.Description = "在此名单中的IP地址直接阻止"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "IP黑名单"
set.Code = "10001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `1\.1\.1\.1|2\.2\.2\.2`,
IsCaseInsensitive: false,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// xss
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "XSS"
group.Code = "xss"
group.Description = "防跨站脚本攻击Cross Site Scripting"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "Javascript事件"
set.Code = "1001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestURI}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)\s*=`, // TODO more keywords here
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "Javascript函数"
set.Code = "1002"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestURI}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `(alert|eval|prompt|confirm)\s*\(`, // TODO more keywords here
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "HTML标签"
set.Code = "1003"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestURI}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `<(script|iframe|link)`, // TODO more keywords here
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// upload
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "文件上传"
group.Code = "upload"
group.Description = "防止上传可执行脚本文件到服务器"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "上传文件扩展名"
set.Code = "2001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
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
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "Web Shell"
group.Code = "webShell"
group.Description = "防止远程执行服务器命令"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "Web Shell"
set.Code = "3001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
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
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "命令注入"
group.Code = "commandInjection"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "命令注入"
set.Code = "4001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
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
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "路径穿越"
group.Code = "pathTraversal"
group.Description = "防止读取网站目录之外的其他系统文件"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "路径穿越"
set.Code = "5001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestURI}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `((\.+)(/+)){2,}`, // TODO more keywords here
IsCaseInsensitive: false,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// special dirs
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "特殊目录"
group.Code = "denyDirs"
group.Description = "防止通过Web访问到一些特殊目录"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "特殊目录"
set.Code = "6001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestPath}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `/\.(git|svn|htaccess|idea)\b`, // TODO more keywords here
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// sql injection
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "SQL注入"
group.Code = "sqlInjection"
group.Description = "防止SQL注入漏洞"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "Union SQL Injection"
set.Code = "7001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `union[\s/\*]+select`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "SQL注释"
set.Code = "7002"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `/\*(!|\x00)`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "SQL条件"
set.Code = "7003"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `\s(and|or|rlike)\s+(if|updatexml)\s*\(`,
IsCaseInsensitive: true,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `\s+(and|or|rlike)\s+(select|case)\s+`,
IsCaseInsensitive: true,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `\s+(and|or|procedure)\s+[\w\p{L}]+\s*=\s*[\w\p{L}]+(\s|$|--|#)`,
IsCaseInsensitive: true,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `\(\s*case\s+when\s+[\w\p{L}]+\s*=\s*[\w\p{L}]+\s+then\s+`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "SQL函数"
set.Code = "7004"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `(updatexml|extractvalue|ascii|ord|char|chr|count|concat|rand|floor|substr|length|len|user|database|benchmark|analyse)\s*\(`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "SQL附加语句"
set.Code = "7005"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${requestAll}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `;\s*(declare|use|drop|create|exec|delete|update|insert)\s`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// bot
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "网络爬虫"
group.Code = "bot"
group.Description = "禁止一些网络爬虫"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "常见网络爬虫"
set.Code = "20001"
set.Connector = HTTPFirewallRuleConnectorOr
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${userAgent}",
Operator: HTTPFirewallRuleOperatorMatch,
Value: `Googlebot|AdsBot|bingbot|BingPreview|facebookexternalhit|Slurp|Sogou|proximic|Baiduspider|yandex|twitterbot|spider|python`,
IsCaseInsensitive: true,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// cc
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "CC攻击"
group.Description = "Challenge Collapsar防止短时间大量请求涌入请谨慎开启和设置"
group.Code = "cc"
{
set := &HTTPFirewallRuleSet{}
set.IsOn = true
set.Name = "CC请求数"
set.Description = "限制单IP在一定时间内的请求数"
set.Code = "8001"
set.Connector = HTTPFirewallRuleConnectorAnd
set.Action = HTTPFirewallActionBlock
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${cc.requests}",
Operator: HTTPFirewallRuleOperatorGt,
Value: "1000",
CheckpointOptions: map[string]interface{}{
"period": "60",
},
IsCaseInsensitive: false,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorNotIPRange,
Value: `127.0.0.1/8`,
IsCaseInsensitive: false,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorNotIPRange,
Value: `192.168.0.1/16`,
IsCaseInsensitive: false,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorNotIPRange,
Value: `10.0.0.1/8`,
IsCaseInsensitive: false,
})
set.AddRule(&HTTPFirewallRule{
IsOn: true,
Param: "${remoteAddr}",
Operator: HTTPFirewallRuleOperatorNotIPRange,
Value: `172.16.0.1/12`,
IsCaseInsensitive: false,
})
group.AddRuleSet(set)
}
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
// custom
{
group := &HTTPFirewallRuleGroup{}
group.IsOn = true
group.Name = "自定义规则分组"
group.Description = "我的自定义规则分组,可以将自定义的规则放在这个分组下"
group.Code = "custom"
policy.Inbound.Groups = append(policy.Inbound.Groups, group)
}
return policy
}

View File

@@ -1,28 +1,32 @@
package serverconfigs
import "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
)
type HTTPWebConfig struct {
Id int64 `yaml:"id" json:"id"` // ID
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
Locations []*HTTPLocationConfig `yaml:"locations" json:"locations"` // 路径规则 TODO
LocationRefs []*HTTPLocationRef `yaml:"locationRefs" json:"locationRefs"` // 路径规则应用
GzipRef *HTTPGzipRef `yaml:"gzipRef" json:"gzipRef"` // Gzip引用
Gzip *HTTPGzipConfig `yaml:"gzip" json:"gzip"` // Gzip配置
Charset *HTTPCharsetConfig `yaml:"charset" json:"charset"` // 字符编码
Shutdown *HTTPShutdownConfig `yaml:"shutdown" json:"shutdown"` // 临时关闭配置
Pages []*HTTPPageConfig `yaml:"pages" json:"pages"` // 特殊页面配置
RedirectToHttps *HTTPRedirectToHTTPSConfig `yaml:"redirectToHTTPS" json:"redirectToHTTPS"` // 是否自动跳转到Https
Root *HTTPRootConfig `yaml:"root" json:"root"` // 资源根目录 TODO
MaxRequestBodySize string `yaml:"maxRequestBodySize" json:"maxRequestBodySize"` // 请求body最大尺寸 TODO 需要实现
AccessLogRef *HTTPAccessLogRef `yaml:"accessLog" json:"accessLog"` // 访问日志配置
StatRef *HTTPStatRef `yaml:"statRef" json:"statRef"` // 统计配置
Cache *HTTPCacheConfig `yaml:"cache" json:"cache"`
FirewallRef *HTTPFirewallRef `yaml:"firewallRef" json:"firewallRef"` // 防火墙设置
WebsocketRef *HTTPWebsocketRef `yaml:"websocketRef" json:"websocketRef"` // Websocket应用配置
Websocket *HTTPWebsocketConfig `yaml:"websocket" json:"websocket"` // Websocket配置
RewriteRefs []*HTTPRewriteRef `yaml:"rewriteRefs" json:"rewriteRefs"` // 重写规则配置
RewriteRules []*HTTPRewriteRule `yaml:"rewriteRules" json:"rewriteRules"` // 重写规则
Id int64 `yaml:"id" json:"id"` // ID
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
Locations []*HTTPLocationConfig `yaml:"locations" json:"locations"` // 路径规则 TODO
LocationRefs []*HTTPLocationRef `yaml:"locationRefs" json:"locationRefs"` // 路径规则应用
GzipRef *HTTPGzipRef `yaml:"gzipRef" json:"gzipRef"` // Gzip引用
Gzip *HTTPGzipConfig `yaml:"gzip" json:"gzip"` // Gzip配置
Charset *HTTPCharsetConfig `yaml:"charset" json:"charset"` // 字符编码
Shutdown *HTTPShutdownConfig `yaml:"shutdown" json:"shutdown"` // 临时关闭配置
Pages []*HTTPPageConfig `yaml:"pages" json:"pages"` // 特殊页面配置
RedirectToHttps *HTTPRedirectToHTTPSConfig `yaml:"redirectToHTTPS" json:"redirectToHTTPS"` // 是否自动跳转到Https
Root *HTTPRootConfig `yaml:"root" json:"root"` // 资源根目录 TODO
MaxRequestBodySize string `yaml:"maxRequestBodySize" json:"maxRequestBodySize"` // 请求body最大尺寸 TODO 需要实现
AccessLogRef *HTTPAccessLogRef `yaml:"accessLog" json:"accessLog"` // 访问日志配置
StatRef *HTTPStatRef `yaml:"statRef" json:"statRef"` // 统计配置
Cache *HTTPCacheConfig `yaml:"cache" json:"cache"`
FirewallRef *firewallconfigs.HTTPFirewallRef `yaml:"firewallRef" json:"firewallRef"` // 防火墙设置
FirewallPolicy *firewallconfigs.HTTPFirewallPolicy `yaml:"firewallPolicy" json:"firewallPolicy"` // 防火墙策略
WebsocketRef *HTTPWebsocketRef `yaml:"websocketRef" json:"websocketRef"` // Websocket应用配置
Websocket *HTTPWebsocketConfig `yaml:"websocket" json:"websocket"` // Websocket配置
RewriteRefs []*HTTPRewriteRef `yaml:"rewriteRefs" json:"rewriteRefs"` // 重写规则配置
RewriteRules []*HTTPRewriteRule `yaml:"rewriteRules" json:"rewriteRules"` // 重写规则
RequestHeaderPolicyRef *shared.HTTPHeaderPolicyRef `yaml:"requestHeaderPolicyRef" json:"requestHeaderPolicyRef"` // 请求Header
RequestHeaderPolicy *shared.HTTPHeaderPolicy `yaml:"requestHeaderPolicy" json:"requestHeaderPolicy"` // 请求Header策略
@@ -125,6 +129,12 @@ func (this *HTTPWebConfig) Init() error {
return err
}
}
if this.FirewallPolicy != nil {
err := this.FirewallPolicy.Init()
if err != nil {
return err
}
}
// websocket
if this.WebsocketRef != nil {

View File

@@ -0,0 +1,147 @@
package shared
import (
"bytes"
"errors"
"github.com/iwind/TeaGo/utils/string"
"net"
"regexp"
"strings"
)
// IP Range类型
type IPRangeType = int
const (
IPRangeTypeRange IPRangeType = 1
IPRangeTypeCIDR IPRangeType = 2
IPRangeTypeAll IPRangeType = 3
IPRangeTypeWildcard IPRangeType = 4 // 通配符,可以使用*
)
// IP Range
type IPRangeConfig struct {
Id string `yaml:"id" json:"id"`
Type IPRangeType `yaml:"type" json:"type"`
Param string `yaml:"param" json:"param"`
CIDR string `yaml:"cidr" json:"cidr"`
IPFrom string `yaml:"ipFrom" json:"ipFrom"`
IPTo string `yaml:"ipTo" json:"ipTo"`
cidr *net.IPNet
ipFrom net.IP
ipTo net.IP
reg *regexp.Regexp
}
// 获取新对象
func NewIPRangeConfig() *IPRangeConfig {
return &IPRangeConfig{
Id: stringutil.Rand(16),
}
}
// 从字符串中分析
func ParseIPRange(s string) (*IPRangeConfig, error) {
if len(s) == 0 {
return nil, errors.New("invalid ip range")
}
ipRange := &IPRangeConfig{}
if s == "*" || s == "all" || s == "ALL" || s == "0.0.0.0" {
ipRange.Type = IPRangeTypeAll
return ipRange, nil
}
if strings.Contains(s, "/") {
ipRange.Type = IPRangeTypeCIDR
ipRange.CIDR = strings.Replace(s, " ", "", -1)
} else if strings.Contains(s, "-") {
ipRange.Type = IPRangeTypeRange
pieces := strings.SplitN(s, "-", 2)
ipRange.IPFrom = strings.TrimSpace(pieces[0])
ipRange.IPTo = strings.TrimSpace(pieces[1])
} else if strings.Contains(s, ",") {
ipRange.Type = IPRangeTypeRange
pieces := strings.SplitN(s, ",", 2)
ipRange.IPFrom = strings.TrimSpace(pieces[0])
ipRange.IPTo = strings.TrimSpace(pieces[1])
} else if strings.Contains(s, "*") {
ipRange.Type = IPRangeTypeWildcard
s = "^" + strings.Replace(regexp.QuoteMeta(s), `\*`, `\d+`, -1) + "$"
ipRange.reg = regexp.MustCompile(s)
} else {
ipRange.Type = IPRangeTypeRange
ipRange.IPFrom = s
ipRange.IPTo = s
}
err := ipRange.Validate()
if err != nil {
return nil, err
}
return ipRange, nil
}
// 校验
func (this *IPRangeConfig) Validate() error {
if this.Type == IPRangeTypeCIDR {
if len(this.CIDR) == 0 {
return errors.New("cidr should not be empty")
}
_, cidr, err := net.ParseCIDR(this.CIDR)
if err != nil {
return err
}
this.cidr = cidr
}
if this.Type == IPRangeTypeRange {
this.ipFrom = net.ParseIP(this.IPFrom)
this.ipTo = net.ParseIP(this.IPTo)
if this.ipFrom.To4() == nil && this.ipFrom.To16() == nil {
return errors.New("from ip should in IPv4 or IPV6 format")
}
if this.ipTo.To4() == nil && this.ipTo.To16() == nil {
return errors.New("to ip should in IPv4 or IPV6 format")
}
}
return nil
}
// 是否包含某个IP
func (this *IPRangeConfig) Contains(ipString string) bool {
ip := net.ParseIP(ipString)
if ip.To4() == nil {
return false
}
if this.Type == IPRangeTypeCIDR {
if this.cidr == nil {
return false
}
return this.cidr.Contains(ip)
}
if this.Type == IPRangeTypeRange {
if this.ipFrom == nil || this.ipTo == nil {
return false
}
return bytes.Compare(ip, this.ipFrom) >= 0 && bytes.Compare(ip, this.ipTo) <= 0
}
if this.Type == IPRangeTypeWildcard {
if this.reg == nil {
return false
}
return this.reg.MatchString(ipString)
}
if this.Type == IPRangeTypeAll {
return true
}
return false
}

View File

@@ -0,0 +1,125 @@
package shared
import (
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestGeoConfig_Contains(t *testing.T) {
a := assert.NewAssertion(t)
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeRange
geo.IPFrom = "192.168.1.100"
geo.IPTo = "192.168.1.110"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.1.100"))
a.IsTrue(geo.Contains("192.168.1.101"))
a.IsTrue(geo.Contains("192.168.1.110"))
a.IsFalse(geo.Contains("192.168.1.111"))
}
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeCIDR
geo.CIDR = "192.168.1.1/24"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.1.100"))
a.IsFalse(geo.Contains("192.168.2.100"))
}
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeCIDR
geo.CIDR = "192.168.1.1/16"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.2.100"))
}
}
func TestParseIPRange(t *testing.T) {
a := assert.NewAssertion(t)
{
_, err := ParseIPRange("")
a.IsNotNil(err)
}
{
r, err := ParseIPRange("192.168.1.100")
a.IsNil(err)
a.IsTrue(r.IPFrom == r.IPTo)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsFalse(r.Contains("192.168.1.99"))
}
{
r, err := ParseIPRange("192.168.1.100/24")
a.IsNil(err)
a.IsTrue(r.CIDR == "192.168.1.100/24")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.99"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.100, 192.168.1.200")
a.IsNil(err)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.IPTo == "192.168.1.200")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.100-192.168.1.200")
a.IsNil(err)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.IPTo == "192.168.1.200")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("all")
a.IsNil(err)
a.IsTrue(r.Type == IPRangeTypeAll)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsTrue(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.*")
a.IsNil(err)
if r != nil {
a.IsTrue(r.Type == IPRangeTypeWildcard)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsFalse(r.Contains("192.168.2.100"))
}
}
{
r, err := ParseIPRange("192.168.*.*")
a.IsNil(err)
if r != nil {
a.IsTrue(r.Type == IPRangeTypeWildcard)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.2.100"))
}
}
}
func BenchmarkIPRangeConfig_Contains(b *testing.B) {
r, err := ParseIPRange("192.168.1.*")
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
_ = r.Contains("192.168.1.100")
}
}