Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpc.Dial fails to connect to unix domain socket when https_proxy is set #8207

Open
Ongy opened this issue Mar 28, 2025 · 2 comments · May be fixed by #8215
Open

grpc.Dial fails to connect to unix domain socket when https_proxy is set #8207

Ongy opened this issue Mar 28, 2025 · 2 comments · May be fixed by #8215
Assignees

Comments

@Ongy
Copy link

Ongy commented Mar 28, 2025

NOTE: if you are reporting is a potential security vulnerability or a crash,
please follow our CVE process at
https://github.com/grpc/proposal/blob/master/P4-grpc-cve-process.md instead of
filing an issue here.

Please see the FAQ in our main README.md, then answer the questions below
before submitting your issue.

What version of gRPC are you using?

google.golang.org/grpc v1.71.0

What version of Go are you using (go version)?

go version go1.23.6 linux/amd64

What operating system (Linux, Windows, …) and version?

Linux (glinux, recent)

What did you do?

If possible, provide a recipe for reproducing the error.

Repro is available in Ongy/go-grpc-repro@3c23732

The repro is quite minimal and requires introspection of the debug output to recognize the error.

What did you expect to see?

I expect Dial to try and connect to a unix:///... domain socket and then fail due to some error of type "file doesn't exist"

What did you see instead?

By looking at the debug output, I can determine that the proxy logic picks up the https_proxy for a URL of schema unix://. While this just leads to a different type of error in the repro, this leads to a real error in an application where the unix socket does exist.

❯ GRPC_GO_LOG_VERBOSITY_LEVEL=99 GRPC_GO_LOG_SEVERITY_LEVEL=info go run ./...
2025/03/28 15:17:16 INFO: [core] original dial target is: "unix:///var/run/test.sock"
2025/03/28 15:17:16 INFO: [core] [Channel #1]Channel created
2025/03/28 15:17:16 INFO: [core] [Channel #1]parsed dial target is: resolver.Target{URL:url.URL{Scheme:"unix", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/var/run/test.sock", RawPath:"", OmitHost:false, ForceQuery:false, RawQuery:"", Fragment:"", RawFragment:""}}
2025/03/28 15:17:16 INFO: [core] [Channel #1]Channel authority set to "localhost"
2025/03/28 15:17:16 INFO: [delegating-resolver] Proxy URL detected : http://127.0.0.1
2025/03/28 15:17:16 INFO: [delegating-resolver] Addresses received from target resolver: [{Addr: "/var/run/test.sock", ServerName: "", Attributes: {"<%!p(networktype.keyType=grpc.internal.transport.networktype)>": "unix" }, }]
2025/03/28 15:17:16 INFO: [delegating-resolver] Addresses received from proxy resolver: [{Addr: "127.0.0.1:443", ServerName: "", }]
2025/03/28 15:17:16 INFO: [core] [Channel #1]Resolver state updated: {
  "Addresses": [
    {
      "Addr": "127.0.0.1:443",
      "ServerName": "",
      "Attributes": {
        "\u003c%!p(proxyattributes.keyType=grpc.resolver.delegatingresolver.proxyOptions)\u003e": "\u003c%!p(proxyattributes.Options={\u003cnil\u003e /var/run/test.sock})\u003e"
      },
      "BalancerAttributes": null,
      "Metadata": null
    }
  ],
  "Endpoints": [
    {
      "Addresses": [
        {
          "Addr": "127.0.0.1:443",
          "ServerName": "",
          "Attributes": {
            "\u003c%!p(proxyattributes.keyType=grpc.resolver.delegatingresolver.proxyOptions)\u003e": "\u003c%!p(proxyattributes.Options={\u003cnil\u003e /var/run/test.sock})\u003e"
          },
          "BalancerAttributes": null,
          "Metadata": null
        }
      ],
      "Attributes": null
    }
  ],
  "ServiceConfig": null,
  "Attributes": null
} (resolver returned new addresses)
@dfawley
Copy link
Member

dfawley commented Mar 28, 2025

Thanks for reporting this.

@eshitachandwani probably the right fix is to also avoid the proxy from inside the delegating resolver when the target resolver produces addresses whose networktype is anything that is not "tcp".

func Get(address resolver.Address) (string, bool) {

That's how the transport decides to use a different dialer:

networkType, ok := networktype.Get(addr)
if fn != nil {
// Special handling for unix scheme with custom dialer. Back in the day,
// we did not have a unix resolver and therefore targets with a unix
// scheme would end up using the passthrough resolver. So, user's used a
// custom dialer in this case and expected the original dial target to
// be passed to the custom dialer. Now, we have a unix resolver. But if
// a custom dialer is specified, we want to retain the old behavior in
// terms of the address being passed to the custom dialer.
if networkType == "unix" && !strings.HasPrefix(address, "\x00") {
// Supported unix targets are either "unix://absolute-path" or
// "unix:relative-path".
if filepath.IsAbs(address) {
return fn(ctx, "unix://"+address)
}
return fn(ctx, "unix:"+address)
}
return fn(ctx, address)
}

In this case, we shouldn't even wait for the proxy resolver to resolve before producing those addresses.

And we should make sure to add a test case for this scenario.

@anjmao
Copy link

anjmao commented Mar 31, 2025

As a quick fix until it's fixed you can look into grpc.WithNoProxy dial option.
Also, it's possible to implement custom address resolver (a bit more complex).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants