Skip to content

fix(client): pass dns:// hostname to Mint for TLS SNI and verification - #575

Open
atirna wants to merge 2 commits into
elixir-grpc:masterfrom
atirna:fix/dns-target-sni
Open

fix(client): pass dns:// hostname to Mint for TLS SNI and verification#575
atirna wants to merge 2 commits into
elixir-grpc:masterfrom
atirna:fix/dns-target-sni

Conversation

@atirna

@atirna atirna commented Aug 24, 2026

Copy link
Copy Markdown

Summary

A dns:// target resolves the hostname to an IP and then dials that IP, so TLS
sees the IP where the hostname belongs: SNI carries the IP, certificate hostname
verification fails (hostname_check_failed, requested IP vs *.googleapis.com
SANs), and the :authority pseudo-header is wrong too.

Root cause

GRPC.Client.Resolver.DNS resolves dns://host:port into
%{address: ip, port: port} and the hostname is dropped right there. The
resolved IP becomes channel.host, and the Mint adapter passes it to
Mint.HTTP.connect/4, which uses the connect host for SNI, certificate
hostname verification, and :authority.

Fix

Keep the hostname alongside each resolved address, carry it onto the channel as
hostname, and pass it to Mint through its :hostname option, which is
documented for exactly this case: dial the address, but use the given hostname
for the Host header, hostname verification, and SNI.

Verified end to end against the real path: a dns:// target resolving to
127.0.0.1 now reaches Mint.HTTP.connect(:https, "127.0.0.1", port, hostname: "my-service.local").

Fixes #572

Test plan

  • test "keeps the hostname of a dns:// target alongside the resolved IP"
    resolver-level regression test (fails on master: the address map has no
    :hostname key)
  • test "carries the dns:// hostname onto the channel for TLS SNI" — the
    connection layer threads the resolver's hostname onto the channel (fails on
    master with a KeyError on channel.hostname)
  • test "dials the resolved address but hands the hostname to Mint for TLS"
    the Mint adapter hands the hostname to the connection process connect opts
    while still dialing the resolved IP (fails on master:
    state.connect_opts[:hostname] is nil)

Full suite locally: mix test passes except one pre-existing timing-sensitive
ReResolveTest flake, which reproduces on master with the same frequency
(reproduced across multiple runs on both trees).

Notes

The Gun adapter derives SNI from the host it dials as well (gun's
ensure_tls_opts/3 uses origin_host), so it has the same shape of problem
for dns:// targets. I left it out of this PR to keep the change scoped to the
adapter from the report; passing server_name_indication through
transport_opts there would follow the same idea.

atirna and others added 2 commits August 25, 2026 01:46
A dns:// target resolves to an IP, and the resolved IP was used as the
connect host, so TLS saw the IP instead of the target hostname: SNI,
certificate hostname verification, and the :authority pseudo-header all
failed against servers like *.googleapis.com.

Keep the hostname alongside each resolved address, carry it onto the
channel, and pass it to Mint as the hostname option, which dials the
resolved address while TLS uses the hostname.

Fixes elixir-grpc#572
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DNS target fails due to hostname checks

1 participant