Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ jobs:
enables: --enable-dpdk
options: --cook dpdk --dpdk-machine corei7-avx

build_with_dual_tls:
name: "Test with both TLS backends"
uses: ./.github/workflows/test.yaml
strategy:
fail-fast: false
with:
compiler: clang++
standard: 23
mode: debug
options: --tls-mode=both

build_with_openssl_tls:
name: "Test with OpenSSL TLS backend only"
uses: ./.github/workflows/test.yaml
strategy:
fail-fast: false
with:
compiler: clang++
standard: 23
mode: debug
options: --tls-mode=openssl

build_with_cxx_modules:
name: "Test with C++20 modules enabled"
uses: ./.github/workflows/test.yaml
Expand Down
17 changes: 14 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,13 @@ seastar_generate_protobuf (
IN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/proto/metrics2.proto
OUT_DIR ${Seastar_GEN_BINARY_DIR}/src/proto)

set_option_if_package_is_found (Seastar_GNUTLS GnuTLS)
set_option_if_package_is_found (Seastar_OPENSSL OpenSSL)
option (Seastar_GNUTLS "Enable the GnuTLS-based TLS backend" ON)
option (Seastar_OPENSSL "Enable the OpenSSL-based TLS backend" OFF)

if (NOT Seastar_GNUTLS AND NOT Seastar_OPENSSL)
message (FATAL_ERROR "At least one TLS/crypto backend is required. Install GnuTLS or OpenSSL development packages.")
message (FATAL_ERROR "At least one TLS backend must be enabled. "
"Pass -DSeastar_GNUTLS=ON and/or -DSeastar_OPENSSL=ON, "
"or use configure.py --tls-mode=gnutls|openssl|both.")
endif ()

add_library (seastar
Expand Down Expand Up @@ -1138,6 +1140,15 @@ if (Seastar_OPENSSL)
PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif ()

if (Seastar_GNUTLS AND Seastar_OPENSSL)
# Public marker: both TLS backends are compiled in, so the active backend is
# selected at reactor startup. Code that needs to handle the no-reactor case
# (e.g. static initializers, unit tests without a reactor) can use this to
# distinguish from the single-backend builds where the backend is fixed at
# compile time and available unconditionally.
target_compile_definitions (seastar PUBLIC SEASTAR_TLS_DUAL_BACKEND)
endif ()

set_option_if_package_is_found (Seastar_IO_URING LibUring)
if (Seastar_IO_URING)
list (APPEND Seastar_PRIVATE_COMPILE_DEFINITIONS SEASTAR_HAVE_URING)
Expand Down
5 changes: 5 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ def resolve_compilers_for_compiler_cache(args, compiler_cache):
arg_parser.add_argument('--verbose', dest='verbose', action='store_true', help='Make configure output more verbose.')
arg_parser.add_argument('--scheduling-groups-count', action='store', dest='scheduling_groups_count', default='16',
help='Number of available scheduling groups in the reactor')
arg_parser.add_argument('--tls-mode', action='store', dest='tls_mode',
choices=['gnutls', 'openssl', 'both'], default='gnutls',
help='TLS backend(s) to enable: gnutls (default), openssl, or both')

add_tristate(
arg_parser,
Expand Down Expand Up @@ -289,6 +292,8 @@ def configure_mode(mode):
'-DBUILD_SHARED_LIBS={}'.format('yes' if mode in ('debug', 'dev') else 'no'),
'-DSeastar_API_LEVEL={}'.format(args.api_level),
'-DSeastar_SCHEDULING_GROUPS_COUNT={}'.format(args.scheduling_groups_count),
'-DSeastar_GNUTLS={}'.format('ON' if args.tls_mode in ('gnutls', 'both') else 'OFF'),
'-DSeastar_OPENSSL={}'.format('ON' if args.tls_mode in ('openssl', 'both') else 'OFF'),
tr(args.exclude_tests, 'EXCLUDE_TESTS_FROM_ALL'),
tr(args.exclude_apps, 'EXCLUDE_APPS_FROM_ALL'),
tr(args.exclude_demos, 'EXCLUDE_DEMOS_FROM_ALL'),
Expand Down
9 changes: 7 additions & 2 deletions include/seastar/core/reactor_config.hh
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ class network_stack_factory;
struct reactor_options : public program_options::option_group {
/// \brief Select cryptographic provider backend.
///
/// Available providers:
/// * gnutls (default)
/// In dual-backend builds (\c SEASTAR_TLS_DUAL_BACKEND), accepts:
/// * gnutls
/// * openssl
///
/// In single-backend builds the only valid value is the backend that was
/// compiled in; the option is kept for CLI compatibility but is otherwise
/// a no-op (the provider is a compile-time-fixed static singleton).
program_options::selection_value<crypto_provider_factory> crypto_provider;
/// \brief Select network stack to use.
///
Expand Down
83 changes: 57 additions & 26 deletions include/seastar/net/tls.hh
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,31 @@ class socket_address;
* Relatively thin SSL wrapper for socket IO.
* (Can be expanded to other IO forms).
*
* The current underlying mechanism is
* gnutls, however, all interfaces are kept
* agnostic, so in theory it could be replaced
* with OpenSSL or similar.
* Two underlying mechanisms are supported, GnuTLS and OpenSSL. Each one can
* be enabled or disabled at build time (\c Seastar_GNUTLS, \c Seastar_OPENSSL).
* When both are compiled in (\c SEASTAR_TLS_DUAL_BACKEND), the active backend
* is chosen at reactor startup via the \c --crypto-provider option; when only
* one is compiled in, that backend is fixed for the lifetime of the program.
*
* The interfaces here are kept agnostic so that either backend can be used
* without changes to client code.
*
* \section backend_lifetime When backend-dependent state is valid
*
* Several entry points below expose state that comes from the active backend
* (\ref error_category, \ref backend_name, the \c ERROR_* globals, and any
* function that internally creates a TLS session, credentials, or DH params).
*
* - In **single-backend** builds the backend is fixed at compile time. All
* of these entry points are valid at any time, including from static
* initializers and from unit tests that never start a reactor. The
* \c ERROR_* globals are additionally declared \c const and statically
* initialized to the backend's constants.
* - In **dual-backend** builds the backend is installed by
* \c smp::configure() at reactor startup. Calling any backend-dependent
* entry point before that point is undefined; the \c ERROR_* globals in
* particular are zero-initialized and silently read as 0. Build with a
* single TLS backend if you need them to be valid unconditionally.
*
*/
namespace tls {
Expand Down Expand Up @@ -696,39 +717,49 @@ namespace tls {
std::ostream& operator<<(std::ostream&, subject_alt_name_type);

/**
* Error handling.
* The error_category instance used by exceptions thrown by TLS.
*
* The error_category instance used by exceptions thrown by TLS
* See \ref backend_lifetime for when this is valid to call.
*/
const std::error_category& error_category();

/**
* Returns the name of the active TLS backend (e.g. "gnutls", "openssl").
*
* See \ref backend_lifetime for when this is valid to call.
*/
const char* backend_name();

/**
* The more common error codes encountered in TLS.
* Not an exhaustive list. Add exports as needed.
* The more common error codes encountered in TLS. Not an exhaustive
* list — add exports as needed.
*
* See \ref backend_lifetime for when these are valid to read.
*/
extern int ERROR_UNKNOWN_COMPRESSION_ALGORITHM;
extern int ERROR_UNKNOWN_CIPHER_TYPE;
extern int ERROR_INVALID_SESSION;
extern int ERROR_UNEXPECTED_HANDSHAKE_PACKET;
extern int ERROR_UNKNOWN_CIPHER_SUITE;
extern int ERROR_UNKNOWN_ALGORITHM;
extern int ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM;
extern int ERROR_SAFE_RENEGOTIATION_FAILED;
extern int ERROR_UNSAFE_RENEGOTIATION_DENIED;
extern int ERROR_UNKNOWN_SRP_USERNAME;
extern int ERROR_PREMATURE_TERMINATION;
extern int ERROR_PUSH;
extern int ERROR_PULL;
extern int ERROR_UNEXPECTED_PACKET;
extern int ERROR_UNSUPPORTED_VERSION;
extern int ERROR_NO_CIPHER_SUITES;
extern int ERROR_DECRYPTION_FAILED;
extern int ERROR_MAC_VERIFY_FAILED;
#ifdef SEASTAR_TLS_DUAL_BACKEND
#define SEASTAR_TLS_ERROR_QUALIFIERS extern
#else
#define SEASTAR_TLS_ERROR_QUALIFIERS extern const
#endif
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNKNOWN_COMPRESSION_ALGORITHM;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNKNOWN_CIPHER_TYPE;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_INVALID_SESSION;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNEXPECTED_HANDSHAKE_PACKET;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNKNOWN_CIPHER_SUITE;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNKNOWN_ALGORITHM;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_SAFE_RENEGOTIATION_FAILED;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNSAFE_RENEGOTIATION_DENIED;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNKNOWN_SRP_USERNAME;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_PREMATURE_TERMINATION;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_PUSH;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_PULL;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNEXPECTED_PACKET;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_UNSUPPORTED_VERSION;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_NO_CIPHER_SUITES;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_DECRYPTION_FAILED;
SEASTAR_TLS_ERROR_QUALIFIERS int ERROR_MAC_VERIFY_FAILED;
#undef SEASTAR_TLS_ERROR_QUALIFIERS
}
}

Expand Down
8 changes: 8 additions & 0 deletions include/seastar/util/assert.hh
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ namespace seastar::internal {
__PRETTY_FUNCTION__); \
} \
} while (0)

/// Like SEASTAR_ASSERT(), but only active when SEASTAR_DEBUG is defined
/// (Debug, Sanitize, and Fuzz build modes). Compiles to nothing otherwise.
#ifdef SEASTAR_DEBUG
#define SEASTAR_DEBUG_ASSERT(x) SEASTAR_ASSERT(x)
#else
#define SEASTAR_DEBUG_ASSERT(x) do { (void)sizeof(x); } while (0)
#endif
27 changes: 27 additions & 0 deletions src/core/crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,48 @@
*/

#include "crypto.hh"
#include <seastar/util/assert.hh>
#include <memory>

namespace seastar::internal::crypto {

#ifdef SEASTAR_TLS_DUAL_BACKEND

// Dual-backend build: the active provider is selected at reactor startup
// (--crypto-provider) and installed via set_provider() from smp::configure().
static std::unique_ptr<crypto_provider> the_provider;

crypto_provider& provider() {
SEASTAR_DEBUG_ASSERT(the_provider != nullptr);
return *the_provider;
}

void set_provider(std::unique_ptr<crypto_provider> p) {
SEASTAR_ASSERT(the_provider == nullptr);
the_provider = std::move(p);
provider().get_tls_backend().init_error_codes();
}

void reset_provider() {
the_provider.reset();
}

#else // single-backend

// Single-backend build: the provider is fixed at compile time. Use a
// function-local static so provider() works at any time, including before
// reactor startup. No set_provider() is compiled or needed.
crypto_provider& provider() {
#ifdef SEASTAR_HAVE_GNUTLS
static auto instance = create_gnutls_provider();
#else // SEASTAR_HAVE_OPENSSL
static auto instance = create_openssl_provider();
#endif
return *instance;
}

#endif // SEASTAR_TLS_DUAL_BACKEND

md5_hasher make_md5_hasher() {
return provider().make_md5_hasher();
}
Expand Down
34 changes: 30 additions & 4 deletions src/core/crypto.hh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public:
virtual std::unique_ptr<tls::dh_params_impl> make_dh_params(const tls::blob&, tls::x509_crt_format) = 0;

/// \brief Initialize backend-specific TLS error code constants.
///
/// In dual-backend builds this fills in the legacy \c tls::ERROR_*
/// globals from the active backend's values. In single-backend builds
/// the globals are \c const, statically initialized in the backend's
/// own translation unit, and this method is a no-op.
virtual void init_error_codes() = 0;

/// \brief Return the name of this TLS backend (e.g. "gnutls", "openssl").
Expand Down Expand Up @@ -106,16 +111,37 @@ public:

/// \brief Return the process-wide crypto provider.
///
/// Must be called after set_provider(). The returned reference
/// remains valid for the lifetime of the process.
/// In dual-backend builds, must be called after \ref set_provider(). The
/// returned reference remains valid for the lifetime of the process.
/// In single-backend builds the provider is fixed at compile time, lazily
/// created on first call, and \ref provider() works at any time including
/// from static initializers / before reactor startup.
crypto_provider& provider();

#ifdef SEASTAR_TLS_DUAL_BACKEND
/// \brief Install the process-wide crypto provider.
///
/// Must be called exactly once, before any call to provider().
/// Ownership is transferred to the crypto subsystem.
/// Must be called exactly once per \c set_provider / \c reset_provider
/// cycle, before any call to \ref provider(). Ownership is transferred
/// to the crypto subsystem.
///
/// Only compiled in dual-backend builds. In single-backend builds the
/// provider is fixed at compile time and \ref provider() handles the
/// lifetime internally.
void set_provider(std::unique_ptr<crypto_provider> p);

/// \brief Tear down the process-wide crypto provider installed by
/// \ref set_provider.
///
/// Called from \c smp::cleanup() so that a subsequent \c app::run()
/// (and the \c smp::configure() it triggers) starts from a clean slate
/// and can call \ref set_provider again. Safe to call when no provider
/// is installed.
///
/// Only compiled in dual-backend builds.
void reset_provider();
#endif

#ifdef SEASTAR_HAVE_GNUTLS
/// \brief Create a GnuTLS-backed crypto provider.
std::unique_ptr<crypto_provider> create_gnutls_provider();
Expand Down
10 changes: 9 additions & 1 deletion src/core/reactor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3930,7 +3930,6 @@ static program_options::selection_value<crypto_provider_factory> create_crypto_p
#ifdef SEASTAR_HAVE_GNUTLS
candidates.push_back({"gnutls", {new crypto_provider_factory(internal::crypto::create_gnutls_provider), deleter}, {}});
#endif

#ifdef SEASTAR_HAVE_OPENSSL
candidates.push_back({"openssl", {new crypto_provider_factory(internal::crypto::create_openssl_provider), deleter}, {}});
#endif
Expand Down Expand Up @@ -4143,6 +4142,9 @@ void smp::cleanup() noexcept {
_shard_to_numa_node_mapping = decltype(_shard_to_numa_node_mapping)();
reactor_holder.reset();
local_engine = nullptr;
#ifdef SEASTAR_TLS_DUAL_BACKEND
internal::crypto::reset_provider();
#endif
}

void smp::cleanup_cpu() {
Expand Down Expand Up @@ -4343,9 +4345,15 @@ unsigned smp::adjust_max_networking_aio_io_control_blocks(unsigned network_iocbs

void smp::configure(const smp_options& smp_opts, const reactor_options& reactor_opts)
{
#ifdef SEASTAR_TLS_DUAL_BACKEND
// Install the crypto provider before anything else, so it is
// available to all reactors from the moment they start.
//
// Only present in dual-backend builds; in single-backend builds the
// provider is a static singleton in src/core/crypto.cc and needs no
// installation step.
internal::crypto::set_provider(reactor_opts.crypto_provider.get_selected_candidate()());
#endif

bool use_transparent_hugepages = !reactor_opts.overprovisioned;

Expand Down
13 changes: 11 additions & 2 deletions src/net/tls-impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -825,8 +825,16 @@ future<> tls::force_rehandshake(connected_socket& socket) {

} // namespace seastar

// Error code globals — initialized at startup by the active backend's
// init_error_codes() method, called from smp::configure().
#ifdef SEASTAR_TLS_DUAL_BACKEND
// Dual-backend build: the error code globals are zero-initialized here and
// filled in at reactor startup by the active backend's init_error_codes()
// method (called from smp::configure() via internal::crypto::set_provider()).
// Accessing them before reactor startup silently reads as 0 — build with a
// single TLS backend if you need them to be valid at any time.
//
// In single-backend builds these globals are instead defined as `const` and
// statically initialized in the backend's own tls_<backend>.cc, so they are
// valid at any time including from static initializers.
int seastar::tls::ERROR_UNKNOWN_COMPRESSION_ALGORITHM = 0;
int seastar::tls::ERROR_UNKNOWN_CIPHER_TYPE = 0;
int seastar::tls::ERROR_INVALID_SESSION = 0;
Expand All @@ -845,3 +853,4 @@ int seastar::tls::ERROR_UNSUPPORTED_VERSION = 0;
int seastar::tls::ERROR_NO_CIPHER_SUITES = 0;
int seastar::tls::ERROR_DECRYPTION_FAILED = 0;
int seastar::tls::ERROR_MAC_VERIFY_FAILED = 0;
#endif // SEASTAR_TLS_DUAL_BACKEND
Loading
Loading