Since commit 23e43e700d (MEN-9262, shipped in 5.0.x and 5.1.0, still on master), the Mender client sends the wrong Host header on the proxy CONNECT request. It names the proxy instead of the tunnel destination.
| Request-target | Host header |
|
|---|---|---|
| What the client sends today | server.example.com:443 |
10.0.0.1:3128 (the proxy) |
| What it must send (RFC 9110 §9.3.6) | server.example.com:443 |
server.example.com:443 |
Captured with tcpdump on the plaintext leg between the device and the proxy:
CONNECT server.example.com:443 HTTP/1.1
HOST: 10.0.0.1:3128
User-Agent: Mender/5.1.0
RFC 9110 §9.3.6 requires the Host header of a CONNECT request to be identical to the request-target. curl and Go’s net/http on the same device, through the same proxy, to the same destination send Host: server.example.com:443 and succeed.
Cause. In src/common/http/platform/beast/http.cpp, Client::HandleProxySetup() overwrites request_->address_.host with the proxy address and then, three lines later, builds the Host header from that same field via CreateHOSTAddress(request_). The request-target (address_.path) is computed correctly from the original request; only the header reads the wrong field. Client::AsyncCall() has the same ordering problem for plain http:// requests via HTTP_PROXY: it sets Host after HandleProxySetup() has rewritten the address.
Impact. Squid, nginx, Envoy and tinyproxy route CONNECT on the request-target and ignore Host, so this went unnoticed. One of our customers runs a corporate forward proxy that enforces destination policy on the Host header. It rejects every CONNECT with 403, so devices can neither authenticate nor update:
Proxy error: POST https://<server>/api/devices/v1/authentication/auth_requests: Proxy returned unexpected response: 403 Forbidden
Isolating the header with curl against that proxy:
curl -x http://10.0.0.1:3128 https://server.example.com/ -> 200 Connection established
curl -x http://10.0.0.1:3128 --proxy-header "Host: 10.0.0.1" https://server.example.com/ -> 403 Forbidden
There is no configuration workaround. The value is hard-coded.
Reproduce without a strict proxy:
nc -l -p 8888 # terminal 1: prints what it receives
HTTPS_PROXY=http://127.0.0.1:8888 mender-auth bootstrap # terminal 2
Terminal 1 shows CONNECT <server>:443 HTTP/1.1 followed by HOST: 127.0.0.1:8888. Expected: HOST: <server>:443.
Fix. I have opened fix: send the tunnel destination in the Host header of proxy CONNECT requests by meghasuvarna-voxelai · Pull Request #2021 · mendersoftware/mender · GitHub. It sets the CONNECT Host header from the request-target before the address is rewritten, moves the regular Host header before proxy setup, and adds two regression tests using a recording proxy built on http::Server (the existing tinyproxy-based tests cannot detect this because tinyproxy ignores Host). Both new tests fail on master and pass with the change. Full http_proxy_test (28) and http_test (47) pass locally.
Could someone open a MEN ticket for this so I can reference it in the commit? Happy to adjust anything for review.