fix(api): resolve external client IP in tracing span and OIDC IP capture #362

Merged
Claude-Run merged 1 commit from fix/BUNYIP-310-access-log-client-ip into main 2026-07-11 05:25:51 +02:00
Member

The access loggers already resolve the trusted external client IP via extract_client_ip (BUNYIP-328), but the tracing root spans and the OIDC token handler still recorded a spoofable or proxy address.

TracingLogger::default() built its root span with DefaultRootSpanBuilder, whose http.client_ip comes from actix realip_remote_addr(): that trusts X-Forwarded-For/Forwarded from any peer and ignores TRUSTED_PROXY_CIDR, so spans behind Traefik recorded the proxy/Docker IP (or a spoofed header from a direct client). Add ClientIpRootSpanBuilder, which sets http.client_ip from the shared extract_client_ip and delegates every other field and on_request_end to DefaultRootSpanBuilder. The field is set once at span creation rather than overwritten via Span::record, because the tracing_subscriber fmt field formatter appends on record and would emit http.client_ip twice. Wire the builder on both the primary and OCI HTTP servers.

Remove the OIDC crate's duplicate extract_ip, which read the first X-Forwarded-For entry with no trusted-proxy gate, and call the shared bunyip_domain::middleware::extract_client_ip instead, so OIDC-path IP capture honours TRUSTED_PROXY_CIDR like every other handler.

Document that the same TRUSTED_PROXY_CIDR trust now governs access-log lines and tracing spans, and that the reverse-proxy range must be present for those to carry the external client IP.

With TRUSTED_PROXY_CIDR unset (the dev default) both the span and the OIDC path record the socket peer, identical to prior behaviour, so no config change is required to deploy.

#BUNYIP-310

The access loggers already resolve the trusted external client IP via extract_client_ip (BUNYIP-328), but the tracing root spans and the OIDC token handler still recorded a spoofable or proxy address. TracingLogger::default() built its root span with DefaultRootSpanBuilder, whose http.client_ip comes from actix realip_remote_addr(): that trusts X-Forwarded-For/Forwarded from any peer and ignores TRUSTED_PROXY_CIDR, so spans behind Traefik recorded the proxy/Docker IP (or a spoofed header from a direct client). Add ClientIpRootSpanBuilder, which sets http.client_ip from the shared extract_client_ip and delegates every other field and on_request_end to DefaultRootSpanBuilder. The field is set once at span creation rather than overwritten via Span::record, because the tracing_subscriber fmt field formatter appends on record and would emit http.client_ip twice. Wire the builder on both the primary and OCI HTTP servers. Remove the OIDC crate's duplicate extract_ip, which read the first X-Forwarded-For entry with no trusted-proxy gate, and call the shared bunyip_domain::middleware::extract_client_ip instead, so OIDC-path IP capture honours TRUSTED_PROXY_CIDR like every other handler. Document that the same TRUSTED_PROXY_CIDR trust now governs access-log lines and tracing spans, and that the reverse-proxy range must be present for those to carry the external client IP. With TRUSTED_PROXY_CIDR unset (the dev default) both the span and the OIDC path record the socket peer, identical to prior behaviour, so no config change is required to deploy. #BUNYIP-310
fix(api): resolve external client IP in tracing span and OIDC IP capture
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 7s
Check / fmt + clippy + build + tests (pull_request) Successful in 9m27s
Create release / Create release from merged PR (pull_request) Has been skipped
fcb078301c
The access loggers already resolve the trusted external client IP via extract_client_ip (BUNYIP-328), but the tracing root spans and the OIDC token handler still recorded a spoofable or proxy address.

TracingLogger::default() built its root span with DefaultRootSpanBuilder, whose http.client_ip comes from actix realip_remote_addr(): that trusts X-Forwarded-For/Forwarded from any peer and ignores TRUSTED_PROXY_CIDR, so spans behind Traefik recorded the proxy/Docker IP (or a spoofed header from a direct client). Add ClientIpRootSpanBuilder, which sets http.client_ip from the shared extract_client_ip and delegates every other field and on_request_end to DefaultRootSpanBuilder. The field is set once at span creation rather than overwritten via Span::record, because the tracing_subscriber fmt field formatter appends on record and would emit http.client_ip twice. Wire the builder on both the primary and OCI HTTP servers.

Remove the OIDC crate's duplicate extract_ip, which read the first X-Forwarded-For entry with no trusted-proxy gate, and call the shared bunyip_domain::middleware::extract_client_ip instead, so OIDC-path IP capture honours TRUSTED_PROXY_CIDR like every other handler.

Document that the same TRUSTED_PROXY_CIDR trust now governs access-log lines and tracing spans, and that the reverse-proxy range must be present for those to carry the external client IP.

With TRUSTED_PROXY_CIDR unset (the dev default) both the span and the OIDC path record the socket peer, identical to prior behaviour, so no config change is required to deploy.

#BUNYIP-310
Claude-Run deleted branch fix/BUNYIP-310-access-log-client-ip 2026-07-11 05:25:52 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!362
No description provided.