mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-03 06:40:25 +08:00
240 lines
6.4 KiB
Go
240 lines
6.4 KiB
Go
package waf
|
|
|
|
import (
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ipconfigs"
|
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
|
|
"github.com/iwind/TeaGo/types"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type recordIPTask struct {
|
|
ip string
|
|
listId int64
|
|
expiresAt int64
|
|
level string
|
|
serverId int64
|
|
|
|
reason string
|
|
|
|
sourceServerId int64
|
|
sourceHTTPFirewallPolicyId int64
|
|
sourceHTTPFirewallRuleGroupId int64
|
|
sourceHTTPFirewallRuleSetId int64
|
|
}
|
|
|
|
var recordIPTaskChan = make(chan *recordIPTask, 2048)
|
|
|
|
func init() {
|
|
if !teaconst.IsMain {
|
|
return
|
|
}
|
|
|
|
events.On(events.EventLoaded, func() {
|
|
goman.New(func() {
|
|
rpcClient, err := rpc.SharedRPC()
|
|
if err != nil {
|
|
remotelogs.Error("WAF_RECORD_IP_ACTION", "create rpc client failed: "+err.Error())
|
|
return
|
|
}
|
|
|
|
const maxItems = 512 // 每次上传的最大IP数
|
|
|
|
for {
|
|
var pbItemMap = map[string]*pb.CreateIPItemsRequest_IPItem{} // ip => IPItem
|
|
|
|
func() {
|
|
for {
|
|
select {
|
|
case task := <-recordIPTaskChan:
|
|
var ipType = "ipv4"
|
|
if strings.Contains(task.ip, ":") {
|
|
ipType = "ipv6"
|
|
}
|
|
var reason = task.reason
|
|
if len(reason) == 0 {
|
|
reason = "触发WAF规则自动加入"
|
|
}
|
|
|
|
pbItemMap[task.ip] = &pb.CreateIPItemsRequest_IPItem{
|
|
IpListId: task.listId,
|
|
Value: task.ip,
|
|
IpFrom: task.ip,
|
|
IpTo: "",
|
|
ExpiredAt: task.expiresAt,
|
|
Reason: reason,
|
|
Type: ipType,
|
|
EventLevel: task.level,
|
|
ServerId: task.serverId,
|
|
SourceNodeId: teaconst.NodeId,
|
|
SourceServerId: task.sourceServerId,
|
|
SourceHTTPFirewallPolicyId: task.sourceHTTPFirewallPolicyId,
|
|
SourceHTTPFirewallRuleGroupId: task.sourceHTTPFirewallRuleGroupId,
|
|
SourceHTTPFirewallRuleSetId: task.sourceHTTPFirewallRuleSetId,
|
|
}
|
|
|
|
if len(pbItemMap) >= maxItems {
|
|
return
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
if len(pbItemMap) > 0 {
|
|
var pbItems = []*pb.CreateIPItemsRequest_IPItem{}
|
|
for _, pbItem := range pbItemMap {
|
|
pbItems = append(pbItems, pbItem)
|
|
}
|
|
_, err = rpcClient.IPItemRPC.CreateIPItems(rpcClient.Context(), &pb.CreateIPItemsRequest{IpItems: pbItems})
|
|
if err != nil {
|
|
remotelogs.Error("WAF_RECORD_IP_ACTION", "create ip item failed: "+err.Error())
|
|
}
|
|
} else {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
type RecordIPAction struct {
|
|
BaseAction
|
|
|
|
Type string `yaml:"type" json:"type"`
|
|
IPListId int64 `yaml:"ipListId" json:"ipListId"`
|
|
IPListIsDeleted bool `yaml:"ipListIsDeleted" json:"ipListIsDeleted"`
|
|
Level string `yaml:"level" json:"level"`
|
|
Timeout int32 `yaml:"timeout" json:"timeout"`
|
|
Scope string `yaml:"scope" json:"scope"`
|
|
}
|
|
|
|
func (this *RecordIPAction) Init(waf *WAF) error {
|
|
return nil
|
|
}
|
|
|
|
func (this *RecordIPAction) Code() string {
|
|
return ActionRecordIP
|
|
}
|
|
|
|
func (this *RecordIPAction) IsAttack() bool {
|
|
return this.Type == ipconfigs.IPListTypeBlack
|
|
}
|
|
|
|
func (this *RecordIPAction) WillChange() bool {
|
|
return this.Type == ipconfigs.IPListTypeBlack
|
|
}
|
|
|
|
func (this *RecordIPAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, request requests.Request, writer http.ResponseWriter) PerformResult {
|
|
var ipListId = this.IPListId
|
|
|
|
|
|
if ipListId <= 0 || firewallconfigs.IsGlobalListId(ipListId) {
|
|
// server or policy list ids
|
|
switch this.Type {
|
|
case ipconfigs.IPListTypeWhite:
|
|
ipListId = waf.AllowListId
|
|
case ipconfigs.IPListTypeBlack:
|
|
ipListId = waf.DenyListId
|
|
case ipconfigs.IPListTypeGrey:
|
|
ipListId = waf.GreyListId
|
|
}
|
|
|
|
// global list ids
|
|
if ipListId <= 0 {
|
|
ipListId = firewallconfigs.FindGlobalListIdWithType(this.Type)
|
|
if ipListId <= 0 {
|
|
ipListId = firewallconfigs.GlobalBlackListId
|
|
}
|
|
}
|
|
}
|
|
|
|
// 是否已删除
|
|
var ipListIsAvailable = (firewallconfigs.IsGlobalListId(ipListId)) || (ipListId > 0 && !this.IPListIsDeleted && !ExistDeletedIPList(ipListId))
|
|
|
|
// 是否在本地白名单中
|
|
if SharedIPWhiteList.Contains("set:"+types.String(set.Id), this.Scope, request.WAFServerId(), request.WAFRemoteIP()) {
|
|
return PerformResult{
|
|
ContinueRequest: true,
|
|
IsAllowed: true,
|
|
AllowScope: AllowScopeGlobal,
|
|
}
|
|
}
|
|
|
|
var timeout = this.Timeout
|
|
var isForever = false
|
|
if timeout <= 0 {
|
|
isForever = true
|
|
timeout = 86400 // 1天
|
|
}
|
|
var expiresAt = time.Now().Unix() + int64(timeout)
|
|
|
|
var isGrey bool
|
|
var isWhite bool
|
|
|
|
if this.Type == ipconfigs.IPListTypeBlack {
|
|
request.ProcessResponseHeaders(writer.Header(), http.StatusForbidden)
|
|
writer.WriteHeader(http.StatusForbidden)
|
|
|
|
request.WAFClose()
|
|
|
|
// 先加入本地的黑名单
|
|
if ipListIsAvailable {
|
|
SharedIPBlackList.Add(IPTypeAll, this.Scope, request.WAFServerId(), request.WAFRemoteIP(), expiresAt)
|
|
}
|
|
} else if this.Type == ipconfigs.IPListTypeGrey {
|
|
isGrey = true
|
|
} else {
|
|
isWhite = true
|
|
|
|
// 加入本地白名单
|
|
if ipListIsAvailable {
|
|
SharedIPWhiteList.Add("set:"+types.String(set.Id), this.Scope, request.WAFServerId(), request.WAFRemoteIP(), expiresAt)
|
|
}
|
|
}
|
|
|
|
// 上报
|
|
if ipListId > 0 && ipListIsAvailable {
|
|
var serverId int64
|
|
if this.Scope == firewallconfigs.FirewallScopeServer {
|
|
serverId = request.WAFServerId()
|
|
}
|
|
|
|
var realExpiresAt = expiresAt
|
|
if isForever {
|
|
realExpiresAt = 0
|
|
}
|
|
|
|
select {
|
|
case recordIPTaskChan <- &recordIPTask{
|
|
ip: request.WAFRemoteIP(),
|
|
listId: ipListId,
|
|
expiresAt: realExpiresAt,
|
|
level: this.Level,
|
|
serverId: serverId,
|
|
sourceServerId: request.WAFServerId(),
|
|
sourceHTTPFirewallPolicyId: waf.Id,
|
|
sourceHTTPFirewallRuleGroupId: group.Id,
|
|
sourceHTTPFirewallRuleSetId: set.Id,
|
|
}:
|
|
default:
|
|
|
|
}
|
|
}
|
|
|
|
return PerformResult{
|
|
ContinueRequest: isWhite || isGrey,
|
|
IsAllowed: isWhite,
|
|
AllowScope: AllowScopeGlobal,
|
|
}
|
|
}
|