Skip to content

Keep board IDs out of server logs, show real client IPs, no traceback for early disconnects - #8

Merged
k3mpaxl merged 1 commit into
mainfrom
fix/websocket-logs
Oct 7, 2026
Merged

k3mpaxl merged 1 commit into
mainfrom
fix/websocket-logs

Conversation

@k3mpaxl

@k3mpaxl k3mpaxl commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Why

The Azure App Service log of v0.5.3 showed:

  1. Board IDs in the log: INFO: 169.254.129.1:43805 - "WebSocket /ws/boards/<board UUID>" [accepted] for every connection. The board ID is the board's access key. --no-access-log does not stop these lines; Uvicorn logs WebSocket connections through uvicorn.error. App Service keeps container logs (log stream, storage, Log Analytics).
  2. A traceback after a restart: a browser closed its connection before sending hello (code 1005), the handler closed the dead connection again, Uvicorn raised ClientDisconnected → "Exception in ASGI application".
  3. The proxy's IP instead of the client's (169.254.129.1).

What changed

  • HideBoardIds logging filter on uvicorn.error and uvicorn.access: /boards/<uuid> becomes /boards/<board> in message and arguments.
  • refuse() closes refused connections and ignores a connection that is already gone (RuntimeError, OSError = uvicorn's ClientDisconnected, WebSocketDisconnect). A browser that leaves before hello ends the handler without closing again. Replacing a duplicated tab's old connection (4001) ignores a gone connection the same way.
  • Real client IPs: the Docker image sets FORWARDED_ALLOW_IPS=127.0.0.1,::1,169.254.0.0/16.
    • Uvicorn reads X-Forwarded-For only from those addresses; App Service front ends use the link-local range, which no client can have from outside, so nobody can fake an IP in the log.
    • Uvicorn 0.54 parses App Service's IP:port format.
    • Behind another reverse proxy, set FORWARDED_ALLOW_IPS to its address (README).
  • README security notes updated.

For reviewers

  • tests/test_logs.py starts Uvicorn with INFO logs and the image's proxy setting, connects with X-Forwarded-For: 203.0.113.9:51515, lets one browser close cleanly, one drop the TCP connection and one say hello. It asserts: no board ID in the log, "WebSocket /ws/boards/<board>" [accepted], the real IP, no traceback.
  • Against the old code it fails with the board ID and with exactly the traceback from App Service (WebSocketDisconnect: (1005, None) → ClientDisconnected, line 837).
  • All backend tests pass.
  • Logs written before this change still contain board IDs: restrict who can read them or delete them, and move sensitive boards to a new ID (JSON export, import into a new board).

🤖 Generated with Claude Code

… for early disconnects

The App Service log of v0.5.3 showed three things:

- Every WebSocket connection was logged with its path, /ws/boards/<id>, and
  the board ID is the board's access key. Uvicorn logs these lines through
  uvicorn.error, so --no-access-log does not stop them. A logging filter now
  replaces the ID by <board> in all Uvicorn records (and in access log lines,
  should someone switch them on).
- A browser that leaves before saying hello (a reload, or several tabs
  reconnecting after a restart) made the handler close the dead connection
  again: uvicorn raised ClientDisconnected and logged "Exception in ASGI
  application". The handler now simply ends; closing a gone connection,
  also when a duplicated tab replaces an old one, is no error.
- The client IP was the proxy's (169.254.x.x). The image now trusts
  X-Forwarded-For from loopback and link-local proxies only
  (FORWARDED_ALLOW_IPS=127.0.0.1,::1,169.254.0.0/16: Azure App Service front
  ends), so logs show the real client IP and a client cannot fake it.

tests/test_logs.py starts the server with INFO logs, connects through a
"proxy", lets one browser close cleanly and one drop the connection: it fails
on the old code with the board ID and the exact traceback from the log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@k3mpaxl
k3mpaxl merged commit 8365a38 into main Oct 7, 2026
4 checks passed
@k3mpaxl k3mpaxl changed the title Keep board IDs out of server logs, show real client IPs, no traceback for early disconnects Relay on App Service: no board IDs in logs, real client IPs, heartbeat, per-browser queues, send backpressure Oct 7, 2026
@k3mpaxl k3mpaxl changed the title Relay on App Service: no board IDs in logs, real client IPs, heartbeat, per-browser queues, send backpressure Keep board IDs out of server logs, show real client IPs, no traceback for early disconnects Oct 7, 2026
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.

1 participant