ClickHouse/src/Server/CertificateReloader.cpp

137 lines
3.7 KiB
C++
Raw Normal View History

2021-07-06 23:15:30 +00:00
#include "CertificateReloader.h"
#if USE_SSL
2021-11-10 12:55:57 +00:00
#include <base/logger_useful.h>
#include <base/errnoToString.h>
2021-07-09 02:27:01 +00:00
#include <Poco/Net/Context.h>
#include <Poco/Net/SSLManager.h>
#include <Poco/Net/Utility.h>
namespace DB
{
2021-07-06 23:15:30 +00:00
2021-11-10 15:35:35 +00:00
namespace
{
/// Call set process for certificate.
int callSetCertificate(SSL * ssl, [[maybe_unused]] void * arg)
2021-11-10 12:55:57 +00:00
{
return CertificateReloader::instance().setCertificate(ssl);
}
2021-11-10 15:35:35 +00:00
}
2021-11-10 12:55:57 +00:00
2021-07-06 23:34:43 +00:00
namespace ErrorCodes
{
extern const int CANNOT_STAT;
}
2021-07-09 02:27:01 +00:00
/// This is callback for OpenSSL. It will be called on every connection to obtain a certificate and private key.
2021-07-06 23:15:30 +00:00
int CertificateReloader::setCertificate(SSL * ssl)
{
2021-07-09 02:27:01 +00:00
auto current = data.get();
if (!current)
return -1;
SSL_use_certificate(ssl, const_cast<X509 *>(current->cert.certificate()));
SSL_use_PrivateKey(ssl, const_cast<EVP_PKEY *>(static_cast<const EVP_PKEY *>(current->key)));
int err = SSL_check_private_key(ssl);
if (err != 1)
{
std::string msg = Poco::Net::Utility::getLastError();
2021-07-09 02:27:01 +00:00
LOG_ERROR(log, "Unusable key-pair {}", msg);
return -1;
}
2021-07-09 02:27:01 +00:00
return 1;
}
2021-07-09 02:27:01 +00:00
void CertificateReloader::init()
{
2021-07-06 23:34:43 +00:00
LOG_DEBUG(log, "Initializing certificate reloader.");
2021-07-09 02:27:01 +00:00
/// Set a callback for OpenSSL to allow get the updated cert and key.
auto* ctx = Poco::Net::SSLManager::instance().defaultServerContext()->sslContext();
2021-11-10 15:35:35 +00:00
SSL_CTX_set_cert_cb(ctx, callSetCertificate, nullptr);
2022-01-31 12:13:28 +00:00
init_was_not_made = false;
}
2021-07-09 02:27:01 +00:00
void CertificateReloader::tryLoad(const Poco::Util::AbstractConfiguration & config)
{
2021-07-09 02:27:01 +00:00
/// If at least one of the files is modified - recreate
2021-07-09 02:27:01 +00:00
std::string new_cert_path = config.getString("openSSL.server.certificateFile", "");
std::string new_key_path = config.getString("openSSL.server.privateKeyFile", "");
/// For empty paths (that means, that user doesn't want to use certificates)
/// no processing required
2021-11-10 15:35:35 +00:00
if (new_cert_path.empty() || new_key_path.empty())
{
LOG_INFO(log, "One of paths is empty. Cannot apply new configuration for certificates. Fill all paths and try again.");
}
else
{
2021-11-10 15:35:35 +00:00
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);
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));
LOG_INFO(log, "Reloaded certificate ({}) and key ({}).", cert_file.path, key_file.path);
}
/// If callback is not set yet
2022-01-31 12:13:28 +00:00
try
{
if (init_was_not_made)
init();
2022-01-31 12:21:22 +00:00
}
catch (...)
{
2022-01-31 12:13:28 +00:00
init_was_not_made = true;
2022-02-03 00:03:46 +00:00
LOG_ERROR(log, fmt::runtime(getCurrentExceptionMessage(false)));
2022-01-31 12:13:28 +00:00
}
}
}
2021-07-09 02:27:01 +00:00
CertificateReloader::Data::Data(std::string cert_path, std::string key_path)
: cert(cert_path), key(/* public key */ "", /* private key */ key_path)
{
}
2021-07-09 02:27:01 +00:00
bool CertificateReloader::File::changeIfModified(std::string new_path, Poco::Logger * logger)
{
std::error_code ec;
std::filesystem::file_time_type new_modification_time = std::filesystem::last_write_time(new_path, ec);
if (ec)
2021-07-06 23:34:43 +00:00
{
2021-07-09 02:27:01 +00:00
LOG_ERROR(logger, "Cannot obtain modification time for {} file {}, skipping update. {}",
description, new_path, errnoToString(ErrorCodes::CANNOT_STAT, ec.value()));
return false;
2021-07-06 23:34:43 +00:00
}
2021-07-09 02:27:01 +00:00
if (new_path != path || new_modification_time != modification_time)
{
2021-07-09 02:27:01 +00:00
path = new_path;
modification_time = new_modification_time;
return true;
}
return false;
}
}
2021-07-06 23:15:30 +00:00
#endif