mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
fix(node): bound what a node's answers can cost the master
Node responses are untrusted input on the master, and a few shapes still
cost it without bound under the 64 MiB body cap:
- a success=false message became an error (logged, stored as lastError)
in full; it is now cut to the 8 KiB the non-200 diagnostic already uses;
- each "{}" of a reported list decodes into a whole struct, so lists are
now decoded entry by entry with per-endpoint caps (1024 descendants,
65536 inbounds and host groups, 1M client-IP rows), and the GUID-keyed
trees and maps are capped at 1M values;
- a TLS handshake the node never answers kept a socket and goroutines
alive forever, since the transport keeps dialing for its pool after the
request gives up; node transports now give up after 10 s.
This commit is contained in:
@@ -327,6 +327,7 @@ func (r *Remote) do(ctx context.Context, method, path string, body any) (*envelo
|
||||
return nil, fmt.Errorf("decode envelope: %w", err)
|
||||
}
|
||||
if !env.Success {
|
||||
env.Msg = truncateNodeMessage(env.Msg)
|
||||
return &env, &remoteAPIError{msg: env.Msg}
|
||||
}
|
||||
return &env, nil
|
||||
@@ -422,8 +423,8 @@ func (r *Remote) ListInboundOptions(ctx context.Context) ([]RemoteInboundOption,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []RemoteInboundOption
|
||||
if err := json.Unmarshal(env.Obj, &list); err != nil {
|
||||
list, err := decodeCappedList[RemoteInboundOption](env.Obj, maxNodeInbounds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode inbound list: %w", err)
|
||||
}
|
||||
return list, nil
|
||||
@@ -434,11 +435,11 @@ func (r *Remote) refreshRemoteIDs(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var list []struct {
|
||||
list, err := decodeCappedList[struct {
|
||||
Id int `json:"id"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
if err := json.Unmarshal(env.Obj, &list); err != nil {
|
||||
}](env.Obj, maxNodeInbounds)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode inbound list: %w", err)
|
||||
}
|
||||
next := make(map[string]int, len(list))
|
||||
@@ -750,11 +751,9 @@ func (r *Remote) GetDescendants(ctx context.Context) ([]model.NodeSummary, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []model.NodeSummary
|
||||
if len(env.Obj) > 0 {
|
||||
if err := json.Unmarshal(env.Obj, &out); err != nil {
|
||||
return nil, fmt.Errorf("decode descendants: %w", err)
|
||||
}
|
||||
out, err := decodeCappedList[model.NodeSummary](env.Obj, maxNodeDescendants)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode descendants: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -813,11 +812,9 @@ func (r *Remote) FetchHostGroups(ctx context.Context) ([]*entity.HostGroup, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var groups []*entity.HostGroup
|
||||
if len(env.Obj) > 0 {
|
||||
if err := json.Unmarshal(env.Obj, &groups); err != nil {
|
||||
return nil, fmt.Errorf("decode host groups: %w", err)
|
||||
}
|
||||
groups, err := decodeCappedList[*entity.HostGroup](env.Obj, maxNodeHostGroups)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode host groups: %w", err)
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
@@ -829,7 +826,7 @@ func (r *Remote) FetchTrafficSnapshot(ctx context.Context) (*TrafficSnapshot, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(envList.Obj, &snap.Inbounds); err != nil {
|
||||
if snap.Inbounds, err = decodeCappedList[*model.Inbound](envList.Obj, maxNodeInbounds); err != nil {
|
||||
return nil, fmt.Errorf("decode inbound list: %w", err)
|
||||
}
|
||||
|
||||
@@ -837,14 +834,14 @@ func (r *Remote) FetchTrafficSnapshot(ctx context.Context) (*TrafficSnapshot, er
|
||||
// node is an old build without the per-GUID endpoint (#4983).
|
||||
envTree, err := r.do(ctx, http.MethodPost, "panel/api/clients/onlinesByGuid", nil)
|
||||
if err == nil && len(envTree.Obj) > 0 {
|
||||
_ = json.Unmarshal(envTree.Obj, &snap.OnlineTree)
|
||||
_ = decodeCappedValues(envTree.Obj, &snap.OnlineTree, maxNodeMapValues)
|
||||
}
|
||||
if len(snap.OnlineTree) == 0 {
|
||||
envOnlines, err := r.do(ctx, http.MethodPost, "panel/api/clients/onlines", nil)
|
||||
if err != nil {
|
||||
logger.Warning("remote", r.node.Name, "onlines fetch failed:", err)
|
||||
} else if len(envOnlines.Obj) > 0 {
|
||||
_ = json.Unmarshal(envOnlines.Obj, &snap.OnlineEmails)
|
||||
_ = decodeCappedValues(envOnlines.Obj, &snap.OnlineEmails, maxNodeMapValues)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,14 +849,14 @@ func (r *Remote) FetchTrafficSnapshot(ctx context.Context) (*TrafficSnapshot, er
|
||||
if err != nil {
|
||||
logger.Warning("remote", r.node.Name, "lastOnline fetch failed:", err)
|
||||
} else if len(envLastOnline.Obj) > 0 {
|
||||
_ = json.Unmarshal(envLastOnline.Obj, &snap.LastOnlineMap)
|
||||
_ = decodeCappedValues(envLastOnline.Obj, &snap.LastOnlineMap, maxNodeMapValues)
|
||||
}
|
||||
|
||||
envActiveInbounds, err := r.do(ctx, http.MethodPost, "panel/api/clients/activeInbounds", nil)
|
||||
if err != nil {
|
||||
logger.Debugf("remote %s active inbounds fetch failed: %v", r.node.Name, err)
|
||||
} else if len(envActiveInbounds.Obj) > 0 {
|
||||
_ = json.Unmarshal(envActiveInbounds.Obj, &snap.ActiveInboundTree)
|
||||
_ = decodeCappedValues(envActiveInbounds.Obj, &snap.ActiveInboundTree, maxNodeMapValues)
|
||||
}
|
||||
|
||||
return snap, nil
|
||||
@@ -989,11 +986,9 @@ func (r *Remote) FetchAllClientIps(ctx context.Context) ([]model.InboundClientIp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ips []model.InboundClientIps
|
||||
if len(env.Obj) > 0 {
|
||||
if err := json.Unmarshal(env.Obj, &ips); err != nil {
|
||||
return nil, fmt.Errorf("decode client ips: %w", err)
|
||||
}
|
||||
ips, err := decodeCappedList[model.InboundClientIps](env.Obj, maxNodeClientIPs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode client ips: %w", err)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
@@ -1014,7 +1009,7 @@ func (r *Remote) FetchClientIpsByGuid(ctx context.Context) (map[string]map[strin
|
||||
}
|
||||
out := map[string]map[string][]model.ClientIpEntry{}
|
||||
if len(env.Obj) > 0 {
|
||||
if err := json.Unmarshal(env.Obj, &out); err != nil {
|
||||
if err := decodeCappedValues(env.Obj, &out, maxNodeMapValues); err != nil {
|
||||
return nil, fmt.Errorf("decode client ips by guid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user