mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-09 22:55:00 +03:00
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:
@@ -307,3 +307,53 @@ func (s *InboundService) PruneStaleClientIps() error {
|
||||
}
|
||||
return pruneStaleNodeClientIps(cutoff)
|
||||
}
|
||||
|
||||
// nodeAttachedEmails is every client linked to one of the node's inbounds here: what a
|
||||
// node reports about any other client (online state, source IPs) is not its to report.
|
||||
func nodeAttachedEmails(db *gorm.DB, nodeID int) (map[string]struct{}, error) {
|
||||
var emails []string
|
||||
err := db.Table("clients").
|
||||
Joins("JOIN client_inbounds ON client_inbounds.client_id = clients.id").
|
||||
Joins("JOIN inbounds ON inbounds.id = client_inbounds.inbound_id").
|
||||
Where("inbounds.node_id = ?", nodeID).
|
||||
Distinct().Pluck("clients.email", &emails).Error
|
||||
attached := make(map[string]struct{}, len(emails))
|
||||
for _, email := range emails {
|
||||
attached[email] = struct{}{}
|
||||
}
|
||||
return attached, err
|
||||
}
|
||||
|
||||
// MergeNodeClientIps merges one node's flat client-IP report, cut to the node's clients.
|
||||
func (s *InboundService) MergeNodeClientIps(nodeID int, rows []model.InboundClientIps) error {
|
||||
attached, err := nodeAttachedEmails(database.GetDB(), nodeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
kept := make([]model.InboundClientIps, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if _, ok := attached[row.ClientEmail]; !ok {
|
||||
continue
|
||||
}
|
||||
var entries []model.ClientIpEntry
|
||||
if json.Unmarshal([]byte(row.Ips), &entries) != nil {
|
||||
continue
|
||||
}
|
||||
clampClientIpEntries(entries, now)
|
||||
row.Ips, _ = model.MarshalClientIpEntries(entries)
|
||||
kept = append(kept, row)
|
||||
}
|
||||
if len(kept) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.MergeInboundClientIps(kept)
|
||||
}
|
||||
|
||||
// clampClientIpEntries caps reported timestamps at now: a future-dated entry would never
|
||||
// age out of the retention window and would keep counting toward the IP limit.
|
||||
func clampClientIpEntries(entries []model.ClientIpEntry, now int64) {
|
||||
for i := range entries {
|
||||
entries[i].Timestamp = min(entries[i].Timestamp, now)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user