Skip to content

Strip PROXY protocol header on the MQTT/secure-socket UDS mirror#877

Open
kriszyp wants to merge 1 commit into
mainfrom
kris/mqtt-uds-proxy-protocol
Open

Strip PROXY protocol header on the MQTT/secure-socket UDS mirror#877
kriszyp wants to merge 1 commit into
mainfrom
kris/mqtt-uds-proxy-protocol

Conversation

@kriszyp
Copy link
Copy Markdown
Member

@kriszyp kriszyp commented May 29, 2026

Summary

The per-worker UDS mirrors (created when tls.unixDomainSockets is enabled) let a fronting proxy (symphony) reach a worker directly and convey the real client address via a PROXY v1 header. The HTTP UDS mirror already strips that header (enableProxyProtocol), but the raw secure-socket UDS mirror (MQTT, replication) did not — so the header was delivered to the protocol parser and corrupted the first packet. For MQTT the first byte 0x50 ('P' of "PROXY ") parses as a PUBREC → Invalid pubrec reason code, connection dropped.

Fix: apply the same enableProxyProtocol wrapper to the secure-socket UDS server in threadServer.js onSocket(), and export it from server/http.ts.

Why

Found while bringing up symphony→instance MQTT over UDS: MQTT CONNECTs never got a CONNACK. (Discovered via load testing; a host-manager-side workaround sets the MQTT route's source header to none, but the proper fix is here so MQTT keeps the real client IP.)

Where to look

  • server/http.tsenableProxyProtocol is now exported (no logic change).
  • server/threads/threadServer.js — one call added in the secure-socket UDS block, mirroring the existing HTTP UDS call.
  • Test: unitTests/server/udsMirror.test.js — strips a PROXY v1 header / passes a non-PROXY chunk through.

🤖 Generated by Claude (Opus 4.7). test:unit:server udsMirror suite passes (15/15).

@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@kriszyp kriszyp requested review from Ethan-Arrowood and heskew May 29, 2026 23:05
@claude

This comment has been minimized.

// Strip the PROXY v1 header a fronting proxy (e.g. symphony) prepends, same as the
// HTTP UDS mirror. Without this the header is fed to the protocol parser (e.g. MQTT),
// corrupting the first packet.
httpComponent.enableProxyProtocol(udsServer);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker: enableProxyProtocol is not exported — this will throw at runtime.

Commit dbe5d734 removed the export keyword from enableProxyProtocol in server/http.ts, but this call in threadServer.js was not removed. At runtime httpComponent.enableProxyProtocol is undefined, so the first time a secure-socket UDS connection arrives this will throw TypeError: httpComponent.enableProxyProtocol is not a function and the connection will be dropped.

Fix: restore the export in server/http.ts:

Suggested change
httpComponent.enableProxyProtocol(udsServer);
httpComponent.enableProxyProtocol(udsServer);

(server/http.ts line 990 needs export function enableProxyProtocol(...).)

The HTTP UDS mirror strips a fronting proxy's PROXY v1 header (enableProxyProtocol)
but the raw secure-socket UDS mirror (MQTT, replication) did not, so the header was
fed to the protocol parser and corrupted the first packet (an MQTT CONNECT's first
byte 0x50 'P' parses as a PUBREC). Apply the same wrapper to the secure-socket UDS
server (and export it), and harden enableProxyProtocol to buffer a PROXY header that
arrives split across data events (or a first chunk shorter than "PROXY ") rather than
forwarding partial header bytes — raw protocols can't recover from a corrupted first
packet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kriszyp kriszyp force-pushed the kris/mqtt-uds-proxy-protocol branch from dbe5d73 to cc8dafe Compare May 30, 2026 02:30
@kriszyp
Copy link
Copy Markdown
Member Author

kriszyp commented May 30, 2026

Force-pushed cc8dafe1f to correct this branch. The earlier review was right — a local git-submodule corruption on my end caused the prior commits to record reverts (lost the export, the buffering, and the tests). The branch now contains the intended change: export function enableProxyProtocol, the enableProxyProtocol(udsServer) call on the secure-socket UDS mirror, the split/partial PROXY-header buffering, and the unit tests (udsMirror suite 18/18 locally).

Note on red CI (pre-existing on main, unrelated to this 3-file change): Integration Tests 1/4 fails in integrationTests/upgrade/4.x-upgrade.test.ts (LMDB→RocksDB migration — RecordEncoder.saveStructures TypeError in copyDb.ts), and Unit Test (Node v26) is the flaky unitTests/apiTests/ws-test.mjs "default subscribe on WS" 5s timeout (v22/v24 pass). Neither touches server/http.ts / threadServer.js. Keeping this draft until those clear.

🤖 Generated by Claude (Opus 4.7).

@kriszyp kriszyp marked this pull request as ready for review May 30, 2026 13:09
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