Summary
When a TLS client sends an SNI hostname that is covered only by a SAN of a configured certificate (e.g. hostname=example.com, san=www.example.com, SNI www.example.com), Carapace serves the listener's default certificate, so the client gets a hostname mismatch even though the right certificate is configured and loaded.
The cause: ListeningChannel.apply() registers only the certificate id (the configured hostname) as the Reactor Netty SNI mapping key, so SANs never make it into the map. A SAN-aware selector already exists (CertificatesUtils.chooseCertificate()), but its only caller is SSLSNITest.
Proposal
Register every name of each certificate (hostname + SANs, excluding *) as an SNI mapping, ideally reading the SANs from the loaded keystore (the chain is already available via readChainFromKeystore()) and falling back to the configured ones. That should do the trick.
Implementation notes
- Netty's
DomainWildcardMappingBuilder already handles wildcard names correctly (single-level match, per RFC 6125): multi-level wildcard matching should not be added, clients would reject those certificates anyway.
- Precedence needs defining when two certificates claim the same name (today the last
addSniMapping wins).
SSLSNITest should be aligned with the real selection path: either promote chooseCertificate() to production (fixing its endsWith matching) or drop it and test through a real TLS handshake.
Summary
When a TLS client sends an SNI hostname that is covered only by a SAN of a configured certificate (e.g.
hostname=example.com,san=www.example.com, SNIwww.example.com), Carapace serves the listener's default certificate, so the client gets a hostname mismatch even though the right certificate is configured and loaded.The cause:
ListeningChannel.apply()registers only the certificate id (the configured hostname) as the Reactor Netty SNI mapping key, so SANs never make it into the map. A SAN-aware selector already exists (CertificatesUtils.chooseCertificate()), but its only caller isSSLSNITest.Proposal
Register every name of each certificate (hostname + SANs, excluding
*) as an SNI mapping, ideally reading the SANs from the loaded keystore (the chain is already available viareadChainFromKeystore()) and falling back to the configured ones. That should do the trick.Implementation notes
DomainWildcardMappingBuilderalready handles wildcard names correctly (single-level match, per RFC 6125): multi-level wildcard matching should not be added, clients would reject those certificates anyway.addSniMappingwins).SSLSNITestshould be aligned with the real selection path: either promotechooseCertificate()to production (fixing itsendsWithmatching) or drop it and test through a real TLS handshake.