Hello,

On Fri, Feb 24, 2023 at 02:19:30PM +0100, Óscar Frías Barranco wrote:
> Hello
> 
> I am using haproxy 2.4.18 with a frontend configured with alpn h2,http/1.1
> 
> The problem that I am facing is that if I add "proto h2" to the backends,
> when a remote client connects to the frontend using HTTP 1.1, the request
> is sent to the backend server without copying the "Host" header to the
> ":authority" pseudo-header.

This is normal and expected, it complies with the spec that is pretty
strict about this:

   https://www.rfc-editor.org/rfc/rfc7540#section-8.1.2.3

      The ":authority" pseudo-header field includes the authority
      portion of the target URI ([RFC3986], Section 3.2).  The authority
      MUST NOT include the deprecated "userinfo" subcomponent for "http"
      or "https" schemed URIs.

      To ensure that the HTTP/1.1 request line can be reproduced
      accurately, this pseudo-header field MUST be omitted when
      translating from an HTTP/1.1 request that has a request target in
      origin or asterisk form (see [RFC7230], Section 5.3).

and was further refined in the updated spec:

   https://www.rfc-editor.org/rfc/rfc9113#name-request-pseudo-header-field

   The ":authority" pseudo-header field conveys the authority portion (Section
   3.2 of [RFC3986]) of the target URI (Section 7.1 of [HTTP]). 
   (...)
   An intermediary that forwards a request over HTTP/2 MUST construct an
   ":authority" pseudo-header field using the authority information from the
   control data of the original request, unless the original request's target 
URI
   does not contain authority information (in which case it MUST NOT generate
   ":authority"). Note that the Host header field is not the sole source of this
   information; see Section 7.2 of [HTTP].

If the HTTP/1 request contains an authority part, it will appear there.
Otherwise only the host is used. In summary, the H2 request looks very
close to the H1 one: Host conveys Host, authority conveys authority.

> This causes the application server (Jetty in my case) to report the backend
> server numeric IP address as the server name instead of reporting the web
> domain (which is in the Host header) as one would expect.  The URI reported
> at the application level is also wrong (numeric instead of the domain) for
> the same reason I suppose.

Given that :authority is not mandatory, I presume there should be at
least an option in Jetty to report the Host when it is missing. For
example RFC7540 used to hint about this:

      Clients
      that generate HTTP/2 requests directly SHOULD use the ":authority"
      pseudo-header field instead of the Host header field.  An
      intermediary that converts an HTTP/2 request to HTTP/1.1 MUST
      create a Host header field if one is not present in a request by
      copying the value of the ":authority" pseudo-header field.

> If the remote user connects to the frontend with HTTP/2 everything is
> correct (I assume that is because the :authority pseudo header was already
> in there).
> 
> If I remove "proto h2" from the backend, everything is OK too (because in
> this case :authority is converted to a Host header when connecting to the
> backend).
> 
> Is there something I can do to fix this?  Is this a bug (either from
> haproxy or Jetty) ?

I suspect it's not a bug but a config decision on Jetty's side.

Particularly, a few years ago some vulnerabilities were exposed in H2
agents not strictly following the spec regarding host vs :authority,
making combinations of such agents rather "fun". It's very possible
that some implementations have decided to restrict the fallbacks by
default and to only rely on :authority unless configured to look at
Host when missing, according to the spec.

If required there is a hack that can be used to force an authority.
If you force an authority in your request it should normally work:

   http-request set-uri https://%[req.hdr(host)]%[pathq]

But it would be cleaner to make sure the server accepts a request that
only has a host and no authority.

Regards,
Willy

Reply via email to