fix(logging): attribute request logs to the external client IP #334

Merged
Claude-Run merged 1 commit from fix/BUNYIP-328-log-external-client-ip into main 2026-07-04 03:02:47 +02:00
Member

The actix access Logger::default() logs %a, the immediate socket peer. Behind Traefik that is the reverse proxy's internal IP, so every request line was attributed to the proxy rather than the client that made the request, making errors hard to trace back to a specific caller.

Add access_log::access_logger(), a Logger whose client field is resolved by extract_client_ip, which honours the proxy's X-Forwarded-For (then X-Real-IP) only when the socket peer is a configured trusted proxy (TRUSTED_PROXY_CIDR). This is the same trusted resolution the rate-limit records already use, so request-log lines and rate-limit entries now point at the same external client. Wire it into both the primary app and the OCI registry server in place of Logger::default().

Factor the trusted-proxy logic out of extract_client_ip into a pure resolve_client_ip(peer, xff, real_ip, trusted_proxies) so it is unit-testable without an actix request, and add tests proving a request forwarded by a trusted proxy resolves to the external client in XFF (not the proxy/internal IP) while a forged XFF from an untrusted peer is ignored.

#BUNYIP-328

The actix access `Logger::default()` logs `%a`, the immediate socket peer. Behind Traefik that is the reverse proxy's internal IP, so every request line was attributed to the proxy rather than the client that made the request, making errors hard to trace back to a specific caller. Add `access_log::access_logger()`, a `Logger` whose client field is resolved by `extract_client_ip`, which honours the proxy's `X-Forwarded-For` (then `X-Real-IP`) only when the socket peer is a configured trusted proxy (`TRUSTED_PROXY_CIDR`). This is the same trusted resolution the rate-limit records already use, so request-log lines and rate-limit entries now point at the same external client. Wire it into both the primary app and the OCI registry server in place of `Logger::default()`. Factor the trusted-proxy logic out of `extract_client_ip` into a pure `resolve_client_ip(peer, xff, real_ip, trusted_proxies)` so it is unit-testable without an actix request, and add tests proving a request forwarded by a trusted proxy resolves to the external client in XFF (not the proxy/internal IP) while a forged XFF from an untrusted peer is ignored. #BUNYIP-328
fix(logging): attribute request logs to the external client IP
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 28s
Check / fmt + clippy + build + tests (pull_request) Successful in 9m23s
Create release / Create release from merged PR (pull_request) Has been skipped
82f01bbfd5
The actix access `Logger::default()` logs `%a`, the immediate socket peer. Behind Traefik that is the reverse proxy's internal IP, so every request line was attributed to the proxy rather than the client that made the request, making errors hard to trace back to a specific caller.

Add `access_log::access_logger()`, a `Logger` whose client field is resolved by `extract_client_ip`, which honours the proxy's `X-Forwarded-For` (then `X-Real-IP`) only when the socket peer is a configured trusted proxy (`TRUSTED_PROXY_CIDR`). This is the same trusted resolution the rate-limit records already use, so request-log lines and rate-limit entries now point at the same external client. Wire it into both the primary app and the OCI registry server in place of `Logger::default()`.

Factor the trusted-proxy logic out of `extract_client_ip` into a pure `resolve_client_ip(peer, xff, real_ip, trusted_proxies)` so it is unit-testable without an actix request, and add tests proving a request forwarded by a trusted proxy resolves to the external client in XFF (not the proxy/internal IP) while a forged XFF from an untrusted peer is ignored.

#BUNYIP-328
Claude-Run deleted branch fix/BUNYIP-328-log-external-client-ip 2026-07-04 03:02:47 +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!334
No description provided.