It appears some HTTP clients (in particular gRPC clients) that connect over Unix Domain Sockets put the path to the socket in the Host header. We should investigate this a little bit. If it's the case that most clients do this, we should consider having an option to allow disabling Host header validation. We could limit this to only cases where we're listening on a Unix Socket to reduce security risk.
A UDS with a path is the recommended value in these docs
--csi-address: This is the path to the CSI driver socket (defined above) inside the pod that the node-driver-registrar container will use to issue CSI operations (e.g. /csi/csi.sock).
https://github.com/kubernetes-csi/node-driver-registrar#required-arguments
We should switch to path style validation, rather than disable altogether.
nginx makes the same host validation of the :authority pseudo header
I read now the RFC https://tools.ietf.org/html/rfc3986#section-3.2.2 twice
The bottom line is that it is a "reg-name" and it is a UTF8 free-text
...
This specification does not mandate a particular registered name
lookup technology and therefore does not restrict the syntax of reg-
name beyond what is necessary for interoperability. Instead, it
delegates the issue of registered name syntax conformance to the
operating system of each application performing URI resolution, and
that operating system decides what it will allow for the purpose of
host identification. A URI resolution implementation might use DNS,
host tables, yellow pages, NetInfo, WINS, or any other system for
lookup of registered names.
...
The reg-name syntax allows percent-encoded octets ... UTF8
I didn't saw that there is some kind of decoding in the current code.
like @blowdart wrote,
In the UDS case a switch to path style validation would be more conform to the RFC
Hi, i was doing something like:
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
var endPoint = new UnixDomainSocketEndPoint("/../run/foo/bar.sock");
socket.Connect(endPoint);
Console.Writeline("It's working!");
var channel = GrpcChannel.ForAddress("/../run/foo/bar.sock");
The last part fails with the message "_System.ArgumentException: Only 'http' and 'https' schemes are allowed_"
Some part of this makes sense? I'm trying to imitate golang things :worried:
Thanks!!!
GrpcChannel only supports TCP. Support for UDS and pipes is planned for 5.0
Do we know if the UDS client is URL-encoding the Host header? I do see that the RFC allows / but only if it's URL-encoded. When I did a quick test with curl, I saw that both foo/bar and foo%2fbar were rejected.
Perhaps this doesn't need special configuration and we just need to be validating pre decoding. If the UDS clients are encoding the header value, then we can be both spec-compliant and support UDS.
@blowdart If you have concerns there though, I like the idea of doing path-style validation when listening on a UDS.
Related to the issue, please consider esoteric Windows named pipes values for when Kestrel is using that transport.
https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
Can we iterate through transports, and have transports decide if the host header is valid for them? That seems a better approach than either on or off.
This may relate to proposed work on Named Pipes, so @Tratcher can take a look for now
Is there any design decision made?
Would need it badly for my kubernetes CSI plugin,
the only other framework or proxy that i found
with a support for h2 over UDS is golang.
@Zetanova even when we do this, it will only be for .NET 5. In the meantime you're going to need a different solution.
Planning notes: We'll look at allowing validation to be disabled when using UDS. Targeting 5.0, but not the immediate next preview and may be punted. It must be configurable per-endpoint.
I've experimented with gRPC over unix domain sockets with Grpc.Net.Client (using HttpClient) calling Grpc.AspNetCore (using Kestrel).
HttpClient will always send a valid host header, even when using a different transport. It is a hard requirement of using HttpClient.
I recommend moving this issue to future milestone. We can judge whether it is needed based on additional demand.
A workaround is to use golang as a sidecar/reverse proxy to overwrite the host header.
https://github.com/Zetanova/grpc-proxy
It is the only way that i found to use dotnet-kestrel for kubelet/kubernetes plugins over UDS
I looked into this more. go-grpc's behavior of sending the file path has been causing problems for other servers like nodejs. See https://github.com/grpc/grpc-go/issues/2628
go-grpc has recently been updated to send "localhost" as the authority with unix domain sockets. See https://github.com/grpc/grpc-go/pull/3730. The fix is in 1.31 which is available today. Upgrade to it and retry?
I think Kestrel is doing the right thing here.
@JamesNK thx for the good news.
Yes all proxies (nginx, apache, kestrel, candy) had this issue.
Because kubernetes/kubelet is the gRPC-client,
to try it out, the kubelet BIN would need to use the new version of golang,
I will do it on the next kubernetes version.