Skip to content

Commit 7c4344e

Browse files
tls: Added OpenSSL implementation
This commit adds support for using OpenSSL, instead of GnuTLS, as the TLS provider within Seastar. To support this change, the configure script has been updated to allow users to select which cryptographic provider should be used by supply `--crypto-provider` and specificying either `OpenSSL` or `GnuTLS`. The OpenSSL implementation mirrors the GnuTLS implementation. Instead of using callbacks, a custom BIO was created to handle moving data on/off of the OpenSSL SSL session into the Seastar TLS session data sinks. When compiled for OpenSSL, the `certificate_credentials::set_priority_string` method is compiled out and replaced with the following: * `set_cipher_string` * `set_ciphersuites` * `enable_server_precedence` * `set_minimum_tls_version` * `set_maximum_tls_version` These methods are specific to OpenSSL. The github actions have been updated to run the full suite of tests against both cryptographic providers. `src/net/tcp.hh` and `src/websocket/server.cc` have been updated to use OpenSSL instead of GnuTLS, depending upon the build configuration. Signed-off-by: Michael Boquard <[email protected]>
1 parent 67b493a commit 7c4344e

17 files changed

+2421
-25
lines changed

.github/workflows/test.yaml

+14-8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ on:
3333
type: string
3434
default: ''
3535
required: false
36+
crypto_provider:
37+
description: 'cryptographic provider to use'
38+
type: string
39+
default: 'GnuTLS'
40+
required: false
3641

3742
jobs:
3843
test:
@@ -83,14 +88,15 @@ jobs:
8388
if ${{ inputs.enable-ccache }}; then
8489
MAYBE_CCACHE_OPT="--ccache"
8590
fi
86-
./configure.py \
87-
--c++-standard ${{ inputs.standard }} \
88-
--compiler ${{ inputs.compiler }} \
89-
--c-compiler $CC \
90-
--mode ${{ inputs.mode }} \
91-
$MAYBE_CCACHE_OPT \
92-
${{ inputs.options }} \
93-
${{ inputs.enables }}
91+
./configure.py \
92+
--c++-standard ${{ inputs.standard }} \
93+
--compiler ${{ inputs.compiler }} \
94+
--c-compiler $CC \
95+
--mode ${{ inputs.mode }} \
96+
$MAYBE_CCACHE_OPT \
97+
${{ inputs.options }} \
98+
${{ inputs.enables }} \
99+
--crypto-provider ${{ inputs.crypto_provider }}
94100
95101
- name: Build
96102
run: cmake --build build/${{inputs.mode}}

.github/workflows/tests.yaml

+18-3
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@ concurrency:
1111

1212
jobs:
1313
regular_test:
14-
name: "Test (${{ matrix.compiler }}, C++${{ matrix.standard}}, ${{ matrix.mode }})"
14+
name: "Test (${{ matrix.compiler }}, C++${{ matrix.standard}}, ${{ matrix.mode }}, ${{ matrix.crypto_provider }})"
1515
uses: ./.github/workflows/test.yaml
1616
strategy:
1717
fail-fast: false
1818
matrix:
1919
compiler: [clang++, g++]
2020
standard: [20, 23]
2121
mode: [dev, debug, release]
22+
crypto_provider: [GnuTLS, OpenSSL]
2223
with:
2324
compiler: ${{ matrix.compiler }}
2425
standard: ${{ matrix.standard }}
2526
mode: ${{ matrix.mode }}
2627
enables: ${{ matrix.enables }}
2728
options: ${{ matrix.options }}
29+
crypto_provider: ${{ matrix.crypto_provider }}
2830
build_with_dpdk:
2931
name: "Test with DPDK enabled"
3032
uses: ./.github/workflows/test.yaml
@@ -36,8 +38,8 @@ jobs:
3638
mode: release
3739
enables: --enable-dpdk
3840
options: --cook dpdk
39-
build_with_cxx_modules:
40-
name: "Test with C++20 modules enabled"
41+
build_with_cxx_modules_gnutls:
42+
name: "Test with C++20 modules enabled (GnuTLS)"
4143
uses: ./.github/workflows/test.yaml
4244
strategy:
4345
fail-fast: false
@@ -47,3 +49,16 @@ jobs:
4749
mode: debug
4850
enables: --enable-cxx-modules
4951
enable-ccache: false
52+
crypto_provider: GnuTLS
53+
build_with_cxx_modules_openssl:
54+
name: "Test with C++20 modules enabled (OpenSSL)"
55+
uses: ./.github/workflows/test.yaml
56+
strategy:
57+
fail-fast: false
58+
with:
59+
compiler: clang++
60+
standard: 23
61+
mode: debug
62+
enables: --enable-cxx-modules
63+
enable-ccache: false
64+
crypto_provider: OpenSSL

CMakeLists.txt

+16-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ if (NOT Seastar_SCHEDULING_GROUPS_COUNT MATCHES "^[1-9][0-9]*")
8989
message(FATAL_ERROR "Seastar_SCHEDULING_GROUPS_COUNT must be a positive number (${Seastar_SCHEDULING_GROUPS_COUNT})")
9090
endif ()
9191

92+
option (Seastar_USE_OPENSSL
93+
"Use OpenSSL rather than GnuTLS for cryptographic operations, including TLS"
94+
OFF)
95+
96+
if (Seastar_USE_OPENSSL)
97+
set(Seastar_USE_GNUTLS OFF)
98+
else()
99+
set(Seastar_USE_GNUTLS ON)
100+
endif ()
101+
92102
#
93103
# Add a dev build type.
94104
#
@@ -753,13 +763,14 @@ add_library (seastar
753763
src/net/native-stack-impl.hh
754764
src/net/native-stack.cc
755765
src/net/net.cc
766+
$<$<BOOL:${Seastar_USE_OPENSSL}>:src/net/ossl.cc>
756767
src/net/packet.cc
757768
src/net/posix-stack.cc
758769
src/net/proxy.cc
759770
src/net/socket_address.cc
760771
src/net/stack.cc
761772
src/net/tcp.cc
762-
src/net/tls.cc
773+
$<$<BOOL:${Seastar_USE_GNUTLS}>:src/net/tls.cc>
763774
src/net/tls-impl.cc
764775
src/net/tls-impl.hh
765776
src/net/udp.cc
@@ -858,7 +869,9 @@ target_link_libraries (seastar
858869
SourceLocation::source_location
859870
PRIVATE
860871
${CMAKE_DL_LIBS}
861-
GnuTLS::gnutls
872+
$<$<BOOL:${Seastar_USE_GNUTLS}>:GnuTLS::gnutls>
873+
$<$<BOOL:${Seastar_USE_OPENSSL}>:OpenSSL::SSL>
874+
$<$<BOOL:${Seastar_USE_OPENSSL}>:OpenSSL::Crypto>
862875
StdAtomic::atomic
863876
lksctp-tools::lksctp-tools
864877
protobuf::libprotobuf
@@ -915,6 +928,7 @@ include (CTest)
915928
target_compile_definitions(seastar
916929
PUBLIC
917930
SEASTAR_API_LEVEL=${Seastar_API_LEVEL}
931+
$<$<BOOL:${Seastar_USE_OPENSSL}>:SEASTAR_USE_OPENSSL>
918932
$<$<BOOL:${BUILD_SHARED_LIBS}>:SEASTAR_BUILD_SHARED_LIBS>)
919933

920934
target_compile_features(seastar

cmake/SeastarDependencies.cmake

+6-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ macro (seastar_find_dependencies)
9191
GnuTLS
9292
LibUring
9393
LinuxMembarrier
94+
OpenSSL
9495
# Protobuf is searched manually.
9596
Sanitizers
9697
SourceLocation
@@ -124,8 +125,12 @@ macro (seastar_find_dependencies)
124125
VERSION 8.1.1)
125126
seastar_set_dep_args (lz4 REQUIRED
126127
VERSION 1.7.3)
128+
seastar_set_dep_args (OpenSSL
129+
VERSION 3.0.0
130+
OPTION ${Seastar_USE_OPENSSL})
127131
seastar_set_dep_args (GnuTLS REQUIRED
128-
VERSION 3.3.26)
132+
VERSION 3.3.26
133+
OPTION ${Seastar_USE_GNUTLS})
129134
seastar_set_dep_args (LibUring
130135
VERSION 2.0
131136
OPTION ${Seastar_IO_URING})

configure.py

+7
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ def standard_supported(standard, compiler='g++'):
8989
arg_parser.add_argument('--verbose', dest='verbose', action='store_true', help='Make configure output more verbose.')
9090
arg_parser.add_argument('--scheduling-groups-count', action='store', dest='scheduling_groups_count', default='16',
9191
help='Number of available scheduling groups in the reactor')
92+
arg_parser.add_argument('--crypto-provider', dest='crypto_provider', choices=seastar_cmake.SUPPORTED_CRYPTO_PROVIDERS,
93+
default='GnuTLS', help='The cryptographic provider ot use')
94+
arg_parser.add_argument('--openssl-root-dir', dest='openssl_root_dir', help="Root directory for OpenSSL library")
9295

9396
add_tristate(
9497
arg_parser,
@@ -191,6 +194,7 @@ def configure_mode(mode):
191194
'-DBUILD_SHARED_LIBS={}'.format('yes' if mode in ('debug', 'dev') else 'no'),
192195
'-DSeastar_API_LEVEL={}'.format(args.api_level),
193196
'-DSeastar_SCHEDULING_GROUPS_COUNT={}'.format(args.scheduling_groups_count),
197+
'-DSeastar_USE_OPENSSL={}'.format('yes' if args.crypto_provider == 'OpenSSL' else 'no'),
194198
tr(args.exclude_tests, 'EXCLUDE_TESTS_FROM_ALL'),
195199
tr(args.exclude_apps, 'EXCLUDE_APPS_FROM_ALL'),
196200
tr(args.exclude_demos, 'EXCLUDE_DEMOS_FROM_ALL'),
@@ -211,6 +215,9 @@ def configure_mode(mode):
211215
tr(args.debug_shared_ptr, 'DEBUG_SHARED_PTR', value_when_none='default'),
212216
]
213217

218+
if args.openssl_root_dir is not None:
219+
TRANSLATED_ARGS.appen(f'-DOPENSSL_ROOT_DIR={args.openssl_root_dir}')
220+
214221
ingredients_to_cook = set(args.cook)
215222

216223
if args.dpdk:

include/seastar/net/tcp.hh

+36
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@
3030
#include <random>
3131
#include <stdexcept>
3232
#include <system_error>
33+
#ifdef SEASTAR_USE_OPENSSL
34+
#include <openssl/evp.h>
35+
#else
3336
#include <gnutls/crypto.h>
3437
#endif
38+
#endif
3539
#include <seastar/core/shared_ptr.hh>
3640
#include <seastar/core/queue.hh>
3741
#include <seastar/core/semaphore.hh>
@@ -42,6 +46,7 @@
4246
#include <seastar/net/ip.hh>
4347
#include <seastar/net/const.hh>
4448
#include <seastar/net/packet-util.hh>
49+
#include <seastar/util/defer.hh>
4550
#include <seastar/util/std-compat.hh>
4651

4752
namespace seastar {
@@ -2084,6 +2089,36 @@ tcp_seq tcp<InetTraits>::tcb::get_isn() {
20842089
// ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
20852090
// M is the 4 microsecond timer
20862091
using namespace std::chrono;
2092+
#ifdef SEASTAR_USE_OPENSSL
2093+
uint32_t hash[8];
2094+
hash[0] = _local_ip.ip;
2095+
hash[1] = _foreign_ip.ip;
2096+
hash[2] = (_local_port << 16) + _foreign_port;
2097+
unsigned int hash_size = sizeof(hash);
2098+
2099+
// Why SHA-256 for OpenSSL vs MD5?
2100+
// MD5 may be disabled if OpenSSL is in FIPS mode, also some bench testing
2101+
// has shown that the SHA-256 performance is equivalent or better than MD5
2102+
// as SHA256 is hardware accelerated on most modern CPU architectures
2103+
auto md_ptr = EVP_MD_fetch(nullptr, "SHA256", nullptr);
2104+
assert(md_ptr);
2105+
auto free_md_ptr = defer([&]() noexcept { EVP_MD_free(md_ptr); });
2106+
assert(hash_size == static_cast<unsigned int>(EVP_MD_get_size(md_ptr)));
2107+
auto md_ctx = EVP_MD_CTX_new();
2108+
assert(md_ctx);
2109+
auto free_md_ctx = defer([&]() noexcept { EVP_MD_CTX_free(md_ctx); });
2110+
auto res = EVP_DigestInit(md_ctx, md_ptr);
2111+
assert(1 == res);
2112+
res = EVP_DigestUpdate(
2113+
md_ctx, hash, 3 * sizeof(hash[0]));
2114+
assert(1 == res);
2115+
res = EVP_DigestUpdate(
2116+
md_ctx, _isn_secret.key, sizeof(_isn_secret.key));
2117+
assert(1 == res);
2118+
res = EVP_DigestFinal_ex(
2119+
md_ctx, reinterpret_cast<unsigned char *>(hash), &hash_size);
2120+
assert(1 == res);
2121+
#else
20872122
uint32_t hash[4];
20882123
hash[0] = _local_ip.ip;
20892124
hash[1] = _foreign_ip.ip;
@@ -2096,6 +2131,7 @@ tcp_seq tcp<InetTraits>::tcb::get_isn() {
20962131
// reuse "hash" for the output of digest
20972132
assert(sizeof(hash) == gnutls_hash_get_len(GNUTLS_DIG_MD5));
20982133
gnutls_hash_deinit(md5_hash_handle, hash);
2134+
#endif
20992135
auto seq = hash[0];
21002136
auto m = duration_cast<microseconds>(clock_type::now().time_since_epoch());
21012137
seq += m.count() / 4;

include/seastar/net/tls.hh

+72
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ namespace tls {
115115
shared_ptr<impl> _impl;
116116
};
117117

118+
enum class tls_version {
119+
tlsv1_0,
120+
tlsv1_1,
121+
tlsv1_2,
122+
tlsv1_3
123+
};
124+
125+
std::string_view format_as(tls_version);
126+
std::ostream& operator<<(std::ostream&, const tls_version&);
127+
118128
class abstract_credentials {
119129
protected:
120130
abstract_credentials() = default;
@@ -202,13 +212,53 @@ namespace tls {
202212

203213
// TODO add methods for certificate verification
204214

215+
#ifndef SEASTAR_USE_OPENSSL
205216
/**
206217
* TLS handshake priority string. See gnutls docs and syntax at
207218
* https://gnutls.org/manual/html_node/Priority-Strings.html
208219
*
209220
* Allows specifying order and allowance for handshake alg.
210221
*/
211222
void set_priority_string(const sstring&);
223+
#endif
224+
225+
#ifdef SEASTAR_USE_OPENSSL
226+
/**
227+
* Used to set the cipher string for TLS versions 1.2 and below
228+
*
229+
* See https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_cipher_list.html
230+
* for documentation on the format of the cipher list string
231+
*/
232+
void set_cipher_string(const sstring&);
233+
234+
/**
235+
* Used to set the cipher suites to use for TLSv1.3
236+
*
237+
* See https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_ciphersuites.html
238+
* for documentation on the format of the ciphersuites string.
239+
*/
240+
void set_ciphersuites(const sstring&);
241+
242+
/**
243+
* Call this when you want to enable server precedence when
244+
* negotitating the TLS handshake. Client precedence is on
245+
* by default.
246+
*/
247+
void enable_server_precedence();
248+
/**
249+
* @brief Set the minimum tls version for this connection
250+
*
251+
* If unset, will default to the minimum of the underlying
252+
* implementation
253+
*/
254+
void set_minimum_tls_version(tls_version);
255+
/**
256+
* @brief Set the maximum tls version for this connection
257+
*
258+
* If unset, will default to the maximum of the underly implementation
259+
*/
260+
void set_maximum_tls_version(tls_version);
261+
#endif
212262

213263
/**
214264
* Register a callback for receiving Distinguished Name (DN) information
@@ -319,7 +369,17 @@ namespace tls {
319369
future<> set_system_trust();
320370
void set_client_auth(client_auth);
321371
void set_session_resume_mode(session_resume_mode);
372+
#ifndef SEASTAR_USE_OPENSSL
322373
void set_priority_string(const sstring&);
374+
#endif
375+
376+
#ifdef SEASTAR_USE_OPENSSL
377+
void set_cipher_string(const sstring&);
378+
void set_ciphersuites(const sstring&);
379+
void enable_server_precedence();
380+
void set_minimum_tls_version(tls_version);
381+
void set_maximum_tls_version(tls_version);
382+
#endif
323383

324384
void apply_to(certificate_credentials&) const;
325385

@@ -337,6 +397,11 @@ namespace tls {
337397
client_auth _client_auth = client_auth::NONE;
338398
session_resume_mode _session_resume_mode = session_resume_mode::NONE;
339399
sstring _priority;
400+
sstring _cipher_string;
401+
sstring _ciphersuites;
402+
bool _enable_server_precedence = false;
403+
std::optional<tls_version> _min_tls_version;
404+
std::optional<tls_version> _max_tls_version;
340405
};
341406

342407
using session_data = std::vector<uint8_t>;
@@ -588,3 +653,10 @@ template <> struct fmt::formatter<seastar::tls::subject_alt_name> : fmt::formatt
588653
return fmt::format_to(ctx.out(), "{}={}", name.type, name.value);
589654
}
590655
};
656+
657+
template <> struct fmt::formatter<seastar::tls::tls_version> : fmt::formatter<string_view> {
658+
template<typename FormatContext>
659+
auto format(seastar::tls::tls_version version, FormatContext& ctx) const {
660+
return fmt::format_to(ctx.out(), "{}", format_as(version));
661+
}
662+
};

install-dependencies.sh

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ debian_packages=(
4444
libpciaccess-dev
4545
libprotobuf-dev
4646
libsctp-dev
47+
libssl-dev
4748
libtool
4849
liburing-dev
4950
libxml2-dev
@@ -96,6 +97,7 @@ redhat_packages=(
9697
meson
9798
numactl-devel
9899
openssl
100+
openssl-devel
99101
protobuf-compiler
100102
protobuf-devel
101103
python3
@@ -227,6 +229,7 @@ opensuse_packages=(
227229
meson
228230
ninja
229231
openssl
232+
openssl-devel
230233
protobuf-devel
231234
python3-PyYAML
232235
ragel

0 commit comments

Comments
 (0)