fix(node): keep a node's online and IP reports to its own clients

A node's online tree and both client-IP reports were stored as sent:
they could name any client of the master or another node, under any
panel's GUID, dated into the future, and in any number. That marked
master clients online, fed source addresses for clients the node does
not serve into the IP limit and its fail2ban log, and planted rows the
retention sweep never removed.

Each report is now cut to the clients linked to the node's inbounds and
to the node's own key and its sub-nodes' origins (never this panel's or
another node's GUID); timestamps are capped at now, and one client's
list from a node keeps at most its 256 newest entries.
This commit is contained in:
MHSanaei
2026-10-31 14:14:01 +01:00
parent 4d27313c5e
commit a69f2dee13
6 changed files with 233 additions and 3 deletions
+40 -2
View File
@@ -1499,7 +1499,17 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
}
}
// The online report outlives this merge, so it is cut first to the node's own clients.
if len(snap.OnlineTree) > 0 || len(snap.OnlineEmails) > 0 {
attached, err := nodeAttachedEmails(db, nodeID)
if err != nil {
logger.Warningf("setRemoteTraffic: load the clients of node %d failed, dropping its online report: %v", nodeID, err)
}
keepAttachedOnlineEmails(snap, attached)
}
if process := currentXrayProcess(); process != nil {
hostedGuids := activeInboundGuidKeys(snap.Inbounds, tagToCentral, originGuidFor)
tree := snap.OnlineTree
switch {
case len(tree) == 0 && len(snap.OnlineEmails) > 0:
@@ -1514,7 +1524,7 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
tree = remapGuidTreeKey(tree, nodeRow.Guid, selfKey)
}
}
process.SetNodeOnlineTree(nodeID, tree)
process.SetNodeOnlineTree(nodeID, filterGuidTreeKeys(tree, hostedGuids))
activeTree := normalizeActiveInboundTreeTags(snap.ActiveInboundTree, tagToCentral)
if guidShared && len(activeTree) > 0 {
@@ -1523,7 +1533,7 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
}
}
if len(activeTree) > 0 {
activeTree = filterGuidTreeKeys(activeTree, activeInboundGuidKeys(snap.Inbounds, tagToCentral, originGuidFor))
activeTree = filterGuidTreeKeys(activeTree, hostedGuids)
}
process.SetNodeActiveInboundTree(nodeID, activeTree)
}
@@ -1700,6 +1710,34 @@ func remapGuidTreeKey(tree map[string][]string, from, to string) map[string][]st
return remapped
}
// keepAttachedOnlineEmails cuts a node's online report to the clients linked to its inbounds,
// once each per GUID, into fresh slices so the reported ones are not pinned.
func keepAttachedOnlineEmails(snap *runtime.TrafficSnapshot, attached map[string]struct{}) {
keep := func(emails []string) []string {
var kept []string
seen := make(map[string]struct{})
for _, email := range emails {
if _, ok := attached[email]; !ok {
continue
}
if _, dup := seen[email]; dup {
continue
}
seen[email] = struct{}{}
kept = append(kept, email)
}
return kept
}
snap.OnlineEmails = keep(snap.OnlineEmails)
tree := make(map[string][]string, len(snap.OnlineTree))
for guid, emails := range snap.OnlineTree {
if kept := keep(emails); len(kept) > 0 {
tree[guid] = kept
}
}
snap.OnlineTree = tree
}
func normalizeActiveInboundTreeTags(tree map[string][]string, tagToCentral map[string]*model.Inbound) map[string][]string {
if len(tree) == 0 {
return nil