Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge OpenSSL implementation into Seastar v24.2.x #119

Merged
18 changes: 15 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ if (NOT Seastar_SCHEDULING_GROUPS_COUNT MATCHES "^[1-9][0-9]*")
message(FATAL_ERROR "Seastar_SCHEDULING_GROUPS_COUNT must be a positive number (${Seastar_SCHEDULING_GROUPS_COUNT})")
endif ()

#
option (Seastar_WITH_OSSL
"Use OpenSSL for underlying TLS mechanism"
OFF)

# Add a dev build type.
#
# All pre-defined build modes include optimizations or debug info,
Expand Down Expand Up @@ -399,6 +402,10 @@ find_package (ragel 6.10 REQUIRED)
find_package (Threads REQUIRED)
find_package (PthreadSetName REQUIRED)
find_package (Valgrind REQUIRED)
# TODO(rob) - Add as cooking_ingredient instead
if (Seastar_WITH_OSSL)
find_package (OpenSSL 3.0.0 REQUIRED)
endif()

cmake_dependent_option (Seastar_LOGGER_COMPILE_TIME_FMT
"Enable the compile-time {fmt} check when formatting logging messages" ON
Expand Down Expand Up @@ -754,7 +761,9 @@ add_library (seastar
src/net/socket_address.cc
src/net/stack.cc
src/net/tcp.cc
src/net/tls.cc
$<$<NOT:$<BOOL:${Seastar_WITH_OSSL}>>:src/net/tls.cc>
$<$<BOOL:${Seastar_WITH_OSSL}>:src/net/ossl.cc>
src/net/tls-impl.cc
src/net/udp.cc
src/net/unix_address.cc
src/net/virtio.cc
Expand Down Expand Up @@ -830,7 +839,9 @@ target_link_libraries (seastar
SourceLocation::source_location
PRIVATE
${CMAKE_DL_LIBS}
GnuTLS::gnutls
$<$<NOT:$<BOOL:${Seastar_WITH_OSSL}>>:GnuTLS::gnutls>
$<$<BOOL:${Seastar_WITH_OSSL}>:OpenSSL::SSL>
$<$<BOOL:${Seastar_WITH_OSSL}>:OpenSSL::Crypto>
StdAtomic::atomic
lksctp-tools::lksctp-tools
protobuf::libprotobuf
Expand Down Expand Up @@ -900,6 +911,7 @@ target_compile_options (seastar
target_compile_definitions(seastar
PUBLIC
SEASTAR_API_LEVEL=${Seastar_API_LEVEL}
$<$<BOOL:${Seastar_WITH_OSSL}>:SEASTAR_WITH_TLS_OSSL=1>
$<$<BOOL:${BUILD_SHARED_LIBS}>:SEASTAR_BUILD_SHARED_LIBS>)

target_compile_features(seastar
Expand Down
3 changes: 3 additions & 0 deletions cmake/SeastarDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ macro (seastar_find_dependencies)
GnuTLS
LibUring
LinuxMembarrier
OpenSSL
# Protobuf is searched manually.
Sanitizers
SourceLocation
Expand Down Expand Up @@ -130,6 +131,8 @@ macro (seastar_find_dependencies)
VERSION 1.7.3)
seastar_set_dep_args (GnuTLS REQUIRED
VERSION 3.3.26)
seastar_set_dep_args (OpenSSL REQUIRED
VERSION 3.0.0)
seastar_set_dep_args (LibUring
VERSION 2.0
OPTION ${Seastar_IO_URING})
Expand Down
7 changes: 7 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ def standard_supported(standard, compiler='g++'):
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('--ssl-provider', dest='ssl_provider', choices=seastar_cmake.SUPPORTED_SSL_PROVIDERS, default='GnuTLS',
help='The SSL provider to use')
arg_parser.add_argument('--openssl-root-dir', dest='openssl_root_dir', help='Root directory for OpenSSL')

add_tristate(
arg_parser,
Expand Down Expand Up @@ -216,6 +219,7 @@ 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_WITH_OSSL={}'.format('yes' if args.ssl_provider == 'OpenSSL' else 'no'),
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 All @@ -236,6 +240,9 @@ def configure_mode(mode):
tr(args.debug_shared_ptr, 'DEBUG_SHARED_PTR', value_when_none='default'),
]

if args.openssl_root_dir is not None:
TRANSLATED_ARGS.append(f'-DOPENSSL_ROOT_DIR={args.openssl_root_dir}')

ingredients_to_cook = set(args.cook)

if args.dpdk:
Expand Down
124 changes: 70 additions & 54 deletions demos/tls_echo_server.hh
Original file line number Diff line number Diff line change
Expand Up @@ -46,70 +46,86 @@ class echoserver {
seastar::gate _gate;
bool _stopped = false;
bool _verbose = false;

future<stop_iteration> run_once() {
if (_stopped) {
return make_ready_future<stop_iteration>(stop_iteration::yes);
}
return with_gate(_gate, [this] {
return _socket.accept().then([this](accept_result ar) {
::connected_socket s = std::move(ar.connection);
socket_address a = std::move(ar.remote_address);
if (_verbose) {
std::cout << "Got connection from "<< a << std::endl;
}
auto strms = make_lw_shared<streams>(std::move(s));
return repeat([strms, this]() {
return strms->in.read().then([this, strms](temporary_buffer<char> buf) {
if (buf.empty()) {
if (_verbose) {
std::cout << "EOM" << std::endl;
}
return make_ready_future<stop_iteration>(stop_iteration::yes);
}
sstring tmp(buf.begin(), buf.end());
if (_verbose) {
std::cout << "Read " << tmp.size() << "B" << std::endl;
}
return strms->out.write(tmp).then([strms]() {
return strms->out.flush();
}).then([] {
return make_ready_future<stop_iteration>(stop_iteration::no);
});
});
}).then([strms]{
return strms->out.close();
}).handle_exception([](auto ep) {
std::cout << "Exception: " << ep << std::endl;
}).finally([this, strms]{
if (_verbose) {
std::cout << "Ending session" << std::endl;
}
return strms->in.close();
});
}).handle_exception([this](auto ep) {
if (!_stopped) {
std::cerr << "Error: " << ep << std::endl;
}
}).then([this] {
return make_ready_future<stop_iteration>(_stopped ? stop_iteration::yes : stop_iteration::no);
});
});
}
public:
echoserver(bool verbose = false)
: _certs(make_shared<tls::server_credentials>(make_shared<tls::dh_params>()))
, _verbose(verbose)
{}

future<> listen(socket_address addr, sstring crtfile, sstring keyfile, tls::client_auth ca = tls::client_auth::NONE) {
_certs->set_client_auth(ca);
return _certs->set_x509_key_file(crtfile, keyfile, tls::x509_crt_format::PEM).then([this, addr] {
::listen_options opts;
opts.reuse_address = true;
future<> listen(socket_address addr, sstring crtfile, sstring keyfile, sstring cafile) {
_certs->set_dn_verification_callback([](seastar::tls::session_type, sstring subject, sstring issuer){
std::cout << "DN Verification callback, subject: " << subject << " issuer: " << issuer << std::endl;
});
auto f = make_ready_future();
auto cauth = tls::client_auth::NONE;
if (cafile != "") {
cauth = tls::client_auth::REQUIRE;
f = _certs->set_x509_trust_file(cafile, tls::x509_crt_format::PEM);
}
_certs->set_client_auth(cauth);
return f.then([this, addr, crtfile, keyfile] {
return _certs->set_x509_key_file(crtfile, keyfile, tls::x509_crt_format::PEM).then([this, addr] {
::listen_options opts;
opts.reuse_address = true;

_socket = tls::listen(_certs, addr, opts);
_socket = tls::listen(_certs, addr, opts);

// Listen in background.
(void)repeat([this] {
if (_stopped) {
return make_ready_future<stop_iteration>(stop_iteration::yes);
}
return with_gate(_gate, [this] {
return _socket.accept().then([this](accept_result ar) {
::connected_socket s = std::move(ar.connection);
socket_address a = std::move(ar.remote_address);
if (_verbose) {
std::cout << "Got connection from "<< a << std::endl;
}
auto strms = make_lw_shared<streams>(std::move(s));
return repeat([strms, this]() {
return strms->in.read().then([this, strms](temporary_buffer<char> buf) {
if (buf.empty()) {
if (_verbose) {
std::cout << "EOM" << std::endl;
}
return make_ready_future<stop_iteration>(stop_iteration::yes);
}
sstring tmp(buf.begin(), buf.end());
if (_verbose) {
std::cout << "Read " << tmp.size() << "B" << std::endl;
}
return strms->out.write(tmp).then([strms]() {
return strms->out.flush();
}).then([] {
return make_ready_future<stop_iteration>(stop_iteration::no);
});
});
}).then([strms]{
return strms->out.close();
}).handle_exception([](auto ep) {
}).finally([this, strms]{
if (_verbose) {
std::cout << "Ending session" << std::endl;
}
return strms->in.close();
});
}).handle_exception([this](auto ep) {
if (!_stopped) {
std::cerr << "Error: " << ep << std::endl;
}
}).then([this] {
return make_ready_future<stop_iteration>(_stopped ? stop_iteration::yes : stop_iteration::no);
// Listen in background.
(void)repeat([this] {
return run_once();
});
});
return make_ready_future();
});
return make_ready_future();
});
}

Expand Down
4 changes: 3 additions & 1 deletion demos/tls_echo_server_demo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ int main(int ac, char** av) {
app.add_options()
("port", bpo::value<uint16_t>()->default_value(10000), "Server port")
("address", bpo::value<std::string>()->default_value("127.0.0.1"), "Server address")
("ca,a", bpo::value<std::string>()->default_value(""), "Server CA chain file")
("cert,c", bpo::value<std::string>()->required(), "Server certificate file")
("key,k", bpo::value<std::string>()->required(), "Certificate key")
("verbose,v", bpo::value<bool>()->default_value(false)->implicit_value(true), "Verbose")
;
return app.run_deprecated(ac, av, [&] {
auto&& config = app.configuration();
uint16_t port = config["port"].as<uint16_t>();
auto ca = config["ca"].as<std::string>();
auto crt = config["cert"].as<std::string>();
auto key = config["key"].as<std::string>();
auto addr = config["address"].as<std::string>();
Expand All @@ -52,7 +54,7 @@ int main(int ac, char** av) {

auto server = ::make_shared<seastar::sharded<echoserver>>();
return server->start(verbose).then([=]() {
return server->invoke_on_all(&echoserver::listen, socket_address(ia), sstring(crt), sstring(key), tls::client_auth::NONE);
return server->invoke_on_all(&echoserver::listen, socket_address(ia), sstring(crt), sstring(key), sstring(ca));
}).handle_exception([=](auto e) {
std::cerr << "Error: " << e << std::endl;
engine().exit(1);
Expand Down
11 changes: 11 additions & 0 deletions demos/tls_simple_client_demo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ int main(int ac, char** av) {
("port", bpo::value<uint16_t>()->default_value(10000), "Remote port")
("address", bpo::value<std::string>()->default_value("127.0.0.1"), "Remote address")
("trust,t", bpo::value<std::string>(), "Trust store")
("certificate", bpo::value<std::string>(), "Certficiate")
("key,k", bpo::value<std::string>(), "Private Keyfile")
("msg,m", bpo::value<std::string>(), "Message to send")
("bytes,b", bpo::value<size_t>()->default_value(512), "Use random bytes of length as message")
("iterations,i", bpo::value<size_t>()->default_value(1), "Repeat X times")
Expand Down Expand Up @@ -67,6 +69,15 @@ int main(int ac, char** av) {
f = certs->set_x509_trust_file(config["trust"].as<std::string>(), tls::x509_crt_format::PEM);
}

if (config.count("certificate") && config.count("key")) {
f = f.then([certs,
cert = config["certificate"].as<std::string>(),
key = config["key"].as<std::string>()]{
return certs->set_x509_key_file(cert, key, tls::x509_crt_format::PEM);
});
}


seastar::shared_ptr<sstring> msg;

if (config.count("msg")) {
Expand Down
32 changes: 32 additions & 0 deletions include/seastar/net/tcp.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@
#include <random>
#include <stdexcept>
#include <system_error>
#ifdef SEASTAR_WITH_TLS_OSSL
#include <openssl/evp.h>
#else
#include <gnutls/crypto.h>
#endif
#endif
#include <seastar/core/shared_ptr.hh>
#include <seastar/core/queue.hh>
#include <seastar/core/semaphore.hh>
Expand All @@ -42,6 +46,7 @@
#include <seastar/net/ip.hh>
#include <seastar/net/const.hh>
#include <seastar/net/packet-util.hh>
#include <seastar/util/defer.hh>
#include <seastar/util/std-compat.hh>

namespace seastar {
Expand Down Expand Up @@ -2086,6 +2091,32 @@ tcp_seq tcp<InetTraits>::tcb::get_isn() {
// ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
// M is the 4 microsecond timer
using namespace std::chrono;
#ifdef SEASTAR_WITH_TLS_OSSL
uint32_t hash[8];
hash[0] = _local_ip.ip;
hash[1] = _foreign_ip.ip;
hash[2] = (_local_port << 16) + _foreign_port;
unsigned int hash_size = sizeof(hash);

// Why SHA-256 for OpenSSL vs MD5?
// MD5 may be disabled if OpenSSL is in FIPS mode, also some bench testing
// has shown that the SHA-256 performance is equivalent or better than MD5
// as SHA256 is hardware accelerated on most modern CPU architectures
auto md_ptr = EVP_MD_fetch(nullptr, "SHA256", nullptr);
assert(md_ptr);
auto free_md_ptr = defer([&]() noexcept { EVP_MD_free(md_ptr); });
assert(hash_size == static_cast<unsigned int>(EVP_MD_get_size(md_ptr)));
auto md_ctx = EVP_MD_CTX_new();
assert(md_ctx);
auto free_md_ctx = defer([&]() noexcept { EVP_MD_CTX_free(md_ctx); });
assert(1 == EVP_DigestInit(md_ctx, md_ptr));
assert(1 == EVP_DigestUpdate(
md_ctx, hash, 3 * sizeof(hash[0])));
assert(1 == EVP_DigestUpdate(
md_ctx, _isn_secret.key, sizeof(_isn_secret.key)));
assert(1 == EVP_DigestFinal_ex(
md_ctx, reinterpret_cast<unsigned char *>(hash), &hash_size));
#else
uint32_t hash[4];
hash[0] = _local_ip.ip;
hash[1] = _foreign_ip.ip;
Expand All @@ -2098,6 +2129,7 @@ tcp_seq tcp<InetTraits>::tcb::get_isn() {
// reuse "hash" for the output of digest
assert(sizeof(hash) == gnutls_hash_get_len(GNUTLS_DIG_MD5));
gnutls_hash_deinit(md5_hash_handle, hash);
#endif
auto seq = hash[0];
auto m = duration_cast<microseconds>(clock_type::now().time_since_epoch());
seq += m.count() / 4;
Expand Down
Loading