diff --git a/ci/conda_env_cpp.yml b/ci/conda_env_cpp.yml
index a0c2e99aca7..90cef3ea2d1 100644
--- a/ci/conda_env_cpp.yml
+++ b/ci/conda_env_cpp.yml
@@ -25,7 +25,7 @@ cmake
gflags
glog
gmock>=1.8.1
-grpc-cpp>=1.21.4
+grpc-cpp>=1.27.3
gtest=1.8.1
libprotobuf
libutf8proc
diff --git a/cpp/cmake_modules/Findzstd.cmake b/cpp/cmake_modules/Findzstd.cmake
index f7c68134e9d..6659a682da7 100644
--- a/cpp/cmake_modules/Findzstd.cmake
+++ b/cpp/cmake_modules/Findzstd.cmake
@@ -23,18 +23,24 @@ set(ZSTD_LIB_NAME_BASE "${ZSTD_MSVC_LIB_PREFIX}zstd")
if(ARROW_ZSTD_USE_SHARED)
set(ZSTD_LIB_NAMES)
if(CMAKE_IMPORT_LIBRARY_SUFFIX)
- list(APPEND ZSTD_LIB_NAMES
- "${CMAKE_IMPORT_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${CMAKE_IMPORT_LIBRARY_SUFFIX}")
+ list(
+ APPEND
+ ZSTD_LIB_NAMES
+ "${CMAKE_IMPORT_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
+ )
endif()
- list(APPEND ZSTD_LIB_NAMES
- "${CMAKE_SHARED_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${CMAKE_SHARED_LIBRARY_SUFFIX}")
+ list(
+ APPEND
+ ZSTD_LIB_NAMES
+ "${CMAKE_SHARED_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${CMAKE_SHARED_LIBRARY_SUFFIX}")
else()
if(MSVC AND NOT DEFINED ZSTD_MSVC_STATIC_LIB_SUFFIX)
set(ZSTD_MSVC_STATIC_LIB_SUFFIX "_static")
endif()
set(ZSTD_STATIC_LIB_SUFFIX
"${ZSTD_MSVC_STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
- set(ZSTD_LIB_NAMES "${CMAKE_STATIC_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${ZSTD_STATIC_LIB_SUFFIX}")
+ set(ZSTD_LIB_NAMES
+ "${CMAKE_STATIC_LIBRARY_PREFIX}${ZSTD_LIB_NAME_BASE}${ZSTD_STATIC_LIB_SUFFIX}")
endif()
# First, find via if specified ZTD_ROOT
@@ -66,7 +72,9 @@ else()
PATH_SUFFIXES ${ARROW_LIBRARY_PATH_SUFFIXES})
else()
# Third, check all other CMake paths
- find_library(ZSTD_LIB NAMES ${ZSTD_LIB_NAMES} PATH_SUFFIXES ${ARROW_LIBRARY_PATH_SUFFIXES})
+ find_library(ZSTD_LIB
+ NAMES ${ZSTD_LIB_NAMES}
+ PATH_SUFFIXES ${ARROW_LIBRARY_PATH_SUFFIXES})
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h PATH_SUFFIXES ${ARROW_INCLUDE_PATH_SUFFIXES})
endif()
endif()
diff --git a/cpp/src/arrow/flight/CMakeLists.txt b/cpp/src/arrow/flight/CMakeLists.txt
index 03483f4e8c3..9d0f8614dd7 100644
--- a/cpp/src/arrow/flight/CMakeLists.txt
+++ b/cpp/src/arrow/flight/CMakeLists.txt
@@ -61,6 +61,48 @@ set_source_files_properties(${FLIGHT_GENERATED_PROTO_FILES} PROPERTIES GENERATED
add_custom_target(flight_grpc_gen ALL DEPENDS ${FLIGHT_GENERATED_PROTO_FILES})
+# -Werror / /WX cause try_compile to fail because there seems to be no
+# way to pass -isystem $GRPC_INCLUDE_DIR instead of -I$GRPC_INCLUDE_DIR
+set(CMAKE_CXX_FLAGS_BACKUP "${CMAKE_CXX_FLAGS}")
+string(REPLACE "/WX" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+string(REPLACE "-Werror" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+
+# Probe the version of gRPC being used to see if it supports disabling server
+# verification when using TLS.
+message(STATUS "Checking support for TlsCredentialsOptions...")
+get_property(CURRENT_INCLUDE_DIRECTORIES
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ PROPERTY INCLUDE_DIRECTORIES)
+try_compile(HAS_GRPC_132 ${CMAKE_CURRENT_BINARY_DIR}/try_compile SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/try_compile/check_tls_opts_132.cc"
+ CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CURRENT_INCLUDE_DIRECTORIES}"
+ LINK_LIBRARIES gRPC::grpc
+ OUTPUT_VARIABLE TSL_CREDENTIALS_OPTIONS_CHECK_OUTPUT CXX_STANDARD 11)
+
+if(HAS_GRPC_132)
+ message(STATUS "TlsCredentialsOptions found in grpc::experimental.")
+ add_definitions(-DGRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS=grpc::experimental)
+else()
+ message(STATUS "TlsCredentialsOptions not found in grpc::experimental.")
+ message(STATUS "Build output: ${TSL_CREDENTIALS_OPTIONS_CHECK_OUTPUT}")
+
+ try_compile(HAS_GRPC_127 ${CMAKE_CURRENT_BINARY_DIR}/try_compile SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/try_compile/check_tls_opts_127.cc"
+ CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CURRENT_INCLUDE_DIRECTORIES}"
+ OUTPUT_VARIABLE TSL_CREDENTIALS_OPTIONS_CHECK_OUTPUT CXX_STANDARD 11)
+
+ if(HAS_GRPC_127)
+ message(STATUS "TlsCredentialsOptions found in grpc_impl::experimental.")
+ add_definitions(-DGRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS=grpc_impl::experimental)
+ else()
+ message(STATUS "TlsCredentialsOptions not found in grpc_impl::experimental.")
+ message(STATUS "Build output: ${TSL_CREDENTIALS_OPTIONS_CHECK_OUTPUT}")
+ endif()
+endif()
+
+# Restore the CXXFLAGS that were modified above
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_BACKUP}")
+
# Note, we do not compile the generated Protobuf sources directly, instead
# compiling then via protocol_internal.cc which contains some gRPC template
# overrides to enable Flight-specific optimizations. See comments in
diff --git a/cpp/src/arrow/flight/client.cc b/cpp/src/arrow/flight/client.cc
index b25b13edf39..821da214a59 100644
--- a/cpp/src/arrow/flight/client.cc
+++ b/cpp/src/arrow/flight/client.cc
@@ -30,10 +30,15 @@
#ifdef GRPCPP_PP_INCLUDE
#include
+#if defined(GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS)
+#include
+#endif
#else
#include
#endif
+#include
+
#include "arrow/buffer.h"
#include "arrow/ipc/reader.h"
#include "arrow/ipc/writer.h"
@@ -84,6 +89,9 @@ std::shared_ptr FlightWriteSizeStatusDetail::Unwrap
return std::dynamic_pointer_cast(status.detail());
}
+FlightClientOptions::FlightClientOptions()
+ : write_size_limit_bytes(0), disable_server_verification(false) {}
+
FlightClientOptions FlightClientOptions::Defaults() { return FlightClientOptions(); }
struct ClientRpc {
@@ -835,6 +843,31 @@ class GrpcMetadataReader : public FlightMetadataReader {
std::shared_ptr read_mutex_;
};
+namespace {
+// Dummy self-signed certificate to be used because TlsCredentials
+// requires root CA certs, even if you are skipping server
+// verification.
+#if defined(GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS)
+constexpr char BLANK_ROOT_PEM[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICwzCCAaugAwIBAgIJAM12DOkcaqrhMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n"
+ "BAMTCWxvY2FsaG9zdDAeFw0yMDEwMDcwODIyNDFaFw0zMDEwMDUwODIyNDFaMBQx\n"
+ "EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+ "ggEBALjJ8KPEpF0P4GjMPrJhjIBHUL0AX9E4oWdgJRCSFkPKKEWzQabTQBikMOhI\n"
+ "W4VvBMaHEBuECE5OEyrzDRiAO354I4F4JbBfxMOY8NIW0uWD6THWm2KkCzoZRIPW\n"
+ "yZL6dN+mK6cEH+YvbNuy5ZQGNjGG43tyiXdOCAc4AI9POeTtjdMpbbpR2VY4Ad/E\n"
+ "oTEiS3gNnN7WIAdgMhCJxjzvPwKszV3f7pwuTHzFMsuHLKr6JeaVUYfbi4DxxC8Z\n"
+ "k6PF6dLlLf3ngTSLBJyaXP1BhKMvz0TaMK3F0y2OGwHM9J8np2zWjTlNVEzffQZx\n"
+ "SWMOQManlJGs60xYx9KCPJMZZsMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo\n"
+ "b3N0MA0GCSqGSIb3DQEBBQUAA4IBAQC0LrmbcNKgO+D50d/wOc+vhi9K04EZh8bg\n"
+ "WYAK1kLOT4eShbzqWGV/1EggY4muQ6ypSELCLuSsg88kVtFQIeRilA6bHFqQSj6t\n"
+ "sqgh2cWsMwyllCtmX6Maf3CLb2ZdoJlqUwdiBdrbIbuyeAZj3QweCtLKGSQzGDyI\n"
+ "KH7G8nC5d0IoRPiCMB6RnMMKsrhviuCdWbAFHop7Ff36JaOJ8iRa2sSf2OXE8j/5\n"
+ "obCXCUvYHf4Zw27JcM2AnnQI9VJLnYxis83TysC5s2Z7t0OYNS9kFmtXQbUNlmpS\n"
+ "doQ/Eu47vWX7S0TXeGziGtbAOKxbHE0BGGPDOAB/jGW/JVbeTiXY\n"
+ "-----END CERTIFICATE-----\n";
+#endif
+} // namespace
class FlightClient::FlightClientImpl {
public:
Status Connect(const Location& location, const FlightClientOptions& options) {
@@ -845,18 +878,49 @@ class FlightClient::FlightClientImpl {
if (scheme == kSchemeGrpc || scheme == kSchemeGrpcTcp || scheme == kSchemeGrpcTls) {
grpc_uri << location.uri_->host() << ":" << location.uri_->port_text();
- if (scheme == "grpc+tls") {
- grpc::SslCredentialsOptions ssl_options;
- if (!options.tls_root_certs.empty()) {
- ssl_options.pem_root_certs = options.tls_root_certs;
- }
- if (!options.cert_chain.empty()) {
- ssl_options.pem_cert_chain = options.cert_chain;
- }
- if (!options.private_key.empty()) {
- ssl_options.pem_private_key = options.private_key;
+ if (scheme == kSchemeGrpcTls) {
+ if (options.disable_server_verification) {
+#if !defined(GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS)
+ return Status::NotImplemented(
+ "Using encryption with server verification disabled is unsupported. "
+ "Please use a release of Arrow Flight built with gRPC 1.27 or higher.");
+#else
+ namespace ge = GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS;
+
+ // A callback to supply to TlsCredentialsOptions that accepts any server
+ // arguments.
+ struct NoOpTlsAuthorizationCheck
+ : public ge::TlsServerAuthorizationCheckInterface {
+ int Schedule(ge::TlsServerAuthorizationCheckArg* arg) override {
+ arg->set_success(1);
+ arg->set_status(GRPC_STATUS_OK);
+ return 0;
+ }
+ };
+
+ noop_auth_check_ = std::make_shared(
+ std::make_shared());
+ auto materials_config = std::make_shared();
+ materials_config->set_pem_root_certs(BLANK_ROOT_PEM);
+ ge::TlsCredentialsOptions tls_options(
+ GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+ GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION, materials_config,
+ std::shared_ptr(), noop_auth_check_);
+ creds = ge::TlsCredentials(tls_options);
+#endif
+ } else {
+ grpc::SslCredentialsOptions ssl_options;
+ if (!options.tls_root_certs.empty()) {
+ ssl_options.pem_root_certs = options.tls_root_certs;
+ }
+ if (!options.cert_chain.empty()) {
+ ssl_options.pem_cert_chain = options.cert_chain;
+ }
+ if (!options.private_key.empty()) {
+ ssl_options.pem_private_key = options.private_key;
+ }
+ creds = grpc::SslCredentials(ssl_options);
}
- creds = grpc::SslCredentials(ssl_options);
} else {
creds = grpc::InsecureChannelCredentials();
}
@@ -1101,6 +1165,15 @@ class FlightClient::FlightClientImpl {
private:
std::unique_ptr stub_;
std::shared_ptr auth_handler_;
+#if defined(GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS)
+ // Scope the TlsServerAuthorizationCheckConfig to be at the class instance level, since
+ // it gets created during Connect() and needs to persist to DoAction() calls. gRPC does
+ // not correctly increase the reference count of this object:
+ // https://github.com/grpc/grpc/issues/22287
+ std::shared_ptr<
+ GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS::TlsServerAuthorizationCheckConfig>
+ noop_auth_check_;
+#endif
int64_t write_size_limit_bytes_;
};
diff --git a/cpp/src/arrow/flight/client.h b/cpp/src/arrow/flight/client.h
index a6ad17b9279..935e8fb92ba 100644
--- a/cpp/src/arrow/flight/client.h
+++ b/cpp/src/arrow/flight/client.h
@@ -90,6 +90,8 @@ class ARROW_FLIGHT_EXPORT FlightWriteSizeStatusDetail : public arrow::StatusDeta
class ARROW_FLIGHT_EXPORT FlightClientOptions {
public:
+ FlightClientOptions();
+
/// \brief Root certificates to use for validating server
/// certificates.
std::string tls_root_certs;
@@ -108,10 +110,14 @@ class ARROW_FLIGHT_EXPORT FlightClientOptions {
/// positive. When enabled, FlightStreamWriter.Write* may yield a
/// IOError with error detail FlightWriteSizeStatusDetail.
int64_t write_size_limit_bytes;
+
/// \brief Generic connection options, passed to the underlying
/// transport; interpretation is implementation-dependent.
std::vector>> generic_options;
+ /// \brief Use TLS without validating the server certificate. Use with caution.
+ bool disable_server_verification;
+
/// \brief Get default options.
static FlightClientOptions Defaults();
};
diff --git a/cpp/src/arrow/flight/flight_test.cc b/cpp/src/arrow/flight/flight_test.cc
index 8726a554a1c..a5fdfe8184e 100644
--- a/cpp/src/arrow/flight/flight_test.cc
+++ b/cpp/src/arrow/flight/flight_test.cc
@@ -1800,6 +1800,32 @@ TEST_F(TestTls, DoAction) {
ASSERT_EQ(result->body->ToString(), "Hello, world!");
}
+#if defined(GRPC_NAMESPACE_FOR_TLS_CREDENTIALS_OPTIONS)
+TEST_F(TestTls, DisableServerVerification) {
+ std::unique_ptr client;
+ auto client_options = FlightClientOptions();
+ // For security reasons, if encryption is being used,
+ // the client should be configured to verify the server by default.
+ ASSERT_EQ(client_options.disable_server_verification, false);
+ client_options.disable_server_verification = true;
+ ASSERT_OK(FlightClient::Connect(location_, client_options, &client));
+
+ FlightCallOptions options;
+ options.timeout = TimeoutDuration{5.0};
+ Action action;
+ action.type = "test";
+ action.body = Buffer::FromString("");
+ std::unique_ptr results;
+ ASSERT_OK(client->DoAction(options, action, &results));
+ ASSERT_NE(results, nullptr);
+
+ std::unique_ptr result;
+ ASSERT_OK(results->Next(&result));
+ ASSERT_NE(result, nullptr);
+ ASSERT_EQ(result->body->ToString(), "Hello, world!");
+}
+#endif
+
TEST_F(TestTls, OverrideHostname) {
std::unique_ptr client;
auto client_options = FlightClientOptions();
diff --git a/cpp/src/arrow/flight/try_compile/check_tls_opts_127.cc b/cpp/src/arrow/flight/try_compile/check_tls_opts_127.cc
new file mode 100644
index 00000000000..3815d13c5ca
--- /dev/null
+++ b/cpp/src/arrow/flight/try_compile/check_tls_opts_127.cc
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Dummy file for checking if TlsCredentialsOptions exists in
+// the grpc_impl::experimental namespace. gRPC versions 1.27-1.31
+// put it here. This is for supporting disabling server
+// validation when using TLS.
+
+#include
+#include
+#include
+
+static grpc_tls_server_verification_option check(
+ const grpc_impl::experimental::TlsCredentialsOptions* options) {
+ grpc_tls_server_verification_option server_opt = options->server_verification_option();
+ return server_opt;
+}
+
+int main(int argc, const char** argv) {
+ grpc_tls_server_verification_option opt = check(nullptr);
+ return 0;
+}
diff --git a/cpp/src/arrow/flight/try_compile/check_tls_opts_132.cc b/cpp/src/arrow/flight/try_compile/check_tls_opts_132.cc
new file mode 100644
index 00000000000..d580aba6e44
--- /dev/null
+++ b/cpp/src/arrow/flight/try_compile/check_tls_opts_132.cc
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Dummy file for checking if TlsCredentialsOptions exists in
+// the grpc::experimental namespace. gRPC versions 1.32 and higher
+// put it here. This is for supporting disabling server
+// validation when using TLS.
+
+#include
+#include
+#include
+
+static grpc_tls_server_verification_option check(
+ const grpc::experimental::TlsCredentialsOptions* options) {
+ grpc_tls_server_verification_option server_opt = options->server_verification_option();
+ return server_opt;
+}
+
+int main(int argc, const char** argv) {
+ grpc_tls_server_verification_option opt = check(nullptr);
+ return 0;
+}
diff --git a/python/pyarrow/_flight.pyx b/python/pyarrow/_flight.pyx
index 9034da7b7c1..64b8548f64d 100644
--- a/python/pyarrow/_flight.pyx
+++ b/python/pyarrow/_flight.pyx
@@ -1011,6 +1011,10 @@ cdef class FlightClient(_Weakrefable):
batch that (when serialized) exceeds this limit will raise an
exception; the client can retry the write with a smaller
batch.
+ disable_server_verification : boolean optional, default False
+ A flag that indicates that, if the client is connecting
+ with TLS, that it skips server verification. If this is
+ enabled, all other TLS settings are overridden.
generic_options : list optional, default None
A list of generic (string, int or string) option tuples passed
to the underlying transport. Effect is implementation
@@ -1021,12 +1025,13 @@ cdef class FlightClient(_Weakrefable):
def __init__(self, location, *, tls_root_certs=None, cert_chain=None,
private_key=None, override_hostname=None, middleware=None,
- write_size_limit_bytes=None, generic_options=None):
+ write_size_limit_bytes=None,
+ disable_server_verification=None, generic_options=None):
if isinstance(location, (bytes, str)):
location = Location(location)
elif isinstance(location, tuple):
host, port = location
- if tls_root_certs:
+ if tls_root_certs or disable_server_verification is not None:
location = Location.for_grpc_tls(host, port)
else:
location = Location.for_grpc_tcp(host, port)
@@ -1035,11 +1040,12 @@ cdef class FlightClient(_Weakrefable):
'Location instance')
self.init(location, tls_root_certs, cert_chain, private_key,
override_hostname, middleware, write_size_limit_bytes,
- generic_options)
+ disable_server_verification, generic_options)
cdef init(self, Location location, tls_root_certs, cert_chain,
private_key, override_hostname, middleware,
- write_size_limit_bytes, generic_options):
+ write_size_limit_bytes, disable_server_verification,
+ generic_options):
cdef:
int c_port = 0
CLocation c_location = Location.unwrap(location)
@@ -1056,6 +1062,8 @@ cdef class FlightClient(_Weakrefable):
c_options.private_key = tobytes(private_key)
if override_hostname:
c_options.override_hostname = tobytes(override_hostname)
+ if disable_server_verification is not None:
+ c_options.disable_server_verification = disable_server_verification
if middleware:
for factory in middleware:
c_options.middleware.push_back(
@@ -1106,13 +1114,17 @@ cdef class FlightClient(_Weakrefable):
@classmethod
def connect(cls, location, tls_root_certs=None, cert_chain=None,
- private_key=None, override_hostname=None):
+ private_key=None, override_hostname=None,
+ disable_server_verification=None):
warnings.warn("The 'FlightClient.connect' method is deprecated, use "
"FlightClient constructor or pyarrow.flight.connect "
"function instead")
- return FlightClient(location, tls_root_certs=tls_root_certs,
- cert_chain=cert_chain, private_key=private_key,
- override_hostname=override_hostname)
+ return FlightClient(
+ location, tls_root_certs=tls_root_certs,
+ cert_chain=cert_chain, private_key=private_key,
+ override_hostname=override_hostname,
+ disable_server_verification=disable_server_verification
+ )
def authenticate(self, auth_handler, options: FlightCallOptions = None):
"""Authenticate to the server.
@@ -2516,6 +2528,9 @@ def connect(location, **kwargs):
batch that (when serialized) exceeds this limit will raise an
exception; the client can retry the write with a smaller
batch.
+ disable_server_verification : boolean or None
+ Disable verifying the server when using TLS.
+ Insecure, use with caution.
generic_options : list or None
A list of generic (string, int or string) options to pass to
the underlying transport.
diff --git a/python/pyarrow/includes/libarrow_flight.pxd b/python/pyarrow/includes/libarrow_flight.pxd
index e9179dd333c..8d79e21e1ee 100644
--- a/python/pyarrow/includes/libarrow_flight.pxd
+++ b/python/pyarrow/includes/libarrow_flight.pxd
@@ -296,6 +296,7 @@ cdef extern from "arrow/flight/api.h" namespace "arrow" nogil:
vector[shared_ptr[CClientMiddlewareFactory]] middleware
int64_t write_size_limit_bytes
vector[pair[c_string, CIntStringVariant]] generic_options
+ c_bool disable_server_verification
cdef cppclass CFlightClient" arrow::flight::FlightClient":
@staticmethod
diff --git a/python/pyarrow/tests/test_flight.py b/python/pyarrow/tests/test_flight.py
index 5d0e1c05bea..4c8c40d884b 100644
--- a/python/pyarrow/tests/test_flight.py
+++ b/python/pyarrow/tests/test_flight.py
@@ -1042,6 +1042,19 @@ def test_tls_do_get():
assert data.equals(table)
+@pytest.mark.requires_testing_data
+def test_tls_disable_server_verification():
+ """Try a simple do_get call over TLS with server verification disabled."""
+ table = simple_ints_table()
+ certs = example_tls_certs()
+
+ with ConstantFlightServer(tls_certificates=certs["certificates"]) as s:
+ client = FlightClient(('localhost', s.port),
+ disable_server_verification=True)
+ data = client.do_get(flight.Ticket(b'ints')).read_all()
+ assert data.equals(table)
+
+
@pytest.mark.requires_testing_data
def test_tls_override_hostname():
"""Check that incorrectly overriding the hostname fails."""