[exporter/opensearchexporter] Validate attribute values in dynamic index names#49362
Open
kylehounslow wants to merge 1 commit into
Open
Conversation
…dex names
Attribute values substituted into a %{placeholder} index name are now
restricted to [a-z0-9_.-] and may not start with "." or contain "..". A value
that fails the check is skipped in favor of the next attribute in the
precedence order, then the configured fallback, matching existing missing-key
behavior. This prevents attacker-controlled attribute values from redirecting
writes to system indices (e.g. .kibana) or other indices via path traversal.
The operator-configured fallback is trusted and not validated.
The allowlist follows OpenSearch's documented index naming rules (lowercase
only; no spaces, commas, or the characters :"*+/\|?#><).
Fixes open-telemetry#49225
Assisted-by: Claude Opus 4.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Validates attribute values before they are substituted into a
%{placeholder}index name in the dynamic index resolver. Values are restricted to[a-z0-9_.-]and may not start with.or contain... A value that fails the check is skipped in favor of the next attribute in the precedence order (item, scope, resource), then the operator-configured fallback, matching the existing missing-key behavior. The configured fallback is operator-controlled and is not validated.The allowlist rejects the characters OpenSearch documents as forbidden in index names (space,
,, and:"*+/\|?#><) plus uppercase, and additionally rejects leading-dot and..so a value cannot resolve to a system index (e.g..kibana).The leading-dot escalation requires the placeholder to lead the index name (e.g.
traces_index: "%{tenant}"); a static prefix likelogs-%{tenant}already keeps the result out of the system namespace.What this does not cover. A well-formed but unauthorized value still resolves. With
traces_index: "%{tenant}", an attacker settingtenant=team-bstill produces theteam-bindex, becauseteam-bis a valid index segment. Character validation can't distinguish an authorized tenant from an unauthorized one; cross-tenant isolation needs tenant allowlisting or an immutable prefix.Link to tracking issue
Testing
go test ./...inexporter/opensearchexporter/.index_resolver_test.gocases cover path traversal (../../system),.., leading dot, forbidden characters (/,\, space), uppercase, and fall-through from an invalid higher-precedence attribute to a valid lower-precedence one.Full repro steps
Two OpenSearch nodes (security plugin off for plain HTTP):
Build a collector with this branch's exporter.
make otelcontribcolfrom the repo root is the official path (itsgenotelcontribcolstep adds a localreplacefor every module). For faster single-exporter iteration, a minimal module wiring the OTLP receiver, debug exporter, and this exporter with areplaceto the local tree works too.Collector config.
traces_index: "%{tenant}"makes the index name fully attacker-controlled, which is what exposes the leading-dot system-index case:Send a span carrying the
tenantvalue under test (swap the value per case):{ "resourceSpans": [ { "resource": { "attributes": [ { "key": "service.name", "value": { "stringValue": "victim" } }, { "key": "tenant", "value": { "stringValue": "../../system" } } ] }, "scopeSpans": [ { "scope": { "name": "repro" }, "spans": [ { "traceId": "5b8efff798038103d269b633813fc60c", "spanId": "eee19b7ec3c1b174", "name": "span", "kind": 1, "startTimeUnixNano": "1700000000000000000", "endTimeUnixNano": "1700000000000000010" } ] } ] } ] }Send the span (swap the
tenantvalue per case), then list indices:Sending
team-a,team-b,.kibana_x,../../systemin turn, without the fix:../../systemreturns HTTP 500; the exporter logsinvalid_index_name_exception(the/is a forbidden index char) and drops the span. The same four with the fix:Documentation
.chloggen/opensearchexporter-sanitize-index-placeholders.yamladded.