h2
h2 copied to clipboard
Do not require authority for UNIX domain sockets
This fixes connections where paths to UNIX domain sockets (*.sock
) are provided or the authority is empty.
Refers to https://github.com/hyperium/tonic/issues/243, https://github.com/cri-o/cri/issues/34
Test are failing now for sure. In case of an unix socket, we would only get a path and nothing else. Do you see any feasible way to implement this in h2?
The description mentions not requiring an authority, but it looks like this changes it to stop checking if the provided authority is valid. So, in the Unix socket case, does it send an :authority
that is not a normal looking domain name?
The description mentions not requiring an authority, but it looks like this changes it to stop checking if the provided authority is valid. So, in the Unix socket case, does it send an
:authority
that is not a normal looking domain name?
Unfortunately not, the authority would be the complete path, like /path/to/my/socket.sock
Would it be okay to early exit the validation path if the parsed URI is a path? Not sure how to classify a “path” though…
@seanmonstar I checked for a path. If this exists locally, then we assume that the connection through the UDS should work.
Is there anyone over here to give this a review? :innocent:
As I mentioned in grpc/grpc-go#3854 (comment), I think it's fine to ignore the
:authority
if it's a blank value. I would expect a client to not send an invalid authority.
Yes, but in case of a unix domain socket the authority is not blank. It's the socket path.
PTAL @seanmonstar
I changed the check to either exclude *.sock
authorities or if the authority is empty.
Has 0.4.0 solved the problem?
Just want to add that I am facing a similar issue using h2 to process gRPC requests using tonic. I have also open an issue https://github.com/hyperium/h2/issues/525
I am using h2 v0.3.1.
I have fixed the problem by just sending a warning when :authority
is invalid instead of rejecting the requests.
See my fix here: https://github.com/hyperium/h2/compare/master...arthurlm:master
Maybe we should find another more elegant solution than just ignoring *.sock
or other allowed list :authority
pattern ?
Didn't grpc-go change to not emit those malformed authority headers?
Any idea when this could be merged? Calling gRPC methods from Go clients or grpcurl on unix domain sockets still does not work due to this issue.
@seanmonstar PTAL
For anyone trying to get that working, the workaround is to simply send a dummy authority field from the client side:
- For grpcurl: add
-authority "dummy"
- For grpc-go: use
grpc.Dial("unix:///blah.sock", grpc.WithAuthority("dummy"), grpc.WithInsecure())
Changing this on the client side is currently not an option as I can't patch every kubelet.
Currently this issue is preventig me from properly implementing a Kubernetes Device plugin in Rust using Tonic.
I am exactly in the same situation: kubelet is the client and patch it is not an option for me either.
This is why few month ago I have fork this repository to disable panic if authority field is Err
(ie. It has failed to be "parsed" but still contains some data).
Moreover, from k8s point of view, authority field is not invalid. It represent namespace from where the request came from. This is not just a valid http authority but it still contains meaningful network information.
In my case, forking unlock me and I have been able to make k8s extension works correctly. But from my point of view this is too bad request fail because an optional field cannot be parsed.
@saschagrunert I think only checking for .sock
is kind of brittle, there is nothing forcing a socket to end with .sock
. A better heuristic might be to check for a leading /
@seanmonstar What would be the harm if we just ignore an invalid authority instead of failing with an error?
So the change would essentially become https://github.com/bachp/h2/commit/0a3f73b635c2c6ff56103428453a03ddd3cfe1eb
Totally agree on the fact there is nothing that enforce a socket file ending with .sock
.
A better check for that would be to check file descriptor exists and check it is a valid socket file (but it will still not works for unnamed unix socket).
The patch you have made is exactly the same I have suggest around 1 year ago :wink: https://github.com/hyperium/h2/commit/2c26edaa257b934744b13e7c8a8333c94f956d28 . I have also edit it later to keep log in case authority is not a valid http host (see https://github.com/hyperium/h2/compare/master...arthurlm:master).
If any maintainers want I would be happy to make a PR for this or you could take the solution of @bachp without the log.
It would avoid more people forking for this and let us use h2
with tonic
to create nice kubernetes rust extension :grin:.
@arthurlm I think logging it makes a lot of sense.
Before creating any PR, I have done more investigation to understand clearly what is happening.
In my extension service k8s case the :authority
field received looks like extension/<target namespace>/<target service>
.
Looking at RFCs:
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.
All HTTP/2 requests MUST include exactly one valid value for the ":method", ":scheme", and ":path" pseudo-header fields, unless it is a CONNECT request (Section 8.3). An HTTP request that omits mandatory pseudo-header fields is malformed (Section 8.1.2.6).
Many URI schemes include a hierarchical element for a naming authority so that governance of the name space defined by the remainder of the URI is delegated to that authority (which may, in turn, delegate it further).
The authority component is preceded by a double slash ("//") and is terminated by the next slash ("/"), question mark ("?"), or number sign ("#") character, or by the end of the URI.
Non-validating parsers (those that merely separate a URI reference into its major components) will often ignore the subcomponent structure of authority, treating it as an opaque string from the double-slash to the first terminating delimiter, until such time as the URI is dereferenced.
In HTTP/2, the CONNECT method is used to establish a tunnel over a single HTTP/2 stream to a remote host for similar purposes. The HTTP header field mapping works as defined in Section 8.1.2.3 ("Request Pseudo-Header Fields"), with a few differences. Specifically:
o The ":authority" pseudo-header field contains the host and port to connect to (equivalent to the authority-form of the request-target of CONNECT requests (see [RFC7230], Section 5.3)).
A CONNECT request that does not conform to these restrictions is malformed (Section 8.1.2.6).
So, from my understanding:
-
Following (1):
-
:authority
field is not mandatory in HTTP2 - It should be a valid URI without scheme and userinfo (so k8s might be sending valid content. Valid URI example:
data:foobar
)
-
-
Following (2):
- k8s is sending invalid content in
:authority
- data should be split as
:authority = extension
and:path = <target namespace>/<target service>
-
:authority
might be treated as opaque string by URI parser
- k8s is sending invalid content in
-
Following (3):
-
:authority
field should be a valid host and port in the case ofCONNECT
method.
-
NOTE: I have not read the whole RFCs but just read in details the mentioned sections. If anyone have better understanding of this RFCs, please feel free to comment :wink: !
Looking at current code:
-
h2
parse:authority
at https://github.com/hyperium/h2/blob/3383ef71e2dabe27fe979e8b2c82b61b51eca9f5/src/server.rs#L1467-L1476 -
h2
rely on http crate to parse content - if
uri::Authority::from_maybe_shared
fail, h2 forward the error and make the whole request "malformed" - In k8s case, parse fail because it contains
/
character. - If
parts
contains valid data it will be used to enhancedb
https://github.com/hyperium/h2/blob/3383ef71e2dabe27fe979e8b2c82b61b51eca9f5/src/server.rs#L1434 https://github.com/hyperium/h2/blob/3383ef71e2dabe27fe979e8b2c82b61b51eca9f5/src/server.rs#L1520 -
b
will finally be used to build return object readingb.body()
https://github.com/hyperium/h2/blob/3383ef71e2dabe27fe979e8b2c82b61b51eca9f5/src/server.rs#L1522-L1534 So,b.headers()
part does not seems to be used :thinking: ?
I now think we are here in a corner case.
- There are arguments telling both
h2
andk8s
are right. - For now, built
Uri
object does not look like to be directly used (except to filter malformed HTTP request) - Fix can be done at multiple level
- http crate (with an "opaque URI" parser)
- h2 crate
- kubernetes and all its extensions projects (way more harder ...)
- (We are really far away here from
*.sock
issue :wink:, which BTW does not seems described in RFCs)
@seanmonstar / hyperium maintainers: what is your point on view on this ?
Should we consider :authority
as an opaque string ?
Should we just ignore it when Authority::from_maybe_shared
fail ?
Should we fail request if :authority
is malformed only on CONNECT
method (see change https://github.com/arthurlm/h2/commit/986308cd2a3665453f74dbd50420260a5ce708f9) ? It better respect RFC. I have also tested it with k8s extension service and it still works. I personally think this is the best choice we have here.
Any other ideas or comments ?
@arthurlm I like your proposal to only ignore the malformed authority
on CONNECT
. Maybe just open a new MR with the proposed change to bring it more visibility from the maintainers.
@arthurlm 18 days before, k8s approves a PR related to this. Seems now tonic and h2 works well with it.