mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Fix CertificateReloader usage for custom protocols
This commit is contained in:
parent
298b19c19d
commit
983fa64e4f
@ -17,6 +17,7 @@
|
|||||||
#ifndef NetSSL_SSLManager_INCLUDED
|
#ifndef NetSSL_SSLManager_INCLUDED
|
||||||
#define NetSSL_SSLManager_INCLUDED
|
#define NetSSL_SSLManager_INCLUDED
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include "Poco/BasicEvent.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
|
/// Unless initializeClient() has been called, the first call to this method initializes the default Context
|
||||||
/// from the application configuration.
|
/// 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();
|
PrivateKeyPassphraseHandlerPtr serverPassphraseHandler();
|
||||||
/// Returns the configured passphrase handler of the server. If none is set, the method will create a default one
|
/// Returns the configured passphrase handler of the server. If none is set, the method will create a default one
|
||||||
/// from an application configuration.
|
/// from an application configuration.
|
||||||
@ -348,6 +356,8 @@ namespace Net
|
|||||||
InvalidCertificateHandlerPtr _ptrClientCertificateHandler;
|
InvalidCertificateHandlerPtr _ptrClientCertificateHandler;
|
||||||
Poco::FastMutex _mutex;
|
Poco::FastMutex _mutex;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Context::Ptr> _mapPtrServerContexts;
|
||||||
|
|
||||||
friend class Poco::SingletonHolder<SSLManager>;
|
friend class Poco::SingletonHolder<SSLManager>;
|
||||||
friend class Context;
|
friend class Context;
|
||||||
};
|
};
|
||||||
|
@ -429,6 +429,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()
|
Poco::Util::AbstractConfiguration& SSLManager::appConfig()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -1359,8 +1359,8 @@ try
|
|||||||
tryLogCurrentException(log, "Disabling cgroup memory observer because of an error during initialization");
|
tryLogCurrentException(log, "Disabling cgroup memory observer because of an error during initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string cert_path = config().getString("openSSL.server.certificateFile", "");
|
std::string cert_path = config().getString("openSSL.server.certificateFile", "");
|
||||||
const std::string key_path = config().getString("openSSL.server.privateKeyFile", "");
|
std::string key_path = config().getString("openSSL.server.privateKeyFile", "");
|
||||||
|
|
||||||
std::vector<std::string> extra_paths = {include_from_path};
|
std::vector<std::string> extra_paths = {include_from_path};
|
||||||
if (!cert_path.empty())
|
if (!cert_path.empty())
|
||||||
@ -1368,6 +1368,18 @@ try
|
|||||||
if (!key_path.empty())
|
if (!key_path.empty())
|
||||||
extra_paths.emplace_back(key_path);
|
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>(
|
auto main_config_reloader = std::make_unique<ConfigReloader>(
|
||||||
config_path,
|
config_path,
|
||||||
extra_paths,
|
extra_paths,
|
||||||
@ -1602,7 +1614,7 @@ try
|
|||||||
|
|
||||||
CompressionCodecEncrypted::Configuration::instance().tryLoad(*config, "encryption_codecs");
|
CompressionCodecEncrypted::Configuration::instance().tryLoad(*config, "encryption_codecs");
|
||||||
#if USE_SSL
|
#if USE_SSL
|
||||||
CertificateReloader::instance().tryLoad(*config);
|
CertificateReloader::instance().tryReloadAll(*config);
|
||||||
#endif
|
#endif
|
||||||
NamedCollectionUtils::reloadFromConfig(*config);
|
NamedCollectionUtils::reloadFromConfig(*config);
|
||||||
|
|
||||||
|
@ -70,25 +70,30 @@ int CertificateReloader::setCertificate(SSL * ssl, const CertificateReloader::Mu
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CertificateReloader::init(MultiData * pdata, SSL_CTX * ctx)
|
void CertificateReloader::init(MultiData * pdata)
|
||||||
{
|
{
|
||||||
LOG_DEBUG(log, "Initializing certificate reloader.");
|
LOG_DEBUG(log, "Initializing certificate reloader.");
|
||||||
|
|
||||||
/// Set a callback for OpenSSL to allow get the updated cert and key.
|
/// Set a callback for OpenSSL to allow get the updated cert and key.
|
||||||
|
|
||||||
SSL_CTX_set_cert_cb(ctx, callSetCertificate, reinterpret_cast<void *>(pdata));
|
SSL_CTX_set_cert_cb(pdata->ctx, callSetCertificate, reinterpret_cast<void *>(pdata));
|
||||||
pdata->init_was_not_made = false;
|
pdata->init_was_not_made = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config)
|
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config)
|
||||||
{
|
{
|
||||||
auto* ctx = Poco::Net::SSLManager::instance().defaultServerContext()->sslContext();
|
tryLoad(config, nullptr, Poco::Net::SSLManager::CFG_SERVER_PREFIX);
|
||||||
tryLoad(config, ctx, Poco::Net::SSLManager::CFG_SERVER_PREFIX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix)
|
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(data_mutex);
|
||||||
|
tryLoadImpl(config, ctx, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
/// If at least one of the files is modified - recreate
|
||||||
|
|
||||||
@ -104,34 +109,39 @@ void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & conf
|
|||||||
}
|
}
|
||||||
else
|
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(prefix + "privateKeyPassphraseHandler.options.password", "");
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(data_mutex);
|
|
||||||
auto it = data.end();
|
auto it = data.end();
|
||||||
auto i = data_index.find(prefix);
|
auto i = data_index.find(prefix);
|
||||||
if (i != data_index.end())
|
if (i != data_index.end())
|
||||||
it = i->second;
|
it = i->second;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
data.push_back(MultiData());
|
data.push_back(MultiData(ctx));
|
||||||
--it;
|
--it;
|
||||||
data_index[prefix] = it;
|
data_index[prefix] = it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
std::string pass_phrase = config.getString(prefix + "privateKeyPassphraseHandler.options.password", "");
|
||||||
|
|
||||||
if (cert_file_changed || key_file_changed)
|
if (cert_file_changed || key_file_changed)
|
||||||
{
|
{
|
||||||
LOG_DEBUG(log, "Reloading certificate ({}) and key ({}).", cert_file.path, key_file.path);
|
LOG_DEBUG(log, "Reloading certificate ({}) and key ({}).", it->cert_file.path, it->key_file.path);
|
||||||
it->data.set(std::make_unique<const Data>(cert_file.path, key_file.path, pass_phrase));
|
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 ({}).", cert_file.path, key_file.path);
|
LOG_INFO(log, "Reloaded certificate ({}) and key ({}).", it->cert_file.path, it->key_file.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If callback is not set yet
|
/// If callback is not set yet
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
ctx = Poco::Net::SSLManager::instance().defaultServerContext()->sslContext();
|
||||||
|
it->ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
if (it->init_was_not_made)
|
if (it->init_was_not_made)
|
||||||
init(&*it, ctx);
|
init(&*it);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
@ -142,6 +152,14 @@ void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & conf
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CertificateReloader::tryReloadAll(const Poco::Util::AbstractConfiguration & config)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> 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)
|
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)
|
: certs_chain(Poco::Crypto::X509Certificate::readPEM(cert_path)), key(/* public key */ "", /* private key */ key_path, pass_phrase)
|
||||||
{
|
{
|
||||||
|
@ -42,10 +42,27 @@ public:
|
|||||||
Data(std::string cert_path, std::string key_path, std::string pass_phrase);
|
Data(std::string cert_path, std::string key_path, std::string pass_phrase);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct File
|
||||||
|
{
|
||||||
|
const char * description;
|
||||||
|
explicit File(const char * description_) : description(description_) {}
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
std::filesystem::file_time_type modification_time;
|
||||||
|
|
||||||
|
bool changeIfModified(std::string new_path, LoggerPtr logger);
|
||||||
|
};
|
||||||
|
|
||||||
struct MultiData
|
struct MultiData
|
||||||
{
|
{
|
||||||
|
SSL_CTX * ctx;
|
||||||
MultiVersion<Data> data;
|
MultiVersion<Data> data;
|
||||||
bool init_was_not_made = true;
|
bool init_was_not_made = true;
|
||||||
|
|
||||||
|
File cert_file{"certificate"};
|
||||||
|
File key_file{"key"};
|
||||||
|
|
||||||
|
explicit MultiData(SSL_CTX * ctx_) : ctx(ctx_) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Singleton
|
/// Singleton
|
||||||
@ -63,6 +80,9 @@ public:
|
|||||||
/// Handle configuration reload
|
/// Handle configuration reload
|
||||||
void tryLoad(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix);
|
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
|
/// A callback for OpenSSL
|
||||||
int setCertificate(SSL * ssl, const MultiData * pdata);
|
int setCertificate(SSL * ssl, const MultiData * pdata);
|
||||||
|
|
||||||
@ -70,24 +90,13 @@ private:
|
|||||||
CertificateReloader() = default;
|
CertificateReloader() = default;
|
||||||
|
|
||||||
/// Initialize the callback and perform the initial cert loading
|
/// Initialize the callback and perform the initial cert loading
|
||||||
void init(MultiData * pdata, SSL_CTX * ctx);
|
void init(MultiData * pdata);
|
||||||
|
|
||||||
|
/// Unsafe implementation
|
||||||
|
void tryLoadImpl(const Poco::Util::AbstractConfiguration & config, SSL_CTX * ctx, const std::string & prefix);
|
||||||
|
|
||||||
LoggerPtr log = getLogger("CertificateReloader");
|
LoggerPtr log = getLogger("CertificateReloader");
|
||||||
|
|
||||||
struct File
|
|
||||||
{
|
|
||||||
const char * description;
|
|
||||||
explicit File(const char * description_) : description(description_) {}
|
|
||||||
|
|
||||||
std::string path;
|
|
||||||
std::filesystem::file_time_type modification_time;
|
|
||||||
|
|
||||||
bool changeIfModified(std::string new_path, LoggerPtr logger);
|
|
||||||
};
|
|
||||||
|
|
||||||
File cert_file{"certificate"};
|
|
||||||
File key_file{"key"};
|
|
||||||
|
|
||||||
std::mutex data_mutex;
|
std::mutex data_mutex;
|
||||||
std::list<MultiData> data;
|
std::list<MultiData> data;
|
||||||
std::unordered_map<std::string, std::list<MultiData>::iterator> data_index;
|
std::unordered_map<std::string, std::list<MultiData>::iterator> data_index;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <Poco/SharedPtr.h>
|
#include <Poco/SharedPtr.h>
|
||||||
#include <Common/Exception.h>
|
#include <Common/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
#if USE_SSL
|
#if USE_SSL
|
||||||
# include <Poco/Net/Utility.h>
|
# include <Poco/Net/Utility.h>
|
||||||
# include <Poco/StringTokenizer.h>
|
# include <Poco/StringTokenizer.h>
|
||||||
@ -96,12 +97,17 @@ void DB::TLSHandler::run()
|
|||||||
auto ctx = SSLManager::instance().defaultServerContext();
|
auto ctx = SSLManager::instance().defaultServerContext();
|
||||||
if (!params.privateKeyFile.empty() && !params.certificateFile.empty())
|
if (!params.privateKeyFile.empty() && !params.certificateFile.empty())
|
||||||
{
|
{
|
||||||
ctx = new Context(usage, params);
|
ctx = SSLManager::instance().getCustomServerContext(prefix);
|
||||||
ctx->disableProtocols(disabled_protocols);
|
if (!ctx)
|
||||||
ctx->enableExtendedCertificateVerification(extended_verification);
|
{
|
||||||
if (prefer_server_ciphers)
|
ctx = new Context(usage, params);
|
||||||
ctx->preferServerCiphers();
|
ctx->disableProtocols(disabled_protocols);
|
||||||
CertificateReloader::instance().tryLoad(config, ctx->sslContext(), prefix);
|
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);
|
socket() = SecureStreamSocket::attach(socket(), ctx);
|
||||||
stack_data.socket = socket();
|
stack_data.socket = socket();
|
||||||
|
Loading…
Reference in New Issue
Block a user