More parameters for tls layer in protocols

This commit is contained in:
Anton Ivashkin 2024-05-16 16:17:07 +02:00
parent 4ee342c9c3
commit 92df0f9737
6 changed files with 176 additions and 49 deletions

View File

@ -258,6 +258,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,40 +348,6 @@ 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
friend class Poco::SingletonHolder<SSLManager>;
friend class Context;
};

68
src/Server/TLSHandler.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <Poco/Net/Utility.h>
#include <Poco/StringTokenizer.h>
#include <Server/TLSHandler.h>
DB::TLSHandler::TLSHandler(const StreamSocket & socket, const LayeredConfiguration & config, const std::string & prefix, TCPProtocolStackData & stack_data_)
: Poco::Net::TCPServerConnection(socket)
, 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 requireTLSv1 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1, false);
bool requireTLSv1_1 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1_1, false);
bool requireTLSv1_2 = config.getBool(prefix + SSLManager::CFG_REQUIRE_TLSV1_2, false);
if (requireTLSv1_2)
usage = Context::TLSV1_2_SERVER_USE;
else if (requireTLSv1_1)
usage = Context::TLSV1_1_SERVER_USE;
else if (requireTLSv1)
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 disabledProtocolsList = config.getString(prefix + SSLManager::CFG_DISABLE_PROTOCOLS, "");
Poco::StringTokenizer dpTok(disabledProtocolsList, ";,", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
disabledProtocols = 0;
for (Poco::StringTokenizer::Iterator it = dpTok.begin(); it != dpTok.end(); ++it)
{
if (*it == "sslv2")
disabledProtocols |= Context::PROTO_SSLV2;
else if (*it == "sslv3")
disabledProtocols |= Context::PROTO_SSLV3;
else if (*it == "tlsv1")
disabledProtocols |= Context::PROTO_TLSV1;
else if (*it == "tlsv1_1")
disabledProtocols |= Context::PROTO_TLSV1_1;
else if (*it == "tlsv1_2")
disabledProtocols |= Context::PROTO_TLSV1_2;
}
extendedVerification = config.getBool(prefix + SSLManager::CFG_EXTENDED_VERIFICATION, false);
preferServerCiphers = config.getBool(prefix + SSLManager::CFG_PREFER_SERVER_CIPHERS, false);
}
#endif
}

View File

@ -4,6 +4,9 @@
#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>
@ -24,33 +27,38 @@ class TLSHandler : public Poco::Net::TCPServerConnection
#if USE_SSL
using SecureStreamSocket = Poco::Net::SecureStreamSocket;
using SSLManager = Poco::Net::SSLManager;
using Context = Poco::Net::Context;
#endif
using Context = Poco::Net::Context;
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
{
#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);
if (!params.privateKeyFile.empty() && !params.certificateFile.empty())
{
ctx = new Context(usage, params);
ctx->disableProtocols(disabledProtocols);
ctx->enableExtendedCertificateVerification(extendedVerification);
if (preferServerCiphers)
ctx->preferServerCiphers();
}
socket() = SecureStreamSocket::attach(socket(), ctx);
stack_data.socket = socket();
stack_data.certificate = certificate;
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
}
private:
std::string key [[maybe_unused]];
std::string certificate [[maybe_unused]];
Context::Params params [[maybe_unused]];
Context::Usage usage [[maybe_unused]];
int disabledProtocols = 0;
bool extendedVerification = false;
bool preferServerCiphers = false;
TCPProtocolStackData & stack_data [[maybe_unused]];
};

View File

@ -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 &)

View File

@ -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-->

View File

@ -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,20 @@ 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(