mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Merge pull request #63985 from ianton-ru/protocols-tls-extend-params
More parameters for tls layer in protocols
This commit is contained in:
commit
fb2f0e78f7
@ -17,6 +17,7 @@
|
||||
#ifndef NetSSL_SSLManager_INCLUDED
|
||||
#define NetSSL_SSLManager_INCLUDED
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include "Poco/BasicEvent.h"
|
||||
@ -219,6 +220,13 @@ namespace Net
|
||||
/// Unless initializeClient() has been called, the first call to this method initializes the default Context
|
||||
/// from the application configuration.
|
||||
|
||||
Context::Ptr getCustomServerContext(const std::string & name);
|
||||
/// Return custom Context used by the server.
|
||||
|
||||
Context::Ptr setCustomServerContext(const std::string & name, Context::Ptr ctx);
|
||||
/// Set custom Context used by the server.
|
||||
/// Return pointer on inserted Context or on old Context if exists.
|
||||
|
||||
PrivateKeyPassphraseHandlerPtr serverPassphraseHandler();
|
||||
/// Returns the configured passphrase handler of the server. If none is set, the method will create a default one
|
||||
/// from an application configuration.
|
||||
@ -258,6 +266,40 @@ namespace Net
|
||||
static const std::string CFG_SERVER_PREFIX;
|
||||
static const std::string CFG_CLIENT_PREFIX;
|
||||
|
||||
static const std::string CFG_PRIV_KEY_FILE;
|
||||
static const std::string CFG_CERTIFICATE_FILE;
|
||||
static const std::string CFG_CA_LOCATION;
|
||||
static const std::string CFG_VER_MODE;
|
||||
static const Context::VerificationMode VAL_VER_MODE;
|
||||
static const std::string CFG_VER_DEPTH;
|
||||
static const int VAL_VER_DEPTH;
|
||||
static const std::string CFG_ENABLE_DEFAULT_CA;
|
||||
static const bool VAL_ENABLE_DEFAULT_CA;
|
||||
static const std::string CFG_CIPHER_LIST;
|
||||
static const std::string CFG_CYPHER_LIST; // for backwards compatibility
|
||||
static const std::string VAL_CIPHER_LIST;
|
||||
static const std::string CFG_PREFER_SERVER_CIPHERS;
|
||||
static const std::string CFG_DELEGATE_HANDLER;
|
||||
static const std::string VAL_DELEGATE_HANDLER;
|
||||
static const std::string CFG_CERTIFICATE_HANDLER;
|
||||
static const std::string VAL_CERTIFICATE_HANDLER;
|
||||
static const std::string CFG_CACHE_SESSIONS;
|
||||
static const std::string CFG_SESSION_ID_CONTEXT;
|
||||
static const std::string CFG_SESSION_CACHE_SIZE;
|
||||
static const std::string CFG_SESSION_TIMEOUT;
|
||||
static const std::string CFG_EXTENDED_VERIFICATION;
|
||||
static const std::string CFG_REQUIRE_TLSV1;
|
||||
static const std::string CFG_REQUIRE_TLSV1_1;
|
||||
static const std::string CFG_REQUIRE_TLSV1_2;
|
||||
static const std::string CFG_DISABLE_PROTOCOLS;
|
||||
static const std::string CFG_DH_PARAMS_FILE;
|
||||
static const std::string CFG_ECDH_CURVE;
|
||||
|
||||
#ifdef OPENSSL_FIPS
|
||||
static const std::string CFG_FIPS_MODE;
|
||||
static const bool VAL_FIPS_MODE;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static int verifyClientCallback(int ok, X509_STORE_CTX * pStore);
|
||||
/// The return value of this method defines how errors in
|
||||
@ -314,39 +356,7 @@ namespace Net
|
||||
InvalidCertificateHandlerPtr _ptrClientCertificateHandler;
|
||||
Poco::FastMutex _mutex;
|
||||
|
||||
static const std::string CFG_PRIV_KEY_FILE;
|
||||
static const std::string CFG_CERTIFICATE_FILE;
|
||||
static const std::string CFG_CA_LOCATION;
|
||||
static const std::string CFG_VER_MODE;
|
||||
static const Context::VerificationMode VAL_VER_MODE;
|
||||
static const std::string CFG_VER_DEPTH;
|
||||
static const int VAL_VER_DEPTH;
|
||||
static const std::string CFG_ENABLE_DEFAULT_CA;
|
||||
static const bool VAL_ENABLE_DEFAULT_CA;
|
||||
static const std::string CFG_CIPHER_LIST;
|
||||
static const std::string CFG_CYPHER_LIST; // for backwards compatibility
|
||||
static const std::string VAL_CIPHER_LIST;
|
||||
static const std::string CFG_PREFER_SERVER_CIPHERS;
|
||||
static const std::string CFG_DELEGATE_HANDLER;
|
||||
static const std::string VAL_DELEGATE_HANDLER;
|
||||
static const std::string CFG_CERTIFICATE_HANDLER;
|
||||
static const std::string VAL_CERTIFICATE_HANDLER;
|
||||
static const std::string CFG_CACHE_SESSIONS;
|
||||
static const std::string CFG_SESSION_ID_CONTEXT;
|
||||
static const std::string CFG_SESSION_CACHE_SIZE;
|
||||
static const std::string CFG_SESSION_TIMEOUT;
|
||||
static const std::string CFG_EXTENDED_VERIFICATION;
|
||||
static const std::string CFG_REQUIRE_TLSV1;
|
||||
static const std::string CFG_REQUIRE_TLSV1_1;
|
||||
static const std::string CFG_REQUIRE_TLSV1_2;
|
||||
static const std::string CFG_DISABLE_PROTOCOLS;
|
||||
static const std::string CFG_DH_PARAMS_FILE;
|
||||
static const std::string CFG_ECDH_CURVE;
|
||||
|
||||
#ifdef OPENSSL_FIPS
|
||||
static const std::string CFG_FIPS_MODE;
|
||||
static const bool VAL_FIPS_MODE;
|
||||
#endif
|
||||
std::unordered_map<std::string, Context::Ptr> _mapPtrServerContexts;
|
||||
|
||||
friend class Poco::SingletonHolder<SSLManager>;
|
||||
friend class Context;
|
||||
|
@ -428,6 +428,23 @@ void SSLManager::initCertificateHandler(bool server)
|
||||
}
|
||||
|
||||
|
||||
Context::Ptr SSLManager::getCustomServerContext(const std::string & name)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
auto it = _mapPtrServerContexts.find(name);
|
||||
if (it != _mapPtrServerContexts.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Context::Ptr SSLManager::setCustomServerContext(const std::string & name, Context::Ptr ctx)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
ctx = _mapPtrServerContexts.insert({name, ctx}).first->second;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
Poco::Util::AbstractConfiguration& SSLManager::appConfig()
|
||||
{
|
||||
try
|
||||
|
@ -1407,8 +1407,8 @@ try
|
||||
tryLogCurrentException(log, "Disabling cgroup memory observer because of an error during initialization");
|
||||
}
|
||||
|
||||
const std::string cert_path = config().getString("openSSL.server.certificateFile", "");
|
||||
const std::string key_path = config().getString("openSSL.server.privateKeyFile", "");
|
||||
std::string cert_path = config().getString("openSSL.server.certificateFile", "");
|
||||
std::string key_path = config().getString("openSSL.server.privateKeyFile", "");
|
||||
|
||||
std::vector<std::string> extra_paths = {include_from_path};
|
||||
if (!cert_path.empty())
|
||||
@ -1416,6 +1416,18 @@ try
|
||||
if (!key_path.empty())
|
||||
extra_paths.emplace_back(key_path);
|
||||
|
||||
Poco::Util::AbstractConfiguration::Keys protocols;
|
||||
config().keys("protocols", protocols);
|
||||
for (const auto & protocol : protocols)
|
||||
{
|
||||
cert_path = config().getString("protocols." + protocol + ".certificateFile", "");
|
||||
key_path = config().getString("protocols." + protocol + ".privateKeyFile", "");
|
||||
if (!cert_path.empty())
|
||||
extra_paths.emplace_back(cert_path);
|
||||
if (!key_path.empty())
|
||||
extra_paths.emplace_back(key_path);
|
||||
}
|
||||
|
||||
auto main_config_reloader = std::make_unique<ConfigReloader>(
|
||||
config_path,
|
||||
extra_paths,
|
||||
@ -1656,7 +1668,7 @@ try
|
||||
|
||||
CompressionCodecEncrypted::Configuration::instance().tryLoad(*config, "encryption_codecs");
|
||||
#if USE_SSL
|
||||
CertificateReloader::instance().tryLoad(*config);
|
||||
CertificateReloader::instance().tryReloadAll(*config);
|
||||
#endif
|
||||
NamedCollectionFactory::instance().reloadFromConfig(*config);
|
||||
|
||||
|
@ -15,18 +15,23 @@ namespace DB
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Call set process for certificate.
|
||||
int callSetCertificate(SSL * ssl, [[maybe_unused]] void * arg)
|
||||
int callSetCertificate(SSL * ssl, void * arg)
|
||||
{
|
||||
return CertificateReloader::instance().setCertificate(ssl);
|
||||
if (!arg)
|
||||
return -1;
|
||||
|
||||
const CertificateReloader::MultiData * pdata = reinterpret_cast<CertificateReloader::MultiData *>(arg);
|
||||
return CertificateReloader::instance().setCertificate(ssl, pdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// This is callback for OpenSSL. It will be called on every connection to obtain a certificate and private key.
|
||||
int CertificateReloader::setCertificate(SSL * ssl)
|
||||
int CertificateReloader::setCertificate(SSL * ssl, const CertificateReloader::MultiData * pdata)
|
||||
{
|
||||
auto current = data.get();
|
||||
auto current = pdata->data.get();
|
||||
if (!current)
|
||||
return -1;
|
||||
|
||||
@ -65,24 +70,54 @@ int CertificateReloader::setCertificate(SSL * ssl)
|
||||
}
|
||||
|
||||
|
||||
void CertificateReloader::init()
|
||||
void CertificateReloader::init(MultiData * pdata)
|
||||
{
|
||||
LOG_DEBUG(log, "Initializing certificate reloader.");
|
||||
|
||||
/// Set a callback for OpenSSL to allow get the updated cert and key.
|
||||
|
||||
auto* ctx = Poco::Net::SSLManager::instance().defaultServerContext()->sslContext();
|
||||
SSL_CTX_set_cert_cb(ctx, callSetCertificate, nullptr);
|
||||
init_was_not_made = false;
|
||||
SSL_CTX_set_cert_cb(pdata->ctx, callSetCertificate, reinterpret_cast<void *>(pdata));
|
||||
pdata->init_was_not_made = false;
|
||||
}
|
||||
|
||||
|
||||
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
tryLoad(config, nullptr, Poco::Net::SSLManager::CFG_SERVER_PREFIX);
|
||||
}
|
||||
|
||||
|
||||
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix)
|
||||
{
|
||||
std::lock_guard lock{data_mutex};
|
||||
tryLoadImpl(config, ctx, prefix);
|
||||
}
|
||||
|
||||
|
||||
std::list<CertificateReloader::MultiData>::iterator CertificateReloader::findOrInsert(SSL_CTX * ctx, const std::string & prefix)
|
||||
{
|
||||
auto it = data.end();
|
||||
auto i = data_index.find(prefix);
|
||||
if (i != data_index.end())
|
||||
it = i->second;
|
||||
else
|
||||
{
|
||||
if (!ctx)
|
||||
ctx = Poco::Net::SSLManager::instance().defaultServerContext()->sslContext();
|
||||
data.push_back(MultiData(ctx));
|
||||
--it;
|
||||
data_index[prefix] = it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
|
||||
void CertificateReloader::tryLoadImpl(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix)
|
||||
{
|
||||
/// If at least one of the files is modified - recreate
|
||||
|
||||
std::string new_cert_path = config.getString("openSSL.server.certificateFile", "");
|
||||
std::string new_key_path = config.getString("openSSL.server.privateKeyFile", "");
|
||||
std::string new_cert_path = config.getString(prefix + "certificateFile", "");
|
||||
std::string new_key_path = config.getString(prefix + "privateKeyFile", "");
|
||||
|
||||
/// For empty paths (that means, that user doesn't want to use certificates)
|
||||
/// no processing required
|
||||
@ -93,32 +128,41 @@ void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & conf
|
||||
}
|
||||
else
|
||||
{
|
||||
bool cert_file_changed = cert_file.changeIfModified(std::move(new_cert_path), log);
|
||||
bool key_file_changed = key_file.changeIfModified(std::move(new_key_path), log);
|
||||
std::string pass_phrase = config.getString("openSSL.server.privateKeyPassphraseHandler.options.password", "");
|
||||
|
||||
if (cert_file_changed || key_file_changed)
|
||||
{
|
||||
LOG_DEBUG(log, "Reloading certificate ({}) and key ({}).", cert_file.path, key_file.path);
|
||||
data.set(std::make_unique<const Data>(cert_file.path, key_file.path, pass_phrase));
|
||||
LOG_INFO(log, "Reloaded certificate ({}) and key ({}).", cert_file.path, key_file.path);
|
||||
}
|
||||
|
||||
/// If callback is not set yet
|
||||
try
|
||||
{
|
||||
if (init_was_not_made)
|
||||
init();
|
||||
auto it = findOrInsert(ctx, prefix);
|
||||
|
||||
bool cert_file_changed = it->cert_file.changeIfModified(std::move(new_cert_path), log);
|
||||
bool key_file_changed = it->key_file.changeIfModified(std::move(new_key_path), log);
|
||||
|
||||
if (cert_file_changed || key_file_changed)
|
||||
{
|
||||
LOG_DEBUG(log, "Reloading certificate ({}) and key ({}).", it->cert_file.path, it->key_file.path);
|
||||
std::string pass_phrase = config.getString(prefix + "privateKeyPassphraseHandler.options.password", "");
|
||||
it->data.set(std::make_unique<const Data>(it->cert_file.path, it->key_file.path, pass_phrase));
|
||||
LOG_INFO(log, "Reloaded certificate ({}) and key ({}).", it->cert_file.path, it->key_file.path);
|
||||
}
|
||||
|
||||
/// If callback is not set yet
|
||||
if (it->init_was_not_made)
|
||||
init(&*it);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
init_was_not_made = true;
|
||||
LOG_ERROR(log, getCurrentExceptionMessageAndPattern(/* with_stacktrace */ false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CertificateReloader::tryReloadAll(const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
std::lock_guard lock{data_mutex};
|
||||
for (auto & item : data_index)
|
||||
tryLoadImpl(config, item.second->ctx, item.first);
|
||||
}
|
||||
|
||||
|
||||
CertificateReloader::Data::Data(std::string cert_path, std::string key_path, std::string pass_phrase)
|
||||
: certs_chain(Poco::Crypto::X509Certificate::readPEM(cert_path)), key(/* public key */ "", /* private key */ key_path, pass_phrase)
|
||||
{
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
#include <Poco/Logger.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
@ -31,28 +34,13 @@ class CertificateReloader
|
||||
public:
|
||||
using stat_t = struct stat;
|
||||
|
||||
/// Singleton
|
||||
CertificateReloader(CertificateReloader const &) = delete;
|
||||
void operator=(CertificateReloader const &) = delete;
|
||||
static CertificateReloader & instance()
|
||||
struct Data
|
||||
{
|
||||
static CertificateReloader instance;
|
||||
return instance;
|
||||
}
|
||||
Poco::Crypto::X509Certificate::List certs_chain;
|
||||
Poco::Crypto::EVPPKey key;
|
||||
|
||||
/// Initialize the callback and perform the initial cert loading
|
||||
void init();
|
||||
|
||||
/// Handle configuration reload
|
||||
void tryLoad(const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/// A callback for OpenSSL
|
||||
int setCertificate(SSL * ssl);
|
||||
|
||||
private:
|
||||
CertificateReloader() = default;
|
||||
|
||||
LoggerPtr log = getLogger("CertificateReloader");
|
||||
Data(std::string cert_path, std::string key_path, std::string pass_phrase);
|
||||
};
|
||||
|
||||
struct File
|
||||
{
|
||||
@ -65,19 +53,55 @@ private:
|
||||
bool changeIfModified(std::string new_path, LoggerPtr logger);
|
||||
};
|
||||
|
||||
File cert_file{"certificate"};
|
||||
File key_file{"key"};
|
||||
|
||||
struct Data
|
||||
struct MultiData
|
||||
{
|
||||
Poco::Crypto::X509Certificate::List certs_chain;
|
||||
Poco::Crypto::EVPPKey key;
|
||||
SSL_CTX * ctx = nullptr;
|
||||
MultiVersion<Data> data;
|
||||
bool init_was_not_made = true;
|
||||
|
||||
Data(std::string cert_path, std::string key_path, std::string pass_phrase);
|
||||
File cert_file{"certificate"};
|
||||
File key_file{"key"};
|
||||
|
||||
explicit MultiData(SSL_CTX * ctx_) : ctx(ctx_) {}
|
||||
};
|
||||
|
||||
MultiVersion<Data> data;
|
||||
bool init_was_not_made = true;
|
||||
/// Singleton
|
||||
CertificateReloader(CertificateReloader const &) = delete;
|
||||
void operator=(CertificateReloader const &) = delete;
|
||||
static CertificateReloader & instance()
|
||||
{
|
||||
static CertificateReloader instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Handle configuration reload for default path
|
||||
void tryLoad(const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/// Handle configuration reload
|
||||
void tryLoad(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix);
|
||||
|
||||
/// Handle configuration reload for all contexts
|
||||
void tryReloadAll(const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/// A callback for OpenSSL
|
||||
int setCertificate(SSL * ssl, const MultiData * pdata);
|
||||
|
||||
private:
|
||||
CertificateReloader() = default;
|
||||
|
||||
/// Initialize the callback and perform the initial cert loading
|
||||
void init(MultiData * pdata) TSA_REQUIRES(data_mutex);
|
||||
|
||||
/// Unsafe implementation
|
||||
void tryLoadImpl(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix) TSA_REQUIRES(data_mutex);
|
||||
|
||||
std::list<MultiData>::iterator findOrInsert(SSL_CTX * ctx, const std::string & prefix) TSA_REQUIRES(data_mutex);
|
||||
|
||||
LoggerPtr log = getLogger("CertificateReloader");
|
||||
|
||||
std::list<MultiData> data TSA_GUARDED_BY(data_mutex);
|
||||
std::unordered_map<std::string, std::list<MultiData>::iterator> data_index TSA_GUARDED_BY(data_mutex);
|
||||
mutable std::mutex data_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
118
src/Server/TLSHandler.cpp
Normal file
118
src/Server/TLSHandler.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include <Server/TLSHandler.h>
|
||||
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Common/Exception.h>
|
||||
|
||||
|
||||
#if USE_SSL
|
||||
# include <Poco/Net/Utility.h>
|
||||
# include <Poco/StringTokenizer.h>
|
||||
# include <Server/CertificateReloader.h>
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SSL) || USE_SSL == 0
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int SUPPORT_IS_DISABLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
DB::TLSHandler::TLSHandler(
|
||||
const StreamSocket & socket,
|
||||
[[maybe_unused]] const LayeredConfiguration & config_,
|
||||
[[maybe_unused]] const std::string & prefix_,
|
||||
TCPProtocolStackData & stack_data_)
|
||||
: Poco::Net::TCPServerConnection(socket)
|
||||
#if USE_SSL
|
||||
, config(config_)
|
||||
, prefix(prefix_)
|
||||
#endif
|
||||
, stack_data(stack_data_)
|
||||
{
|
||||
#if USE_SSL
|
||||
params.privateKeyFile = config.getString(prefix + SSLManager::CFG_PRIV_KEY_FILE, "");
|
||||
params.certificateFile = config.getString(prefix + SSLManager::CFG_CERTIFICATE_FILE, params.privateKeyFile);
|
||||
if (!params.privateKeyFile.empty() && !params.certificateFile.empty())
|
||||
{
|
||||
// for backwards compatibility
|
||||
auto ctx = SSLManager::instance().defaultServerContext();
|
||||
params.caLocation = config.getString(prefix + SSLManager::CFG_CA_LOCATION, ctx->getCAPaths().caLocation);
|
||||
|
||||
// optional options for which we have defaults defined
|
||||
params.verificationMode = SSLManager::VAL_VER_MODE;
|
||||
if (config.hasProperty(prefix + SSLManager::CFG_VER_MODE))
|
||||
{
|
||||
// either: none, relaxed, strict, once
|
||||
std::string mode = config.getString(prefix + SSLManager::CFG_VER_MODE);
|
||||
params.verificationMode = Poco::Net::Utility::convertVerificationMode(mode);
|
||||
}
|
||||
|
||||
params.verificationDepth = config.getInt(prefix + SSLManager::CFG_VER_DEPTH, SSLManager::VAL_VER_DEPTH);
|
||||
params.loadDefaultCAs = config.getBool(prefix + SSLManager::CFG_ENABLE_DEFAULT_CA, SSLManager::VAL_ENABLE_DEFAULT_CA);
|
||||
params.cipherList = config.getString(prefix + SSLManager::CFG_CIPHER_LIST, SSLManager::VAL_CIPHER_LIST);
|
||||
params.cipherList = config.getString(prefix + SSLManager::CFG_CYPHER_LIST, params.cipherList); // for backwards compatibility
|
||||
|
||||
bool require_tlsv1 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1, false);
|
||||
bool require_tlsv1_1 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1_1, false);
|
||||
bool require_tlsv1_2 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1_2, false);
|
||||
if (require_tlsv1_2)
|
||||
usage = Context::TLSV1_2_SERVER_USE;
|
||||
else if (require_tlsv1_1)
|
||||
usage = Context::TLSV1_1_SERVER_USE;
|
||||
else if (require_tlsv1)
|
||||
usage = Context::TLSV1_SERVER_USE;
|
||||
else
|
||||
usage = Context::SERVER_USE;
|
||||
|
||||
params.dhParamsFile = config.getString(prefix + SSLManager::CFG_DH_PARAMS_FILE, "");
|
||||
params.ecdhCurve = config.getString(prefix + SSLManager::CFG_ECDH_CURVE, "");
|
||||
|
||||
std::string disabled_protocols_list = config.getString(prefix + SSLManager::CFG_DISABLE_PROTOCOLS, "");
|
||||
Poco::StringTokenizer dp_tok(disabled_protocols_list, ";,", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
|
||||
disabled_protocols = 0;
|
||||
for (const auto & token : dp_tok)
|
||||
{
|
||||
if (token == "sslv2")
|
||||
disabled_protocols |= Context::PROTO_SSLV2;
|
||||
else if (token == "sslv3")
|
||||
disabled_protocols |= Context::PROTO_SSLV3;
|
||||
else if (token == "tlsv1")
|
||||
disabled_protocols |= Context::PROTO_TLSV1;
|
||||
else if (token == "tlsv1_1")
|
||||
disabled_protocols |= Context::PROTO_TLSV1_1;
|
||||
else if (token == "tlsv1_2")
|
||||
disabled_protocols |= Context::PROTO_TLSV1_2;
|
||||
}
|
||||
|
||||
extended_verification = config.getBool(prefix + SSLManager::CFG_EXTENDED_VERIFICATION, false);
|
||||
prefer_server_ciphers = config.getBool(prefix + SSLManager::CFG_PREFER_SERVER_CIPHERS, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DB::TLSHandler::run()
|
||||
{
|
||||
#if USE_SSL
|
||||
auto ctx = SSLManager::instance().defaultServerContext();
|
||||
if (!params.privateKeyFile.empty() && !params.certificateFile.empty())
|
||||
{
|
||||
ctx = SSLManager::instance().getCustomServerContext(prefix);
|
||||
if (!ctx)
|
||||
{
|
||||
ctx = new Context(usage, params);
|
||||
ctx->disableProtocols(disabled_protocols);
|
||||
ctx->enableExtendedCertificateVerification(extended_verification);
|
||||
if (prefer_server_ciphers)
|
||||
ctx->preferServerCiphers();
|
||||
CertificateReloader::instance().tryLoad(config, ctx->sslContext(), prefix);
|
||||
ctx = SSLManager::instance().setCustomServerContext(prefix, ctx);
|
||||
}
|
||||
}
|
||||
socket() = SecureStreamSocket::attach(socket(), ctx);
|
||||
stack_data.socket = socket();
|
||||
stack_data.certificate = params.certificateFile;
|
||||
#else
|
||||
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSL support for TCP protocol is disabled because Poco library was built without NetSSL support.");
|
||||
#endif
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <Poco/Net/TCPServerConnection.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Server/TCPProtocolStackData.h>
|
||||
#include <Poco/Util/LayeredConfiguration.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if USE_SSL
|
||||
# include <Poco/Net/Context.h>
|
||||
@ -14,11 +15,6 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int SUPPORT_IS_DISABLED;
|
||||
}
|
||||
|
||||
class TLSHandler : public Poco::Net::TCPServerConnection
|
||||
{
|
||||
#if USE_SSL
|
||||
@ -27,30 +23,22 @@ class TLSHandler : public Poco::Net::TCPServerConnection
|
||||
using Context = Poco::Net::Context;
|
||||
#endif
|
||||
using StreamSocket = Poco::Net::StreamSocket;
|
||||
using LayeredConfiguration = Poco::Util::LayeredConfiguration;
|
||||
public:
|
||||
explicit TLSHandler(const StreamSocket & socket, const std::string & key_, const std::string & certificate_, TCPProtocolStackData & stack_data_)
|
||||
: Poco::Net::TCPServerConnection(socket)
|
||||
, key(key_)
|
||||
, certificate(certificate_)
|
||||
, stack_data(stack_data_)
|
||||
{}
|
||||
explicit TLSHandler(const StreamSocket & socket, const LayeredConfiguration & config_, const std::string & prefix_, TCPProtocolStackData & stack_data_);
|
||||
|
||||
void run() override;
|
||||
|
||||
void run() override
|
||||
{
|
||||
#if USE_SSL
|
||||
auto ctx = SSLManager::instance().defaultServerContext();
|
||||
if (!key.empty() && !certificate.empty())
|
||||
ctx = new Context(Context::Usage::SERVER_USE, key, certificate, ctx->getCAPaths().caLocation);
|
||||
socket() = SecureStreamSocket::attach(socket(), ctx);
|
||||
stack_data.socket = socket();
|
||||
stack_data.certificate = certificate;
|
||||
#else
|
||||
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSL support for TCP protocol is disabled because Poco library was built without NetSSL support.");
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
std::string key [[maybe_unused]];
|
||||
std::string certificate [[maybe_unused]];
|
||||
#if USE_SSL
|
||||
Context::Params params [[maybe_unused]];
|
||||
Context::Usage usage [[maybe_unused]];
|
||||
int disabled_protocols = 0;
|
||||
bool extended_verification = false;
|
||||
bool prefer_server_ciphers = false;
|
||||
const LayeredConfiguration & config [[maybe_unused]];
|
||||
std::string prefix [[maybe_unused]];
|
||||
#endif
|
||||
TCPProtocolStackData & stack_data [[maybe_unused]];
|
||||
};
|
||||
|
||||
|
@ -48,8 +48,8 @@ public:
|
||||
LOG_TRACE(log, "TCP Request. Address: {}", socket.peerAddress().toString());
|
||||
return new TLSHandler(
|
||||
socket,
|
||||
server.config().getString(conf_name + ".privateKeyFile", ""),
|
||||
server.config().getString(conf_name + ".certificateFile", ""),
|
||||
server.config(),
|
||||
conf_name + ".",
|
||||
stack_data);
|
||||
}
|
||||
catch (const Poco::Net::NetException &)
|
||||
|
@ -58,6 +58,26 @@
|
||||
<port>8444</port>
|
||||
<description>https protocol endpoint</description>
|
||||
</https_endpoint>
|
||||
<https_tls1_2>
|
||||
<type>tls</type>
|
||||
<impl>http</impl>
|
||||
<host>0.0.0.0</host>
|
||||
<port>8445</port>
|
||||
<description>https protocol with TLSv1_2 minimum version</description>
|
||||
<disableProtocols>sslv2,sslv3,tlsv1,tlsv1_1</disableProtocols>
|
||||
<certificateFile>/etc/clickhouse-server/config.d/server.crt</certificateFile>
|
||||
<privateKeyFile>/etc/clickhouse-server/config.d/server.key</privateKeyFile>
|
||||
</https_tls1_2>
|
||||
<https_tls1_3>
|
||||
<type>tls</type>
|
||||
<impl>http</impl>
|
||||
<host>0.0.0.0</host>
|
||||
<port>8446</port>
|
||||
<description>https protocol with TLSv1_3 minimum version</description>
|
||||
<disableProtocols>sslv2,sslv3,tlsv1,tlsv1_1,tlsv1_2</disableProtocols>
|
||||
<certificateFile>/etc/clickhouse-server/config.d/server.crt</certificateFile>
|
||||
<privateKeyFile>/etc/clickhouse-server/config.d/server.key</privateKeyFile>
|
||||
</https_tls1_3>
|
||||
</protocols>
|
||||
|
||||
<!--tcp_port>9010</tcp_port-->
|
||||
|
@ -7,6 +7,7 @@ from helpers.client import Client
|
||||
import urllib.request, urllib.parse
|
||||
import subprocess
|
||||
import socket
|
||||
import warnings
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
@ -27,18 +28,34 @@ def setup_nodes():
|
||||
cluster.shutdown()
|
||||
|
||||
|
||||
def execute_query_https(host, port, query):
|
||||
def execute_query_https(host, port, query, version=None):
|
||||
url = f"https://{host}:{port}/?query={urllib.parse.quote(query)}"
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
if version:
|
||||
ctx.minimum_version = version
|
||||
ctx.maximum_version = version
|
||||
|
||||
request = urllib.request.Request(url)
|
||||
response = urllib.request.urlopen(request, context=ctx).read()
|
||||
return response.decode("utf-8")
|
||||
|
||||
|
||||
def execute_query_https_unsupported(host, port, query, version=None):
|
||||
try:
|
||||
execute_query_https(host, port, query, version)
|
||||
except Exception as e:
|
||||
e_text = str(e)
|
||||
if "NO_PROTOCOLS_AVAILABLE" in e_text:
|
||||
return True
|
||||
if "TLSV1_ALERT_PROTOCOL_VERSION" in e_text:
|
||||
return True
|
||||
raise
|
||||
return False
|
||||
|
||||
|
||||
def execute_query_http(host, port, query):
|
||||
url = f"http://{host}:{port}/?query={urllib.parse.quote(query)}"
|
||||
|
||||
@ -84,6 +101,49 @@ def test_connections():
|
||||
|
||||
assert execute_query_https(server.ip_address, 8444, "SELECT 1") == "1\n"
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8445, "SELECT 1", version=ssl.TLSVersion.SSLv3
|
||||
)
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8445, "SELECT 1", version=ssl.TLSVersion.TLSv1
|
||||
)
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8445, "SELECT 1", version=ssl.TLSVersion.TLSv1_1
|
||||
)
|
||||
assert (
|
||||
execute_query_https(
|
||||
server.ip_address, 8445, "SELECT 1", version=ssl.TLSVersion.TLSv1_2
|
||||
)
|
||||
== "1\n"
|
||||
)
|
||||
assert (
|
||||
execute_query_https(
|
||||
server.ip_address, 8445, "SELECT 1", version=ssl.TLSVersion.TLSv1_3
|
||||
)
|
||||
== "1\n"
|
||||
)
|
||||
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8446, "SELECT 1", version=ssl.TLSVersion.SSLv3
|
||||
)
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8446, "SELECT 1", version=ssl.TLSVersion.TLSv1
|
||||
)
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8446, "SELECT 1", version=ssl.TLSVersion.TLSv1_1
|
||||
)
|
||||
assert execute_query_https_unsupported(
|
||||
server.ip_address, 8446, "SELECT 1", version=ssl.TLSVersion.TLSv1_2
|
||||
)
|
||||
assert (
|
||||
execute_query_https(
|
||||
server.ip_address, 8446, "SELECT 1", version=ssl.TLSVersion.TLSv1_3
|
||||
)
|
||||
== "1\n"
|
||||
)
|
||||
|
||||
data = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n\0\021ClickHouse client\024\r\253\251\003\0\007default\0\004\001\0\001\0\0\t0.0.0.0:0\001\tmilovidov\021milovidov-desktop\21ClickHouse client\024\r\253\251\003\0\001\0\0\0\002\001\025SELECT 'Hello, world'\002\0\247\203\254l\325\\z|\265\254F\275\333\206\342\024\202\024\0\0\0\n\0\0\0\240\01\0\02\377\377\377\377\0\0\0"
|
||||
assert (
|
||||
netcat(server.ip_address, 9100, bytearray(data, "latin-1")).find(
|
||||
|
Loading…
Reference in New Issue
Block a user