Skip to content

fix(socket): set customize_hostname_check for TLS wildcard matching - #60

Merged
hpopp merged 1 commit into
codedge-llc:masterfrom
henriiik:fix/tls-wildcard-hostname-check
Jul 1, 2026
Merged

fix(socket): set customize_hostname_check for TLS wildcard matching#60
hpopp merged 1 commit into
codedge-llc:masterfrom
henriiik:fix/tls-wildcard-hostname-check

Conversation

@henriiik

Copy link
Copy Markdown
Contributor

Summary

Kadabra.Socket opens :https connections with verify: :verify_peer but never sets customize_hostname_check. Without it, OTP's default hostname matcher compares DNS names by literal equality and skips RFC 6125 wildcard matching, so a server presenting only a wildcard SAN (e.g. *.googleapis.com) for a concrete host (fcm.googleapis.com) is rejected and the TLS handshake fails — fatal at boot for anything opening a Kadabra connection in its supervision tree (e.g. Pigeon's FCM dispatcher).

Impact

We use Kadabra via Pigeon for FCM push notifications. Around 05:45 CEST Google began serving an fcm.googleapis.com edge cert whose only matching SAN is the wildcard *.googleapis.com (no exact entry). Every connection then crashed at startup:

TLS :client: In state :wait_cert_cr at ssl_handshake.erl:2221 generated CLIENT ALERT: Fatal - Bad Certificate
 - {:bad_cert,
 {:hostname_check_failed, {:requested, ~c"fcm.googleapis.com"},
  {:received,
   [
     dNSName: ~c"upload.video.google.com",
     dNSName: ~c"*.clients.google.com",
     dNSName: ~c"*.docs.google.com",
     dNSName: ~c"*.drive.google.com",
     dNSName: ~c"*.gdata.youtube.com",
     dNSName: ~c"*.googleapis.com",
     dNSName: ~c"*.photos.google.com",
     dNSName: ~c"*.youtube-3rd-party.com",
     dNSName: ~c"upload.google.com",
     dNSName: ~c"*.upload.google.com",
     dNSName: ~c"upload.youtube.com",
     dNSName: ~c"*.upload.youtube.com",
     dNSName: ~c"uploads.stage.gdata.youtube.com",
     dNSName: ~c"bg-call-donation.goog",
     dNSName: ~c"bg-call-donation-alpha.goog",
     dNSName: ~c"bg-call-donation-canary.goog",
     dNSName: ~c"bg-call-donation-dev.goog"
   ]}}}

*.googleapis.com covers the requested host, but the default matcher refuses the wildcard.

Fix

Add the HTTPS match_fun to the default :https options:

{:customize_hostname_check,
 [match_fun: :public_key.pkix_verify_hostname_match_fun(:https)]},

pkix_verify_hostname_match_fun(:https) is the only thing that routes DNS pairs into wildcard matching; ssl uses it solely via customize_hostname_check. Caller :ssl opts still win — they're prepended and :ssl honors the first occurrence.

Verification

  • OTP 28, wildcard-only *.googleapis.com cert: pkix_verify_hostname/2 returns false with the default matcher, true with pkix_verify_hostname_match_fun(:https).
  • mix test green on pinned toolchain (Elixir 1.19 / OTP 28): 47 doctests, 24 tests, 0 failures.

The :https socket options enable verify: :verify_peer but never set
customize_hostname_check. Without it, OTP's default hostname matcher
compares DNS names by literal equality and skips RFC 6125 wildcard
matching, so a server presenting a wildcard SAN (e.g. *.googleapis.com)
is rejected against a concrete host (fcm.googleapis.com) with
{:bad_cert, {:hostname_check_failed, ...}}, killing the connection at boot.

Add the HTTPS match_fun (public_key.pkix_verify_hostname_match_fun(:https))
so wildcard certificates verify correctly. Caller-supplied :ssl opts still
win, since ssl honors the first occurrence of an option and caller opts
are prepended.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hpopp

hpopp commented Jul 1, 2026

Copy link
Copy Markdown
Member

Thanks for this! Let me get a release out.

@hpopp
hpopp merged commit 32d569b into codedge-llc:master Jul 1, 2026
3 checks passed
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.

2 participants