1
0
Fork 0
mirror of https://github.com/dragonflydb/dragonfly.git synced 2024-12-14 11:58:02 +00:00

Add tls support and config flags simulating redis configuration params

This commit is contained in:
Roman Gershman 2021-11-19 18:00:14 +02:00
parent bf714b1a64
commit 9282d96d00
7 changed files with 213 additions and 13 deletions

View file

@ -1,13 +1,13 @@
add_executable(dragonfly dfly_main.cc)
cxx_link(dragonfly base dragonfly_lib)
add_library(dragonfly_lib command_registry.cc db_slice.cc dragonfly_listener.cc
add_library(dragonfly_lib command_registry.cc config_flags.cc db_slice.cc dragonfly_listener.cc
dragonfly_connection.cc
main_service.cc engine_shard_set.cc
redis_parser.cc resp_expr.cc reply_builder.cc)
cxx_link(dragonfly_lib uring_fiber_lib
fibers_ext strings_lib http_server_lib)
fibers_ext strings_lib http_server_lib tls_lib)
add_library(dfly_test_lib test_utils.cc)
cxx_link(dfly_test_lib dragonfly_lib gtest_main_ext)

19
server/config_flags.cc Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2021, Beeri 15. All rights reserved.
// Author: Roman Gershman (romange@gmail.com)
//
#include "server/config_flags.h"
namespace dfly {
bool ValidateConfigEnum(const char* nm, const std::string& val, const ConfigEnum* ptr, unsigned len,
int* dest) {
for (unsigned i = 0; i < len; ++i) {
if (val == ptr[i].first) {
*dest = ptr[i].second;
return true;
}
}
return false;
}
} // namespace dfly

92
server/config_flags.h Normal file
View file

@ -0,0 +1,92 @@
// Copyright 2021, Beeri 15. All rights reserved.
// Author: Roman Gershman (romange@gmail.com)
//
#pragma once
#include <string.h>
#include <absl/base/macros.h>
#include "base/flags.h"
namespace dfly {
// DashStr - replaces all underscores to dash characters and keeps the rest as is.
template <unsigned N> class DashStr {
public:
DashStr(const char* s) {
memcpy(str_, s, N);
for (unsigned i = 0; i < N; ++i) {
if (str_[i] == '_')
str_[i] = '-';
}
}
const char* str() const {
return str_;
}
private:
char str_[N];
};
using ConfigEnum = std::pair<const char*, int>;
bool ValidateConfigEnum(const char* nm, const std::string& val, const ConfigEnum* ptr, unsigned len,
int* dest);
} // namespace dfly
inline bool TrueValidator(const char* nm, const std::string& val) {
return true;
}
#define DEFINE_CONFIG_VAR(type, shorttype, name, value, help, validator) \
namespace fL##shorttype { \
type FLAGS_##name = value; \
static type FLAGS_no##name = value; \
static ::dfly::DashStr<sizeof(#name)> _dash_##name(#name); \
static GFLAGS_NAMESPACE::FlagRegisterer o_##name( \
_dash_##name.str(), MAYBE_STRIPPED_HELP(help), __FILE__, &FLAGS_##name, &FLAGS_no##name); \
static const bool name##_val_reg = \
GFLAGS_NAMESPACE::RegisterFlagValidator(&FLAGS_##name, validator); \
} \
using fL##shorttype::FLAGS_##name
#define BIND_CONFIG(var) [](const char* nm, auto val) { \
var = val; \
return true;}
#define BIND_ENUM_CONFIG(enum_arr, dest_var) [](const char* nm, const std::string& val) { \
return ::dfly::ValidateConfigEnum(nm, val, enum_arr, ABSL_ARRAYSIZE(enum_arr), \
&(dest_var));}
#define CONFIG_uint64(name,val, txt, validator) \
DEFINE_CONFIG_VAR(GFLAGS_NAMESPACE::uint64, U64, name, val, txt, validator)
#define CONFIG_string(name, val, txt, validator) \
namespace fLS { \
using ::fLS::clstring; \
using ::fLS::StringFlagDestructor; \
static union { void* align; char s[sizeof(clstring)]; } s_##name[2]; \
clstring* const FLAGS_no##name = ::fLS:: \
dont_pass0toDEFINE_string(s_##name[0].s, \
val); \
static ::dfly::DashStr<sizeof(#name)> _dash_##name(#name); \
static GFLAGS_NAMESPACE::FlagRegisterer o_##name( \
_dash_##name.str(), MAYBE_STRIPPED_HELP(txt), __FILE__, \
FLAGS_no##name, new (s_##name[1].s) clstring(*FLAGS_no##name)); \
static StringFlagDestructor d_##name(s_##name[0].s, s_##name[1].s); \
extern GFLAGS_DLL_DEFINE_FLAG clstring& FLAGS_##name; \
using fLS::FLAGS_##name; \
clstring& FLAGS_##name = *FLAGS_no##name; \
static const bool name##_val_reg = \
GFLAGS_NAMESPACE::RegisterFlagValidator(&FLAGS_##name, validator); \
} \
using fLS::FLAGS_##name
#define CONFIG_enum(name, val, txt, enum_arr, dest_var) \
CONFIG_string(name, val, txt, BIND_ENUM_CONFIG(enum_arr, dest_var))

View file

@ -8,11 +8,12 @@
#include "base/io_buf.h"
#include "base/logging.h"
#include "server/command_registry.h"
#include "server/conn_context.h"
#include "server/main_service.h"
#include "server/redis_parser.h"
#include "server/conn_context.h"
#include "server/command_registry.h"
#include "util/fiber_sched_algo.h"
#include "util/tls/tls_socket.h"
using namespace util;
using namespace std;
@ -68,8 +69,7 @@ struct Connection::Shutdown {
}
};
Connection::Connection(Service* service)
: service_(service) {
Connection::Connection(Service* service, SSL_CTX* ctx) : service_(service), ctx_(ctx) {
redis_parser_.reset(new RedisParser);
}
@ -106,7 +106,19 @@ void Connection::HandleRequests() {
int val = 1;
CHECK_EQ(0, setsockopt(socket_->native_handle(), SOL_TCP, TCP_NODELAY, &val, sizeof(val)));
FiberSocketBase* peer = socket_.get();
std::unique_ptr<tls::TlsSocket> tls_sock;
if (ctx_) {
tls_sock.reset(new tls::TlsSocket(socket_.get()));
tls_sock->InitSSL(ctx_);
FiberSocketBase::accept_result aresult = tls_sock->Accept();
if (!aresult) {
LOG(WARNING) << "Error handshaking " << aresult.error().message();
return;
}
VLOG(1) << "TLS handshake succeeded";
}
FiberSocketBase* peer = tls_sock ? (FiberSocketBase*)tls_sock.get() : socket_.get();
cc_.reset(new ConnectionContext(peer, this));
cc_->shard_set = &service_->shard_set();
@ -132,7 +144,7 @@ void Connection::InputLoop(FiberSocketBase* peer) {
io_buf.CommitWrite(*recv_sz);
status = ParseRedis(&io_buf);
if (status == NEED_MORE) {
if (status == NEED_MORE) {
status = OK;
} else if (status != OK) {
break;

View file

@ -8,15 +8,17 @@
#include "base/io_buf.h"
typedef struct ssl_ctx_st SSL_CTX;
namespace dfly {
class Service;
class RedisParser;
class ConnectionContext;
class RedisParser;
class Service;
class Connection : public util::Connection {
public:
Connection(Service* service);
Connection(Service* service, SSL_CTX* ctx);
~Connection();
using error_code = std::error_code;
@ -39,9 +41,10 @@ class Connection : public util::Connection {
ParserStatus ParseRedis(base::IoBuf* buf);
std::unique_ptr<RedisParser> redis_parser_;
Service* service_;
SSL_CTX* ctx_;
std::unique_ptr<ConnectionContext> cc_;
Service* service_;
unsigned parser_error_ = 0;
struct Shutdown;

View file

@ -4,25 +4,96 @@
#include "server/dragonfly_listener.h"
#include <openssl/ssl.h>
#include "base/logging.h"
#include "server/config_flags.h"
#include "server/dragonfly_connection.h"
#include "util/proactor_pool.h"
using namespace util;
DEFINE_uint32(conn_threads, 0, "Number of threads used for handing server connections");
DEFINE_bool(tls, false, "");
CONFIG_string(tls_client_cert_file, "", "", TrueValidator);
CONFIG_string(tls_client_key_file, "", "", TrueValidator);
enum TlsClientAuth {
CL_AUTH_NO = 0,
CL_AUTH_YES = 1,
CL_AUTH_OPTIONAL = 2,
};
dfly::ConfigEnum tls_auth_clients_enum[] = {
{"no", CL_AUTH_NO},
{"yes", CL_AUTH_YES},
{"optional", CL_AUTH_OPTIONAL},
};
static int tls_auth_clients_opt = CL_AUTH_YES;
CONFIG_enum(tls_auth_clients, "yes", "", tls_auth_clients_enum, tls_auth_clients_opt);
namespace dfly {
// To connect: openssl s_client -cipher "ADH:@SECLEVEL=0" -state -crlf -connect 127.0.0.1:6380
static SSL_CTX* CreateSslCntx() {
SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
if (FLAGS_tls_client_key_file.empty()) {
// To connect - use openssl s_client -cipher with either:
// "AECDH:@SECLEVEL=0" or "ADH:@SECLEVEL=0" setting.
CHECK_EQ(1, SSL_CTX_set_cipher_list(ctx, "aNULL"));
// To allow anonymous ciphers.
SSL_CTX_set_security_level(ctx, 0);
// you can still connect with redis-cli with :
// redis-cli --tls --insecure --tls-ciphers "ADH:@SECLEVEL=0"
LOG(WARNING)
<< "tls-client-key-file not set, no keys are loaded and anonymous ciphers are enabled. "
<< "Do not use in production!";
} else { // tls_client_key_file is set.
CHECK_EQ(1,
SSL_CTX_use_PrivateKey_file(ctx, FLAGS_tls_client_key_file.c_str(), SSL_FILETYPE_PEM));
if (!FLAGS_tls_client_cert_file.empty()) {
// TO connect with redis-cli you need both tls-client-key-file and tls-client-cert-file
// loaded. Use `redis-cli --tls -p 6380 --insecure PING` to test
CHECK_EQ(1, SSL_CTX_use_certificate_chain_file(ctx, FLAGS_tls_client_cert_file.c_str()));
}
CHECK_EQ(1, SSL_CTX_set_cipher_list(ctx, "DEFAULT"));
}
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
unsigned mask = SSL_VERIFY_NONE;
// if (tls_auth_clients_opt)
// mask |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_CTX_set_verify(ctx, mask, NULL);
CHECK_EQ(1, SSL_CTX_set_dh_auto(ctx, 1));
return ctx;
}
Listener::Listener(Service* e) : engine_(e) {
if (FLAGS_tls) {
OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL);
ctx_ = CreateSslCntx();
}
}
Listener::~Listener() {
SSL_CTX_free(ctx_);
}
util::Connection* Listener::NewConnection(ProactorBase* proactor) {
return new Connection{engine_};
return new Connection{engine_, ctx_};
}
void Listener::PreShutdown() {

View file

@ -6,6 +6,8 @@
#include "util/listener_interface.h"
typedef struct ssl_ctx_st SSL_CTX;
namespace dfly {
class Service;
@ -26,6 +28,7 @@ class Listener : public util::ListenerInterface {
Service* engine_;
std::atomic_uint32_t next_id_{0};
SSL_CTX* ctx_ = nullptr;
};
} // namespace dfly