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,271 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package nodes
import (
"context"
"encoding/base64"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils/encrypt"
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
"sync"
"time"
)
const (
LNKeyHeader = "X-Edge-Ln-Key"
LNExpiresHeader = "X-Edge-Ln-Expires"
LnExpiresSeconds int64 = 30
)
var lnOriginConfigsMap = map[string]*serverconfigs.OriginConfig{}
var lnEncryptMethodMap = map[string]encrypt.MethodInterface{}
var lnRequestIdSet = setutils.NewFixedSet(32 * 1024)
var lnNodeIPMap = map[string]bool{} // node ip => bool
var lnNodeIPLocker = &sync.RWMutex{}
var lnLocker = &sync.RWMutex{}
func existsLnNodeIP(nodeIP string) bool {
lnNodeIPLocker.RLock()
defer lnNodeIPLocker.RUnlock()
return lnNodeIPMap[nodeIP]
}
func (this *HTTPRequest) checkLnRequest() bool {
var keyString = this.RawReq.Header.Get(LNKeyHeader)
if len(keyString) <= 20 {
return false
}
// 删除
this.RawReq.Header.Del(LNKeyHeader)
// 当前连接IP
connIP, _, _ := net.SplitHostPort(this.RawReq.RemoteAddr)
var realIP = this.RawReq.Header.Get("X-Real-Ip")
if len(realIP) > 0 && !iputils.IsValid(realIP) {
realIP = ""
}
// 如果是在已经识别的L[n-1]节点IP列表中无需再次验证
if existsLnNodeIP(connIP) && len(realIP) > 0 {
this.tags = append(this.tags, "L"+types.String(this.nodeConfig.Level))
this.lnRemoteAddr = realIP
return true
}
// 如果已经在允许的IP中则直接允许通过无需再次验证
// 这个需要放在 existsLnNodeIP() 检查之后
if this.nodeConfig != nil && this.nodeConfig.IPIsAutoAllowed(connIP) && len(realIP) > 0 {
this.tags = append(this.tags, "L"+types.String(this.nodeConfig.Level))
this.lnRemoteAddr = realIP
lnNodeIPLocker.Lock()
lnNodeIPMap[connIP] = true
lnNodeIPLocker.Unlock()
return true
}
// 检查Key
keyEncodedData, err := base64.StdEncoding.DecodeString(keyString)
if err != nil {
return false
}
var secret = this.nodeConfig.SecretHash()
lnLocker.Lock()
method, ok := lnEncryptMethodMap[secret]
if !ok {
method, err = encrypt.NewMethodInstance("aes-192-cfb", secret, secret)
if err != nil {
lnLocker.Unlock()
return false
}
}
lnLocker.Unlock()
keyData, err := method.Decrypt(keyEncodedData)
if err != nil {
return false
}
var key = &LnRequestKey{}
err = UnmarshalLnRequestKey(keyData, key)
if err != nil {
return false
}
// Method和URL需要一致
if key.URLMd5 != stringutil.Md5(this.URL()) || key.Method != this.Method() {
return false
}
// N秒钟过期这里要求两个节点时间相差不能超过此时间
var currentUnixTime = fasttime.Now().Unix()
if key.Timestamp < currentUnixTime-LnExpiresSeconds {
return false
}
// 检查请求ID唯一性
// TODO 因为FixedSet是有限的这里仍然无法避免重放攻击
// TODO 而RequestId是并发的并不能简单的对比大小
// TODO 所以为了绝对的安全需要使用HTTPS并检查子节点的IP
if lnRequestIdSet.Has(key.RequestId) {
return false
}
lnRequestIdSet.Push(key.RequestId)
this.lnRemoteAddr = key.RemoteAddr
this.tags = append(this.tags, "L"+types.String(this.nodeConfig.Level))
// 当前连接IP
if len(connIP) > 0 {
lnNodeIPLocker.Lock()
lnNodeIPMap[connIP] = true
lnNodeIPLocker.Unlock()
}
return true
}
func (this *HTTPRequest) getLnOrigin(excludingNodeIds []int64, urlHash uint64) (originConfig *serverconfigs.OriginConfig, lnNodeId int64, hasMultipleNodes bool) {
var parentNodesMap = this.nodeConfig.ParentNodes // 需要复制,防止运行过程中修改
if len(parentNodesMap) == 0 {
return nil, 0, false
}
parentNodes, ok := parentNodesMap[this.ReqServer.ClusterId]
var countParentNodes = len(parentNodes)
if ok && countParentNodes > 0 {
var parentNode *nodeconfigs.ParentNodeConfig
// 尝试顺序读取
if len(excludingNodeIds) > 0 {
for _, node := range parentNodes {
if !lists.ContainsInt64(excludingNodeIds, node.Id) {
parentNode = node
break
}
}
}
// 尝试随机读取
if parentNode == nil {
if countParentNodes == 1 {
parentNode = parentNodes[0]
} else {
var method = serverconfigs.LnRequestSchedulingMethodURLMapping
if this.nodeConfig != nil {
var globalServerConfig = this.nodeConfig.GlobalServerConfig // copy
if globalServerConfig != nil {
method = globalServerConfig.HTTPAll.LnRequestSchedulingMethod
}
}
switch method {
case serverconfigs.LnRequestSchedulingMethodRandom:
// 随机选取一个L2节点有利于流量均衡但同一份缓存可能会存在多个L2节点上占用更多的空间
parentNode = parentNodes[rands.Int(0, countParentNodes-1)]
default:
// 从固定的L2节点读取内容优点是能够提升缓存命中率缺点是可能会导致多个L2节点流量不均衡
parentNode = parentNodes[urlHash%uint64(countParentNodes)]
}
}
}
lnNodeId = parentNode.Id
var countAddrs = len(parentNode.Addrs)
if countAddrs == 0 {
return nil, 0, false
}
var addr = parentNode.Addrs[rands.Int(0, countAddrs-1)]
var protocol = this.requestScheme() // http|https TODO 需要可以设置是否强制HTTPS回二级节点
var portString = types.String(this.requestServerPort())
var originKey = protocol + "@" + addr + "@" + portString
lnLocker.RLock()
config, ok := lnOriginConfigsMap[originKey]
lnLocker.RUnlock()
if !ok {
config = &serverconfigs.OriginConfig{
Id: 0,
IsOn: true,
Addr: &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(protocol),
Host: addr,
PortRange: portString,
},
IsOk: true,
}
err := config.Init(context.Background())
if err != nil {
remotelogs.Error("HTTP_REQUEST", "create ln origin config failed: "+err.Error())
return nil, 0, false
}
lnLocker.Lock()
lnOriginConfigsMap[originKey] = config
lnLocker.Unlock()
}
// 添加Header
this.RawReq.Header.Set(LNKeyHeader, this.encodeLnKey(parentNode.SecretHash))
return config, lnNodeId, len(parentNodes) > 0
}
return nil, 0, false
}
func (this *HTTPRequest) encodeLnKey(secretHash string) string {
var key = &LnRequestKey{
NodeId: this.nodeConfig.Id,
RequestId: this.requestId,
Timestamp: time.Now().Unix(),
RemoteAddr: this.RemoteAddr(),
URLMd5: stringutil.Md5(this.URL()),
Method: this.Method(),
}
data, err := key.AsPB()
if err != nil {
remotelogs.Error("HTTP_REQUEST", "ln request: encode key failed: "+err.Error())
return ""
}
lnLocker.Lock()
method, ok := lnEncryptMethodMap[secretHash]
if !ok {
method, err = encrypt.NewMethodInstance("aes-192-cfb", secretHash, secretHash)
if err != nil {
remotelogs.Error("HTTP_REQUEST", "ln request: create encrypt method failed: "+err.Error())
lnLocker.Unlock()
return ""
}
lnEncryptMethodMap[secretHash] = method
}
lnLocker.Unlock()
dst, err := method.Encrypt(data)
if err != nil {
remotelogs.Error("HTTP_REQUEST", "ln request: encode key failed: "+err.Error())
return ""
}
return base64.StdEncoding.EncodeToString(dst)
}