Skip to content

feat: Propagate Request Headers#19572

Open
mshahid6 wants to merge 1 commit into
apache:masterfrom
mshahid6:propogate-header
Open

feat: Propagate Request Headers#19572
mshahid6 wants to merge 1 commit into
apache:masterfrom
mshahid6:propogate-header

Conversation

@mshahid6

@mshahid6 mshahid6 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a configurable mechanism to capture inbound HTTP headers and propagate them through Druid:

  1. Inbound capture — a servlet filter (RequestHeaderContextFilter) reads the headers listed in druid.audit.requestHeaders.headerToContextKey, binds the values to a request thread-local, and clears them in a finally block.

  2. Injection into Query.getContext() — QueryLifecycle.initialize() strips any user-supplied values for the configured reserved context keys (anti-spoof), then injects the filter-captured values. Druid's existing native sub-query context propagation flows the values to historicals/peons for free.

  3. Wire propagation on broker → historical RPCs — DirectDruidClient reads from the current query context and re-attaches the configured headers onto each outbound Request, so the receiving node's filter captures them just as if a client had sent them. End-to-end propagation without trusting the JSON body context.

  4. Typed RequestInfo.traceId audit column — AuthorizationUtils.buildRequestInfo reads the value of the "traceId" context key (canonical name) from the filter thread-local, populating a new typed field on RequestInfo. Every AuditEntry built via AuthorizationUtils automatically carries the trace ID, with @JsonInclude(NON_NULL) so existing audit JSON is byte-identical when no trace header is sent.

Config

Default: X-Druid-Trace-Id → traceId (enabled out of the box)
druid.audit.requestHeaders.headerToContextKey={"X-Druid-Trace-Id": "traceId"}

Add additional headers

druid.audit.requestHeaders.headerToContextKey={
  "X-Druid-Trace-Id": "traceId",
  "X-Tenant-Id": "tenantId"
}

Explicitly disable (empty map)
druid.audit.requestHeaders.headerToContextKey={}

Mapping any header to a Druid reserved context key (queryId, subQueryId, sqlQueryId) throws at config-bind time so a client can't overwrite the server-assigned queryId.

Release note

Druid now supports propagating configured inbound HTTP headers (default X-Druid-Trace-Id) through the query context and into audit events. Configure via druid.audit.requestHeaders.headerToContextKey. Mapping a header to a Druid reserved context key (queryId, subQueryId, sqlQueryId) is rejected at startup. A new typed traceId field on RequestInfo lands in the audit table for correlation with distributed-trace systems.

Key changed/added classes

  • org.apache.druid.audit.AuditManager — X_DRUID_TRACE_ID constant
  • org.apache.druid.audit.RequestInfo — traceId typed field
  • org.apache.druid.audit.RequestHeaderContextConfig (new)
  • org.apache.druid.server.audit.RequestHeaderContext (new)
  • org.apache.druid.server.audit.RequestHeaderContextFilter (new)
  • org.apache.druid.server.audit.RequestHeaderContextFilterHolder (new)
  • org.apache.druid.server.QueryLifecycle — strip + inject in initialize()
  • org.apache.druid.server.security.AuthorizationUtils — buildRequestInfo
  • org.apache.druid.client.DirectDruidClient — outbound header attachment
  • org.apache.druid.server.initialization.jetty.JettyServerModule — config + filter holder binding

This PR has:

  • been self-reviewed.
  • using the concurrency checklist (Remove this item if the PR doesn't have any relation to concurrency.)
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@mshahid6 mshahid6 changed the title Propagate Request Headers feat: Propagate Request Headers Jun 9, 2026
* no header propagation is performed (the feature is effectively disabled).
*/
@JsonProperty
public Map<String, String> getHeaderToContextKey()
responseHandler,
Duration.millis(timeLeft)
);
final Request outbound = new Request(HttpMethod.POST, new URL(url))

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 0
P2 1
P3 0
Total 1

Reviewed 33 files, including all 22 changed files.

I found one issue: configured request-header targets can bypass existing query-context authorization when mapped to operational context keys such as priority or lane.


This is an automated review by Codex GPT-5.5

}
final Map<String, String> captured = RequestHeaderContext.current();
if (!captured.isEmpty()) {
finalContext.putAll(captured);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Header-mapped context bypasses context authorization

Captured request headers are merged into the final query context here, but these keys were not included in the user context keys that AuthConfig.contextKeysToAuthorize checks. Since RequestHeaderContextConfig only rejects queryId/subQueryId/sqlQueryId, an operator can map a client-controlled header to existing operational context keys such as priority, lane, or cache flags; clients can then set those values via headers without the QUERY_CONTEXT WRITE authorization required for the same keys in the query body. Please either reject mappings to existing Druid query-context keys or include captured header target keys in context authorization unless they come from a trusted internal source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants