mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2026-04-08 06:35:19 +08:00
实现WAF
This commit is contained in:
@@ -96,7 +96,11 @@ func (this *HTTPRequest) Do() {
|
||||
}
|
||||
|
||||
// WAF
|
||||
// TODO 需要实现
|
||||
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn && this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
|
||||
if this.doWAFRequest() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 访问控制
|
||||
// TODO 需要实现
|
||||
@@ -253,6 +257,12 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
||||
this.web.Cache = web.Cache
|
||||
}
|
||||
|
||||
// waf
|
||||
if web.FirewallRef != nil && (web.FirewallRef.IsPrior || isTop) {
|
||||
this.web.FirewallRef = web.FirewallRef
|
||||
this.web.FirewallPolicy = web.FirewallPolicy
|
||||
}
|
||||
|
||||
// 重写规则
|
||||
if len(web.RewriteRefs) > 0 {
|
||||
for index, ref := range web.RewriteRefs {
|
||||
|
||||
@@ -166,12 +166,26 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
}
|
||||
|
||||
// WAF对出站进行检查
|
||||
// TODO
|
||||
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn && this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
|
||||
if this.doWAFResponse(resp) {
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 清除源站错误次数
|
||||
|
||||
// 特殊页面
|
||||
// TODO
|
||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 设置Charset
|
||||
// TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集
|
||||
|
||||
51
internal/nodes/http_request_waf.go
Normal file
51
internal/nodes/http_request_waf.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 调用WAF
|
||||
func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
||||
w := sharedWAFManager.FindWAF(this.web.FirewallPolicy.Id)
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
goNext, _, ruleSet, err := w.MatchRequest(this.RawReq, this.writer)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if ruleSet != nil {
|
||||
if ruleSet.Action != waf.ActionAllow {
|
||||
// TODO 记录日志
|
||||
}
|
||||
}
|
||||
|
||||
return !goNext
|
||||
}
|
||||
|
||||
// call response waf
|
||||
func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
||||
w := sharedWAFManager.FindWAF(this.web.FirewallPolicy.Id)
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
goNext, _, ruleSet, err := w.MatchResponse(this.RawReq, resp, this.writer)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if ruleSet != nil {
|
||||
if ruleSet.Action != waf.ActionAllow {
|
||||
// TODO 记录日志
|
||||
}
|
||||
}
|
||||
|
||||
return !goNext
|
||||
}
|
||||
@@ -104,6 +104,7 @@ func (this *Node) syncConfig(isFirstTime bool) error {
|
||||
logs.Println("[NODE]reload config ...")
|
||||
nodeconfigs.ResetNodeConfig(nodeConfig)
|
||||
caches.SharedManager.UpdatePolicies(nodeConfig.AllCachePolicies())
|
||||
sharedWAFManager.UpdatePolicies(nodeConfig.AllHTTPFirewallPolicies())
|
||||
sharedNodeConfig = nodeConfig
|
||||
|
||||
if !isFirstTime {
|
||||
|
||||
175
internal/nodes/waf_manager.go
Normal file
175
internal/nodes/waf_manager.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var sharedWAFManager = NewWAFManager()
|
||||
|
||||
// WAF管理器
|
||||
type WAFManager struct {
|
||||
mapping map[int64]*waf.WAF // policyId => WAF
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewWAFManager() *WAFManager {
|
||||
return &WAFManager{
|
||||
mapping: map[int64]*waf.WAF{},
|
||||
}
|
||||
}
|
||||
|
||||
// 更新策略
|
||||
func (this *WAFManager) UpdatePolicies(policies []*firewallconfigs.HTTPFirewallPolicy) {
|
||||
this.locker.Lock()
|
||||
defer this.locker.Unlock()
|
||||
|
||||
m := map[int64]*waf.WAF{}
|
||||
for _, p := range policies {
|
||||
w, err := this.convertWAF(p)
|
||||
if err != nil {
|
||||
logs.Println("[WAF]initialize policy '" + strconv.FormatInt(p.Id, 10) + "' failed: " + err.Error())
|
||||
continue
|
||||
}
|
||||
if w == nil {
|
||||
continue
|
||||
}
|
||||
m[p.Id] = w
|
||||
}
|
||||
this.mapping = m
|
||||
}
|
||||
|
||||
// 查找WAF
|
||||
func (this *WAFManager) FindWAF(policyId int64) *waf.WAF {
|
||||
this.locker.RLock()
|
||||
w, _ := this.mapping[policyId]
|
||||
this.locker.RUnlock()
|
||||
return w
|
||||
}
|
||||
|
||||
// 判断是否包含int64
|
||||
func (this *WAFManager) containsInt64(values []int64, value int64) bool {
|
||||
for _, v := range values {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 将Policy转换为WAF
|
||||
func (this *WAFManager) convertWAF(policy *firewallconfigs.HTTPFirewallPolicy) (*waf.WAF, error) {
|
||||
if policy == nil {
|
||||
return nil, errors.New("policy should not be nil")
|
||||
}
|
||||
w := &waf.WAF{
|
||||
Id: strconv.FormatInt(policy.Id, 10),
|
||||
IsOn: policy.IsOn,
|
||||
Name: policy.Name,
|
||||
}
|
||||
|
||||
// inbound
|
||||
if policy.Inbound != nil && policy.Inbound.IsOn {
|
||||
for _, group := range policy.Inbound.Groups {
|
||||
g := &waf.RuleGroup{
|
||||
Id: strconv.FormatInt(group.Id, 10),
|
||||
IsOn: group.IsOn,
|
||||
Name: group.Name,
|
||||
Description: group.Description,
|
||||
Code: group.Code,
|
||||
IsInbound: true,
|
||||
}
|
||||
|
||||
// rule sets
|
||||
for _, set := range group.Sets {
|
||||
s := &waf.RuleSet{
|
||||
Id: strconv.FormatInt(set.Id, 10),
|
||||
Code: set.Code,
|
||||
IsOn: set.IsOn,
|
||||
Name: set.Name,
|
||||
Description: set.Description,
|
||||
Connector: set.Connector,
|
||||
Action: set.Action,
|
||||
ActionOptions: set.ActionOptions,
|
||||
}
|
||||
|
||||
// rules
|
||||
for _, rule := range set.Rules {
|
||||
r := &waf.Rule{
|
||||
Description: rule.Description,
|
||||
Param: rule.Param,
|
||||
Operator: rule.Operator,
|
||||
Value: rule.Value,
|
||||
IsCaseInsensitive: rule.IsCaseInsensitive,
|
||||
CheckpointOptions: rule.CheckpointOptions,
|
||||
}
|
||||
s.Rules = append(s.Rules, r)
|
||||
}
|
||||
|
||||
g.RuleSets = append(g.RuleSets, s)
|
||||
}
|
||||
|
||||
w.Inbound = append(w.Inbound, g)
|
||||
}
|
||||
}
|
||||
|
||||
// outbound
|
||||
if policy.Outbound != nil && policy.Outbound.IsOn {
|
||||
for _, group := range policy.Outbound.Groups {
|
||||
g := &waf.RuleGroup{
|
||||
Id: strconv.FormatInt(group.Id, 10),
|
||||
IsOn: group.IsOn,
|
||||
Name: group.Name,
|
||||
Description: group.Description,
|
||||
Code: group.Code,
|
||||
IsInbound: true,
|
||||
}
|
||||
|
||||
// rule sets
|
||||
for _, set := range group.Sets {
|
||||
s := &waf.RuleSet{
|
||||
Id: strconv.FormatInt(set.Id, 10),
|
||||
Code: set.Code,
|
||||
IsOn: set.IsOn,
|
||||
Name: set.Name,
|
||||
Description: set.Description,
|
||||
Connector: set.Connector,
|
||||
Action: set.Action,
|
||||
ActionOptions: set.ActionOptions,
|
||||
}
|
||||
|
||||
// rules
|
||||
for _, rule := range set.Rules {
|
||||
r := &waf.Rule{
|
||||
Description: rule.Description,
|
||||
Param: rule.Param,
|
||||
Operator: rule.Operator,
|
||||
Value: rule.Value,
|
||||
IsCaseInsensitive: rule.IsCaseInsensitive,
|
||||
CheckpointOptions: rule.CheckpointOptions,
|
||||
}
|
||||
s.Rules = append(s.Rules, r)
|
||||
}
|
||||
|
||||
g.RuleSets = append(g.RuleSets, s)
|
||||
}
|
||||
|
||||
w.Outbound = append(w.Outbound, g)
|
||||
}
|
||||
}
|
||||
|
||||
// action
|
||||
// TODO
|
||||
|
||||
err := w.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
44
internal/nodes/waf_manager_test.go
Normal file
44
internal/nodes/waf_manager_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWAFManager_convert(t *testing.T) {
|
||||
p := &firewallconfigs.HTTPFirewallPolicy{
|
||||
Id: 1,
|
||||
IsOn: true,
|
||||
Inbound: &firewallconfigs.HTTPFirewallInboundConfig{
|
||||
IsOn: true,
|
||||
Groups: []*firewallconfigs.HTTPFirewallRuleGroup{
|
||||
{
|
||||
Id: 1,
|
||||
Sets: []*firewallconfigs.HTTPFirewallRuleSet{
|
||||
{
|
||||
Id: 1,
|
||||
},
|
||||
{
|
||||
Id: 2,
|
||||
Rules: []*firewallconfigs.HTTPFirewallRule{
|
||||
{
|
||||
Id: 1,
|
||||
},
|
||||
{
|
||||
Id: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
w, err := sharedWAFManager.convertWAF(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
logs.PrintAsJSON(w, t)
|
||||
}
|
||||
Reference in New Issue
Block a user