Files
egress-proxies-operator/docs/plans-executions/2026-08-11-1838-gcp-http-wire-logging-v5.md
Jan Novak 420c3509b0 Wire logging: drop auth token-exchange records, elide huge payload fields
The option.WithLogger logger also reaches cloud.google.com/go/auth,
which logged its token exchange at Debug — JWT assertion and bearer
token included. wireLogger now allowlists only the compute client's
api request/response records at Debug (fail-closed for future SDK
additions); Warn/Error pass through. String fields over 1KiB (e.g.
Shielded-VM UEFI dbx blobs) are elided recursively by default; the new
--gcp-wire-log-full-payloads flag restores verbatim payloads.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 19:02:28 +02:00

3.1 KiB
Raw Blame History

Execution: GCP HTTP wire logging at V(5)

Plan: docs/plans/2026-08-11-1838-gcp-http-wire-logging-v5.md

  • Step 1 — wireLogger + option.WithLogger wiring in internal/provider/gcp/gcp.go
  • Step 2 — Tests (TestWireLogger_gatesAtV5, TestWireLogger_infoLandsAtV1)
  • Step 3 — Live verification at --zap-log-level=5 (user, on cluster)
  • Step 3b — Post-verification fix: drop auth records, elide huge fields
  • Step 4 — CHANGELOG entry (after live confirmation of 3b; batch with the two earlier pending entries: GCP V-logging, version stamp)

Steps 12

Went exactly as planned — the whole feature is ~10 lines of production code because both halves already existed: the compute SDK logs full HTTP request/response records at slog Debug to an injectable logger, and logr.ToSlogHandler does the slog→logr bridging. The only real design content is the level shift (base.V(1) + slog-Debug's +4 = V(5)) and the startup warning line when V(5) is active (raw payloads include cloud-init user-data, which the curated V(2) logging deliberately hides).

Worth noting for future readers:

  • option.WithLogger disables GOOGLE_SDK_GO_LOGGING_LEVEL for this client (documented SDK precedence) — --zap-log-level is now the only knob for GCP wire logs.
  • The V(5) check in New runs once at startup; that is sound because the zap level is fixed by flags at process start.
  • Added TestWireLogger_infoLandsAtV1 beyond the plan's table — it pins the shift arithmetic from the other side (slog Info → V(1)), so a future logr mapping change would fail loudly.

Verified with:

go test -race ./internal/provider/gcp/
go build ./... && go test ./...

Step 3b — what live verification exposed, and the fix

Live V(5) output revealed two problems the plan missed:

  1. Security: the injected logger propagates into cloud.google.com/go/auth, which logs its own token exchange (auth.go:571/576) — signed JWT assertion in the request, full bearer access token in the response. The plan's "auth token is safe" analysis only covered the compute client's request headers, not the auth library's own records. Fix: wireLogger now wraps the handler in a filter that drops every Debug record except the compute client's "api request"/"api response" (allowlist, so future SDK additions fail closed); Warn/Error still pass through.
  2. Readability: GCP responses embed multi-KB blobs (Shielded-VM UEFI dbx databases) that swamp the line. Fix: string fields >1KiB are elided to [elided N bytes] by default, recursively through payload maps/arrays. Opt-out via new manager flag --gcp-wire-log-full-payloads (threaded through a constructor closure in cmd/main.gogcp.NewWithWireOptions; the registry.Constructor signature stays unchanged). Chosen by the user: elision on by default, verbatim available on demand. Auth records are dropped in both modes.

The filter/elision logic lives in internal/provider/gcp/wirelog.go with tests covering: auth-record drop (both modes), elision marker + small-field preservation, verbatim mode, and the original V(5) gating.