Skip to content

GH-1319: [JDBC] Add hostnameOverride connection property for TLS certificate verification and SNI - #1363

Open
fredjoonpark wants to merge 2 commits into
apache:mainfrom
fredjoonpark:GH-1319-hostname-override
Open

fredjoonpark wants to merge 2 commits into
apache:mainfrom
fredjoonpark:GH-1319-hostname-override

Conversation

@fredjoonpark

Copy link
Copy Markdown

What's Changed

Adds an optional hostnameOverride connection property to the Flight SQL JDBC driver. When TLS is enabled, the driver verifies the server certificate against this hostname and sends it for SNI and as the gRPC :authority, instead of using the host in the JDBC URL. This lets the driver connect with full certificate verification through SSH tunnels, port-forwarding, private endpoints, or IP addresses that don't match the server certificate. Today the only option in those cases is disableCertificateVerification=true.

  • New property hostnameOverride (string, default unset) in ArrowFlightConnectionProperty, passed from ArrowFlightConnection to ArrowFlightSqlClientHandler.Builder.
  • Builder.build() calls NettyClientBuilder.overrideHostname(...) when encryption is on. The property is ignored when useEncryption=false, and a blank value is treated as unset.
  • Extra endpoint locations in getStreams(): the override is applied only when the endpoint host matches the original connection host (case-insensitive; the port isn't compared). Endpoints on any other host, or any endpoint when the original connection isn't encrypted, are verified against their own hostname, as they are today. This logic is in Builder.getHostnameOverrideForEndpoint(URI) so it can be unit-tested.
  • Documented in docs/source/flight_sql_jdbc_driver.rst next to tlsRootCerts.

Example:

jdbc:arrow-flight-sql://192.0.2.10:443?useEncryption=true&tlsRootCerts=/path/ca.pem&hostnameOverride=flight.example.com

Tests

  • New ConnectionTlsHostnameOverrideTest. It uses the existing test certificate, which is issued for localhost and has no IP SAN. Its servers bind to 127.0.0.1 and the tests connect there, so they don't depend on how localhost resolves. Failure cases assert that the cause is a CertificateException, not just any SQLException:
    • without the property, the connection fails (No subject alternative names present);
    • with hostnameOverride=localhost, set in the URL or in Properties, it succeeds with full verification;
    • a non-matching override fails, which shows verification uses the override;
    • a blank hostnameOverride= behaves as unset;
    • the property is ignored for unencrypted connections;
    • a partitioned query whose endpoint is on the connection's IP succeeds, which shows the override reaches endpoint clients.
  • ArrowFlightSqlClientHandlerBuilderTest: the default is unset and the builder copy keeps the value. The endpoint rule is covered for same host, different port, different case, IPv6, different host, unencrypted connection, blank value, and no override set.
  • ArrowFlightConnectionConfigImplTest: the property is parsed.

This change is additive and has no breaking changes.

Closes #1319.

@github-actions

Copy link
Copy Markdown

Thank you for opening a pull request!

Please label the PR with one or more of:

  • bug-fix
  • chore
  • dependencies
  • documentation
  • enhancement

Also, add the 'breaking-change' label if appropriate.

See CONTRIBUTING.md for details.

buildTimeMiddlewareFactories.forEach(clientBuilder::intercept);
if (useEncryption) {
clientBuilder.useTls();
if (hostnameOverride != null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hostnameOverride is only checked for != null, so an empty value (e.g. ...&hostnameOverride= from a templated URL) calls overrideAuthority(""). That fails with an invalid-authority error instead of falling back to the connection host.

Could we treat empty or blank as unset?

Suggested change
if (hostnameOverride != null) {
if (hostnameOverride != null && !hostnameOverride.trim().isEmpty()) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

withHostnameOverride now trims the value and treats blank as unset, so endpoint connections fall back to the host too.

Normalize hostnameOverride in the builder so an empty or blank value
(e.g. from a templated URL) falls back to the connection host instead of
failing with an invalid authority. Also skip the override for endpoints
when encryption is disabled, and document that it is sent as the gRPC
:authority.
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.

[JDBC] Add a hostnameOverride connection property to verify TLS against a hostname different from the connection host

2 participants