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:
MHSanaei
2026-10-31 09:19:51 +00:00
parent 08482154c2
commit 5a1ea42785
5 changed files with 172 additions and 26 deletions
+5
View File
@@ -140,8 +140,13 @@ const (
maxIdleNodeConnsPerHost = 8
)
// nodeTLSHandshakeTimeout ends a handshake the node never answers: the transport keeps
// dialing for its pool after the request gives up, so without it the socket lives forever.
var nodeTLSHandshakeTimeout = remoteHTTPTimeout
func newNodeTransport(tlsCfg *tls.Config) *http.Transport {
return &http.Transport{
TLSHandshakeTimeout: nodeTLSHandshakeTimeout,
MaxIdleConns: maxIdleNodeConns,
MaxIdleConnsPerHost: maxIdleNodeConnsPerHost,
IdleConnTimeout: 60 * time.Second,