Skip to content

[DB-23477] Verify node hostnames against the cluster endpoint - #39

Open
HarshDaryani896 wants to merge 1 commit into
masterfrom
fix/ybmanaged-hostname-verifier-endpoint
Open

[DB-23477] Verify node hostnames against the cluster endpoint#39
HarshDaryani896 wants to merge 1 commit into
masterfrom
fix/ybmanaged-hostname-verifier-endpoint

Conversation

@HarshDaryani896

@HarshDaryani896 HarshDaryani896 commented Sep 10, 2026

Copy link
Copy Markdown

Fixes DB-23477.

The problem

sslhostnameverifier=com.yugabyte.ysql.YBManagedHostnameVerifier — the setting our docs tell Aeon users to apply for verify-full + load balancing — fails 100% of connections. A customer hit this in production:

PSQLException: Something unusual has occurred to cause the driver to fail.
 ← RuntimeException: PSQLException: The connection attempt failed.
 ← UnknownHostException: *.<cluster-uuid>.aws.ybdb.io

The verifier never matched the host against the certificate. It read a SAN out of the certificate and dialed that SAN string as a hostname, then queried yb_servers() to decide whether the host was a cluster member. A SAN is a pattern for matching names, not an address, so every certificate shape hit one of three failures:

Certificate Deployment Failure
wildcard DNS SAN Aeon UnknownHostException — a wildcard resolves to nothing
resolvable DNS or IP SAN YBA, on-premises StackOverflowError — see below
no usable SAN any NullPointerException from setProperty("PGHOST", null)

The membership check the class exists to perform has therefore never executed.

The recursion is worth calling out: the control connection was built from the same Properties, still carrying sslhostnameverifier and verify-full, so it re-entered verify(). The static field meant to prevent that is only assigned once the constructor returns, which never happens on the first call.

Failures were also wrapped in RuntimeException, which bypassed every SQLException handler in LoadBalanceService — including the "Failed to apply load balance. Trying normal connection" fallback. That is why pools died at startup rather than degrading.

The fix

The information needed was in the connection URL all along.

LoadBalanceService records the user's host in yb-endpoint-host before replacing it with a node address. verify() then makes two comparisons with no I/O at all:

  1. the dialed host against the certificate — per-node certificates (YBA, on-premises);
  2. the recorded endpoint — a cluster-wide certificate (Aeon).

Wildcard matching is RFC 6125 and already implemented in PGjdbcHostnameVerifier.verifyHostName.

The resulting guarantee equals verify-full connecting straight to the endpoint: the peer must present a certificate that chains to the configured sslrootcert and is valid for the name the user asked for. This is what the Node.js smart driver already does via servername.

Removing the round trip removes the recursion, the null SAN and the RuntimeException together, and deletes getCurrentServers(), the SAN loop, the static control connection and four unused fields — about 180 lines.

The endpoint is recorded for the control connection too, because the refresh retry path in checkAndRefresh can point it at a node address from yb_servers(), which a cluster-wide certificate does not cover.

MakeSSL no longer special-cases this class by name — it has a (Properties) constructor and goes through ObjectFactory like any other verifier, restoring verifyPeerName to its upstream shape.

Testing

  1. Manually tested against a multi-node VPC peered YB Aeon cluster with verify-full. (The connection string should have the load balancer IP).
  2. Ran the existing 7 tests [excluding Performance Test] in driver-examples repo.
  3. Added a new hostname verifier test: https://github.com/yugabyte/driver-examples/pull/71

🤖 Generated with Claude Code

YBManagedHostnameVerifier did not match the host against the certificate.
It read a SAN out of the certificate and opened a connection to that SAN
string, then queried yb_servers() to decide whether the host was a cluster
member. That could never work:

  - a YugabyteDB Aeon certificate carries only a wildcard DNS SAN
    (*.<cluster-uuid>.aws.ybdb.io), which resolves to nothing, so the
    control connection failed with UnknownHostException. The wildcard guard
    covered only TYPE_IP_ADDRESS, and was ineffective even there because san
    was assigned before the check.
  - that control connection was built from the same Properties, still
    carrying sslhostnameverifier and verify-full, so on a cluster whose SAN
    did resolve it re-entered verify() and recursed until StackOverflowError.
    The static field meant to prevent this is only assigned once the
    constructor returns, which never happened on the first call.
  - a certificate with no usable SAN left san null, and
    setProperty("PGHOST", null) threw NullPointerException.

Every certificate shape hit one of those three, so the membership check the
class exists to perform never executed in any released version.

Failures were also wrapped in RuntimeException, which bypassed every
SQLException handler in LoadBalanceService, including the fallback to a
normal connection - so a connection pool died at startup instead of
degrading.

The information needed was in the connection URL all along. Load balancing
now records the user's host in yb-endpoint-host before replacing it with a
node address, and verify() performs two checks with no I/O: the dialed host
against the certificate (per-node certificates, YBA and on-premises), then
the recorded endpoint (a cluster-wide certificate, Aeon). Wildcard matching
is RFC 6125 and already implemented by PGjdbcHostnameVerifier.

The resulting guarantee equals verify-full connecting to the endpoint
directly: the peer must present a certificate that chains to sslrootcert and
is valid for the name the user asked for. The endpoint is recorded for the
control connection too, since the refresh retry path can point it at a node
address from yb_servers().

MakeSSL no longer special-cases this class by name; it has a (Properties)
constructor and goes through ObjectFactory like any other verifier, which
restores verifyPeerName to its upstream shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HarshDaryani896
HarshDaryani896 force-pushed the fix/ybmanaged-hostname-verifier-endpoint branch from 8330ba4 to 5cc42bb Compare September 10, 2026 11:00
Comment thread pgjdbc/src/main/java/com/yugabyte/ysql/LoadBalanceService.java

@ashetkar ashetkar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please bump up the version before merging

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