Context
Follow-up to the tls.server_name OpenSSL fixes in #25881 and #25899.
To keep tls.server_name from being applied to the TLS connection to an HTTPS forward proxy (which must verify the proxy's own certificate against the proxy host), build_https_connector skips the server_name override when the per-connection callback's target authority matches a configured https:// proxy authority (ProxyAuthorities::matches, src/http.rs).
Problem
The connector callback only sees the target host:port of the connection it is dialing. A connection to a given authority can be either:
- a legitimate TLS connection to the proxy (override should be skipped), or
- a direct connection to a destination that happens to share the exact same
host:port as the proxy — e.g. a no_proxy bypass, or an https:// destination not routed through a proxy that only intercepts a different scheme (override should be applied).
Both produce identical callback inputs but require opposite decisions, so when a direct destination's authority collides exactly with a configured https:// proxy authority, server_name is skipped and certificate verification falls back to the URL host — the very hostname mismatch this option is meant to avoid.
This is a pathological configuration (a data endpoint sharing the exact host:port with the forward proxy), but the behavior is silent.
Why it isn't easily fixed
- The ambiguity is fundamental at the connector-callback layer:
hyper-proxy reuses one inner connector for both proxy and direct (no_proxy) connections, and the destination URI is not known until request time.
- Bringing
no_proxy into the check does not disambiguate: the same authority can serve as both a proxy target and a no_proxy destination in the same config.
- Early failure at init is not feasible centrally:
build_proxy_connector / HttpClient::new do not know destination URIs at construction (they arrive per-request). The destination authority is only known inside each individual sink/source, so any early-fail check would have to be added per-component and would need to re-implement no_proxy / scheme-based routing to decide whether a given endpoint would be dialed directly — scattered, incomplete (dynamic/multiple endpoints), and not worth it for a pathological config.
Possible directions
- Narrow the tracked authorities to
proxy.https only (an https:// URL in the proxy.http field only tunnels plaintext http destinations, for which server_name is meaningless), removing one concrete slice of the problem.
- Consider architectural options that make proxy vs. direct connections distinguishable at the TLS layer (e.g. separate connectors, or threading per-request intent into the callback).
- Optionally, per-sink init-time validation/warning when a sink's endpoint authority collides with a configured proxy authority while
tls.server_name is set.
References
Context
Follow-up to the
tls.server_nameOpenSSL fixes in #25881 and #25899.To keep
tls.server_namefrom being applied to the TLS connection to an HTTPS forward proxy (which must verify the proxy's own certificate against the proxy host),build_https_connectorskips theserver_nameoverride when the per-connection callback's target authority matches a configuredhttps://proxy authority (ProxyAuthorities::matches,src/http.rs).Problem
The connector callback only sees the target
host:portof the connection it is dialing. A connection to a given authority can be either:host:portas the proxy — e.g. ano_proxybypass, or anhttps://destination not routed through a proxy that only intercepts a different scheme (override should be applied).Both produce identical callback inputs but require opposite decisions, so when a direct destination's authority collides exactly with a configured
https://proxy authority,server_nameis skipped and certificate verification falls back to the URL host — the very hostname mismatch this option is meant to avoid.This is a pathological configuration (a data endpoint sharing the exact
host:portwith the forward proxy), but the behavior is silent.Why it isn't easily fixed
hyper-proxyreuses one inner connector for both proxy and direct (no_proxy) connections, and the destination URI is not known until request time.no_proxyinto the check does not disambiguate: the same authority can serve as both a proxy target and ano_proxydestination in the same config.build_proxy_connector/HttpClient::newdo not know destination URIs at construction (they arrive per-request). The destination authority is only known inside each individual sink/source, so any early-fail check would have to be added per-component and would need to re-implementno_proxy/ scheme-based routing to decide whether a given endpoint would be dialed directly — scattered, incomplete (dynamic/multiple endpoints), and not worth it for a pathological config.Possible directions
proxy.httpsonly (anhttps://URL in theproxy.httpfield only tunnels plaintext http destinations, for whichserver_nameis meaningless), removing one concrete slice of the problem.tls.server_nameis set.References
tls.server_name)tls.server_nameOpenSSL verification fix)src/http.rs:build_https_connector,ProxyAuthorities,tls_proxy_authority