CrowdControl.LogRedactor (crowd_control v0.2.0)

Copy Markdown View Source

A :logger primary filter that keeps a %Req.Request{} out of crash reports.

Why this exists

kubereq 0.4.5 changed how an exec/log websocket is established: its Req adapter answers a synthetic 101 and casts the real request to the connection process, which then performs the upgrade. When that upgrade is rejected — a deleted Pod (404), a wrong container name (400), an unsupported subprotocol (403) — or the socket fails, the connection process stops abnormally, and OTP's crash report includes its last message: the cast, carrying the whole %Req.Request{}.

That struct holds the kubeconfig. Measured against a live cluster:

  • with a certificate kubeconfig, :connect_options carries cert: <<48, 130, …>> — client-certificate DER, in a ~2 KB :error line;
  • with a token kubeconfig — the in-cluster ServiceAccount posture, i.e. production — Req's Inspect implementation redacts the authorization header, but options.kubeconfig.current_user["token"] is printed in full.

A failed exec upgrade is a routine event: a Pod that was reaped mid-session produces one. So without this filter a cluster credential reaches the log on an ordinary day, which is precisely the leak CrowdControl.Backend.Kubernetes.API's exception_reason/1 was written to close for the return path.

What it does, and does not do

It redacts fields; it never drops an event. The reason, the process name and the stacktrace all survive, so a crash is still diagnosable — only the request term, the process state and the client info are replaced with :redacted_by_crowd_control.

It is deliberately narrow: it fires only for a crash report that actually carries a %Req.Request{} (or the Kubereq.Connect state), found by a depth-bounded walk. Any other library's crash reports pass through untouched.

Opting out

config :crowd_control, redact_logs: false

Set that if you install your own filter, or if you would rather have the raw reports and accept what they contain.

Summary

Functions

The filter itself. Returns the event, possibly with fields redacted.

Installs the filter, unless config :crowd_control, redact_logs: false.

Functions

filter(event, opts)

@spec filter(:logger.log_event(), term()) :: :logger.log_event()

The filter itself. Returns the event, possibly with fields redacted.

Never returns :stop: suppressing a crash report would trade a credential leak for an invisible failure, which is a worse bargain.

install()

@spec install() :: :ok

Installs the filter, unless config :crowd_control, redact_logs: false.

Idempotent: adding a filter under a name that already exists is a no-op, so a release that restarts the application does not accumulate filters.