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:
parent
bf714b1a64
commit
9282d96d00
7 changed files with 213 additions and 13 deletions
|
@ -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
19
server/config_flags.cc
Normal 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
92
server/config_flags.h
Normal 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))
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue