Files
waf-platform/EdgeNode/internal/nodes/http_request_uam_plus.go
2026-02-04 20:27:13 +08:00

211 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nodes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
"github.com/TeaOSLab/EdgeNode/internal/events"
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
"github.com/TeaOSLab/EdgeNode/internal/uam"
"github.com/TeaOSLab/EdgeNode/internal/utils/agents"
"github.com/TeaOSLab/EdgeNode/internal/utils/counters"
"github.com/TeaOSLab/EdgeNode/internal/utils/ttlcache"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/types"
"net/http"
"strings"
)
var sharedUAMManager *uam.Manager
func init() {
if !teaconst.IsMain {
return
}
events.On(events.EventLoaded, func() {
if sharedUAMManager != nil {
return
}
manager, _ := uam.NewManager(sharedNodeConfig.NodeId, sharedNodeConfig.Secret)
if manager != nil {
sharedUAMManager = manager
}
})
events.On(events.EventReload, func() {
if sharedUAMManager != nil {
return
}
manager, _ := uam.NewManager(sharedNodeConfig.NodeId, sharedNodeConfig.Secret)
if manager != nil {
sharedUAMManager = manager
}
})
}
func (this *HTTPRequest) isUAMRequest() bool {
if this.web.UAM == nil || !this.web.UAM.IsOn || this.RawReq.Method != http.MethodPost {
return false
}
var cookiesString = this.RawReq.Header.Get("Cookie")
if len(cookiesString) == 0 {
return false
}
return strings.HasPrefix(cookiesString, uam.CookiePrevKey+"=") ||
strings.Contains(cookiesString, " "+uam.CookiePrevKey+"=")
}
// UAM
// TODO 需要检查是否为plus
func (this *HTTPRequest) doUAM() (block bool) {
var serverId int64
if this.ReqServer != nil {
serverId = this.ReqServer.Id
}
var uamConfig = this.web.UAM
if uamConfig == nil ||
!uamConfig.IsOn ||
!uamConfig.MatchURL(this.requestScheme()+"://"+this.ReqHost+this.Path()) ||
!uamConfig.MatchRequest(this.Format) {
return
}
var policy = this.nodeConfig.FindUAMPolicyWithClusterId(this.ReqServer.ClusterId)
if policy == nil {
policy = nodeconfigs.NewUAMPolicy()
}
if policy == nil || !policy.IsOn {
return
}
// 获取UAM管理器
var manager = sharedUAMManager
if manager == nil {
return false
}
// 忽略URL白名单
if this.RawReq.URL.Path == "/favicon.ico" || this.RawReq.URL.Path == "/favicon.png" {
return false
}
// 检查白名单
var remoteAddr = this.requestRemoteAddr(true)
// 检查UAM白名单
if uamConfig.AddToWhiteList && ttlcache.SharedInt64Cache.Read("UAM:WHITE:"+remoteAddr) != nil {
return false
}
// 检查是否为白名单直连
if !Tea.IsTesting() && this.nodeConfig.IPIsAutoAllowed(remoteAddr) {
return
}
// 是否在全局名单中
canGoNext, isInAllowedList, _ := iplibrary.AllowIP(remoteAddr, serverId)
if !canGoNext {
this.disableLog = true
this.Close()
return true
}
if isInAllowedList {
return false
}
// 如果是搜索引擎直接通过
var userAgent = this.RawReq.UserAgent()
if len(userAgent) == 0 {
// 如果User-Agent为空则直接阻止
this.writer.WriteHeader(http.StatusForbidden)
// 增加失败次数
if manager.IncreaseFails(policy, remoteAddr, serverId) {
this.isAttack = true
}
return true
}
// 不管是否开启允许搜索引擎,这里都放行,避免收录了拦截的代码
if agents.SharedManager.ContainsIP(remoteAddr) {
return false
}
if policy.AllowSearchEngines {
if searchEngineRegex.MatchString(userAgent) {
return false
}
}
// 如果是python之类的直接拦截
if policy.DenySpiders && spiderRegexp.MatchString(userAgent) {
this.writer.WriteHeader(http.StatusForbidden)
// 增加失败次数
if manager.IncreaseFails(policy, remoteAddr, serverId) {
this.isAttack = true
}
return true
}
// 检查预生成Key
var step = this.Header().Get("X-GE-UA-Step")
if step == uam.StepPrev {
if this.Method() != http.MethodPost {
this.writer.WriteHeader(http.StatusForbidden)
return true
}
if manager.CheckPrevKey(policy, uamConfig, this.RawReq, remoteAddr, this.writer) {
_, _ = this.writer.Write([]byte(`{"ok": true}`))
} else {
_, _ = this.writer.Write([]byte(`{"ok": false}`))
}
// 增加失败次数
if manager.IncreaseFails(policy, remoteAddr, serverId) {
this.isAttack = true
}
return true
}
// 检查是否启用QPS
if uamConfig.MinQPSPerIP > 0 && len(step) == 0 {
var avgQPS = counters.SharedCounter.IncreaseKey("UAM:"+remoteAddr, 60) / 60
if avgQPS <= 0 {
avgQPS = 1
}
if avgQPS < types.Uint32(uamConfig.MinQPSPerIP) {
return false
}
}
// 检查Cookie中的Key
isVerified, isAttack, _ := manager.CheckKey(policy, this.RawReq, this.writer, remoteAddr, serverId, uamConfig.KeyLife)
if isVerified {
return false
}
this.isAttack = isAttack
// 检查是否已生成有效的prev key如果已经生成则表示当前请求是附带请求比如favicon.ico不再重新生成新的
// TODO 考虑这里的必要性
if manager.ExistsActivePreKey(this.RawReq) {
return true
}
// 显示加载页面
err := manager.LoadPage(policy, this.RawReq, this.Format, remoteAddr, this.writer)
if err != nil {
return false
}
return true
}