Repository navigation
feat(ingress): opt-in PROXY protocol for the client address - #130
Merged
kvinwang merged 2 commits intoOct 9, 2026
Merged
Conversation
The gateway relays TCP, so haproxy sees connections from the gateway and the backend from haproxy. The dstack gateway can prepend the client's address with the PROXY protocol (the app's port_policy, `pp`), but the public bind did not read it and the backends did not get it. ACCEPT_PROXY_PROTOCOL=true makes the public bind (both modes) read the header, and SEND_PROXY_PROTOCOL=true passes the client's address to the backends with PROXY protocol v2. Both default to false; the config is otherwise unchanged. In tls-alpn-01 mode the internal hop already carries the PROXY protocol, so the address reaches the TLS frontend too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- README: the port_policy entry must name PORT, not a fixed 443; guest OS 0.5.9+ reports the policy at registration, so public_tcbinfo is only needed on older images; require gateway 0.5.9+; note that anything reaching PORT without the gateway fails, that SEND_PROXY_PROTOCOL covers every ROUTING_MAP backend, and the gateway's inbound_pp_enabled when it sits behind an L4 load balancer. - Warn when SEND_PROXY_PROTOCOL is set without ACCEPT_PROXY_PROTOCOL: the backend would get the gateway's address. - Shorten the haproxy-lib.sh comment, which claimed the client's address is used in either case. - Test backend: read the PROXY header fully instead of a single recv. Signed-off-by: Kevin Wang <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The gateway relays TCP, so HAProxy sees every connection as coming from the gateway, and the backend sees it as coming from HAProxy. Apps behind dstack-ingress therefore can't rate-limit, log, or block by client address; all clients share one address.
The dstack gateway already supports the fix. With the app's
port_policyset to{"port": 443, "pp": true}(Phala Cloud: the "Proxy protocol (pp)" switch), the gateway prepends a PROXY protocol header carrying the client's address. Gateway support landed in 87ce31cd and shipped in gateway 0.5.9. dstack-ingress is the missing hop: its public bind doesn't read the header, and its backends don't receive one.What
Two opt-in switches, both defaulting to
false. With both off, the generated config is unchanged.ACCEPT_PROXY_PROTOCOL=trueaddsaccept-proxyto the public bind:tls_peekin tls-alpn-01 mode;tls_inin dns-01 mode.HAProxy then uses the header's address everywhere, including in logs. A connection without the header is refused, so the gateway's
ppand this switch have to change in the same deployment.SEND_PROXY_PROTOCOL=trueaddssend-proxy-v2to the app backends, for bothTARGET_ENDPOINTandROUTING_MAP. For connections accepted withaccept-proxy, HAProxy advertises the client's address. The ACME responder and the evidence backend never get a header.In tls-alpn-01 mode the hop from
tls_peekto the loopback TLS frontend already usessend-proxy-v2/accept-proxy, so the client's address survives it unchanged.Both values are validated by a new
sanitize_boolean; anything other thantrueorfalsestops startup.README: two env table rows and a "Client address (PROXY protocol)" section, covering the gateway side and the backend side (Caddy's
proxy_protocollistener wrapper, nginx'sproxy_protocolwithset_real_ip_from).Tests
scripts/tests/test_proxy_protocol.sh <image>(new, in the style oftest_x25519mlkem768.sh). It builds the config with the realhaproxy-lib.shhelpers and runs HAProxy, checking that:203.0.113.9reaches the backend as203.0.113.9;All pass against
2.6with this branch'sscripts/copied in.scripts/tests/test_sanitizers.shnow coverssanitize_boolean. All pass.shellcheck -xreports no new findings againstmain, and the new test is clean.Not tested here: a real CVM behind a gateway (tier 3 in TESTING.md). We intend to run that on our deployment, which will be the first to enable
pp.🤖 Generated with Claude Code