Skip to content

Commit 3132b13

Browse files
Add SSL/TLS configuration support for PostgreSQL and MySQL connections
This commit adds comprehensive SSL/TLS configuration capabilities for PostgreSQL and MySQL database connections in ClickHouse, along with a security fix for the MariaDB connector. Changes: 1. MariaDB Connector/C Security Fix: - Updated submodule to aiven/mariadb-connector-c fork - Fixed X509_check_host call to include hostname length parameter - Prevents potential certificate validation bypass vulnerabilities 2. PostgreSQL SSL Configuration: - Added SSLMode enum (DISABLE, ALLOW, PREFER, REQUIRE, VERIFY_CA, VERIFY_FULL) - Added server settings: * postgresql_connection_pool_ssl_mode (default: PREFER) * postgresql_connection_pool_ssl_root_cert (default: empty) - Updated PoolWithFailover to accept SSL mode and CA certificate path - Modified formatConnectionString to include sslmode and sslrootcert parameters - Integrated SSL settings across all PostgreSQL integration points: * DatabasePostgreSQL * DatabaseMaterializedPostgreSQL * StoragePostgreSQL * StorageMaterializedPostgreSQL * TableFunctionPostgreSQL * PostgreSQLDictionarySource 3. MySQL SSL Configuration: - Added MySQLSSLMode enum (DISABLE, PREFER, VERIFY_FULL) - Updated Connection, Pool, and PoolWithFailover classes to accept SSL mode - Added ssl_mode and ssl_root_cert to StorageMySQL::Configuration - Enhanced MySQL dictionary source to support ssl_mode in named collections - Integrated SSL settings in MySQLHelpers and StorageMySQL Security Benefits: - Enables encrypted connections to prevent data interception - Supports certificate validation to prevent man-in-the-middle attacks - Provides flexible SSL mode selection for different security requirements - Fixes critical certificate hostname validation bug in MariaDB connector The changes maintain backward compatibility with default SSL mode set to PREFER, which attempts SSL but falls back gracefully if unavailable. Co-authored-by: Joe Lynch <joe.lynch@aiven.io>
1 parent 3107e73 commit 3132b13

28 files changed

+193
-35
lines changed

.gitmodules

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
url = https://github.com/google/re2
3131
[submodule "contrib/mariadb-connector-c"]
3232
path = contrib/mariadb-connector-c
33-
url = https://github.com/ClickHouse/mariadb-connector-c
33+
url = https://github.com/aiven/mariadb-connector-c
34+
branch = aiven/clickhouse-v25.8.12.129
3435
[submodule "contrib/jemalloc"]
3536
path = contrib/jemalloc
3637
url = https://github.com/jemalloc/jemalloc

src/Common/mysqlxx/Connection.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <mysql/mysql.h>
55
#endif
66

7+
#include <Core/SettingsEnums.h>
78
#include <mysqlxx/Connection.h>
89
#include <mysqlxx/Exception.h>
910

@@ -49,13 +50,14 @@ Connection::Connection(
4950
const char* ssl_ca,
5051
const char* ssl_cert,
5152
const char* ssl_key,
53+
DB::MySQLSSLMode ssl_mode,
5254
unsigned timeout,
5355
unsigned rw_timeout,
5456
bool enable_local_infile,
5557
bool opt_reconnect)
5658
: Connection()
5759
{
58-
connect(db, server, user, password, port, socket, ssl_ca, ssl_cert, ssl_key, timeout, rw_timeout, enable_local_infile, opt_reconnect);
60+
connect(db, server, user, password, port, socket, ssl_ca, ssl_cert, ssl_key, ssl_mode, timeout, rw_timeout, enable_local_infile, opt_reconnect);
5961
}
6062

6163
Connection::Connection(const std::string & config_name)
@@ -79,6 +81,7 @@ void Connection::connect(const char* db,
7981
const char * ssl_ca,
8082
const char * ssl_cert,
8183
const char * ssl_key,
84+
DB::MySQLSSLMode ssl_mode,
8285
unsigned timeout,
8386
unsigned rw_timeout,
8487
bool enable_local_infile,
@@ -111,8 +114,15 @@ void Connection::connect(const char* db,
111114
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
112115

113116
/// Specifies particular ssl key and certificate if it needs
114-
if (mysql_ssl_set(driver.get(), ifNotEmpty(ssl_key), ifNotEmpty(ssl_cert), ifNotEmpty(ssl_ca), nullptr, nullptr))
115-
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
117+
if (ssl_mode != DB::MySQLSSLMode::DISABLE) {
118+
if (mysql_ssl_set(driver.get(), ifNotEmpty(ssl_key), ifNotEmpty(ssl_cert), ifNotEmpty(ssl_ca), nullptr, nullptr))
119+
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
120+
}
121+
if (ssl_mode == DB::MySQLSSLMode::VERIFY_FULL) {
122+
static const char enable = 1;
123+
if (mysql_options(driver.get(), MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &enable))
124+
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
125+
}
116126

117127
if (!mysql_real_connect(driver.get(), server, user, password, db, port, ifNotEmpty(socket), driver->client_flag))
118128
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));

src/Common/mysqlxx/Pool.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Pool::Pool(
135135
const std::string & ssl_ca_,
136136
const std::string & ssl_cert_,
137137
const std::string & ssl_key_,
138+
DB::MySQLSSLMode ssl_mode_,
138139
const std::string & socket_,
139140
unsigned connect_timeout_,
140141
unsigned rw_timeout_,
@@ -155,6 +156,7 @@ Pool::Pool(
155156
, ssl_ca(ssl_ca_)
156157
, ssl_cert(ssl_cert_)
157158
, ssl_key(ssl_key_)
159+
, ssl_mode(ssl_mode_)
158160
, enable_local_infile(enable_local_infile_)
159161
, opt_reconnect(opt_reconnect_)
160162
{
@@ -311,6 +313,7 @@ void Pool::Entry::forceConnected() const
311313
pool->ssl_ca.c_str(),
312314
pool->ssl_cert.c_str(),
313315
pool->ssl_key.c_str(),
316+
pool->ssl_mode,
314317
pool->connect_timeout,
315318
pool->rw_timeout,
316319
pool->enable_local_infile,
@@ -387,6 +390,7 @@ Pool::Connection * Pool::allocConnection(bool dont_throw_if_failed_first_time)
387390
ssl_ca.c_str(),
388391
ssl_cert.c_str(),
389392
ssl_key.c_str(),
393+
ssl_mode,
390394
connect_timeout,
391395
rw_timeout,
392396
enable_local_infile,

src/Common/mysqlxx/PoolWithFailover.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <pcg_random.hpp>
88
#include <mysqlxx/PoolWithFailover.h>
99
#include <Common/randomSeed.h>
10+
#include <Core/SettingsEnums.h>
1011
#include <IO/WriteBufferFromString.h>
1112
#include <IO/Operators.h>
1213

@@ -124,6 +125,7 @@ PoolWithFailover::PoolWithFailover(
124125
const std::string & ssl_ca,
125126
const std::string & ssl_cert,
126127
const std::string & ssl_key,
128+
DB::MySQLSSLMode ssl_mode,
127129
unsigned default_connections_,
128130
unsigned max_connections_,
129131
size_t max_tries_,
@@ -140,7 +142,7 @@ PoolWithFailover::PoolWithFailover(
140142
for (const auto & [host, port] : addresses)
141143
{
142144
replicas_by_priority[0].emplace_back(std::make_shared<Pool>(database,
143-
host, user, password, port, ssl_ca, ssl_cert, ssl_key,
145+
host, user, password, port, ssl_ca, ssl_cert, ssl_key, ssl_mode,
144146
/* socket_ = */ "",
145147
connect_timeout_,
146148
rw_timeout_,

src/Common/mysqlxx/mysqlxx/Connection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <Poco/Util/Application.h>
77

8+
#include <Core/SettingsEnums.h>
89

910
#include <mysqlxx/Query.h>
1011
#include <mysqlxx/Exception.h>
@@ -78,6 +79,7 @@ class Connection final : private boost::noncopyable
7879
const char * ssl_ca = "",
7980
const char * ssl_cert = "",
8081
const char * ssl_key = "",
82+
DB::MySQLSSLMode ssl_mode = DB::MySQLSSLMode::PREFER,
8183
unsigned timeout = MYSQLXX_DEFAULT_TIMEOUT,
8284
unsigned rw_timeout = MYSQLXX_DEFAULT_RW_TIMEOUT,
8385
bool enable_local_infile = MYSQLXX_DEFAULT_ENABLE_LOCAL_INFILE,
@@ -99,6 +101,7 @@ class Connection final : private boost::noncopyable
99101
const char* ssl_ca,
100102
const char* ssl_cert,
101103
const char* ssl_key,
104+
DB::MySQLSSLMode ssl_mode = DB::MySQLSSLMode::PREFER,
102105
unsigned timeout = MYSQLXX_DEFAULT_TIMEOUT,
103106
unsigned rw_timeout = MYSQLXX_DEFAULT_RW_TIMEOUT,
104107
bool enable_local_infile = MYSQLXX_DEFAULT_ENABLE_LOCAL_INFILE,
@@ -140,6 +143,7 @@ class Connection final : private boost::noncopyable
140143
ssl_ca.c_str(),
141144
ssl_cert.c_str(),
142145
ssl_key.c_str(),
146+
DB::MySQLSSLMode::PREFER,
143147
timeout,
144148
rw_timeout,
145149
enable_local_infile,

src/Common/mysqlxx/mysqlxx/Pool.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <Poco/Exception.h>
1111
#include <Poco/Logger.h>
1212

13+
#include <Core/SettingsEnums.h>
1314
#include <mysqlxx/Connection.h>
1415

1516

@@ -158,6 +159,7 @@ class Pool final
158159
const std::string & ssl_ca_ = "",
159160
const std::string & ssl_cert_ = "",
160161
const std::string & ssl_key_ = "",
162+
DB::MySQLSSLMode ssl_mode_ = DB::MySQLSSLMode::PREFER,
161163
const std::string & socket_ = "",
162164
unsigned connect_timeout_ = MYSQLXX_DEFAULT_TIMEOUT,
163165
unsigned rw_timeout_ = MYSQLXX_DEFAULT_RW_TIMEOUT,
@@ -173,7 +175,7 @@ class Pool final
173175
user{other.user}, password{other.password},
174176
port{other.port}, socket{other.socket},
175177
connect_timeout{other.connect_timeout}, rw_timeout{other.rw_timeout},
176-
ssl_ca(other.ssl_ca), ssl_cert(other.ssl_cert), ssl_key(other.ssl_key),
178+
ssl_ca(other.ssl_ca), ssl_cert(other.ssl_cert), ssl_key(other.ssl_key), ssl_mode{other.ssl_mode},
177179
enable_local_infile{other.enable_local_infile}, opt_reconnect(other.opt_reconnect)
178180
{}
179181

@@ -235,6 +237,7 @@ class Pool final
235237
std::string ssl_ca;
236238
std::string ssl_cert;
237239
std::string ssl_key;
240+
DB::MySQLSSLMode ssl_mode;
238241
bool enable_local_infile;
239242
bool opt_reconnect;
240243

src/Common/mysqlxx/mysqlxx/PoolWithFailover.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <Core/SettingsEnums.h>
34
#include <mysqlxx/Pool.h>
45

56

@@ -126,6 +127,7 @@ namespace mysqlxx
126127
const std::string & ssl_ca,
127128
const std::string & ssl_cert,
128129
const std::string & ssl_key,
130+
DB::MySQLSSLMode ssl_mode,
129131
unsigned default_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_START_CONNECTIONS,
130132
unsigned max_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_CONNECTIONS,
131133
size_t max_tries_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES,

src/Core/PostgreSQL/PoolWithFailover.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ namespace ErrorCodes
2121
}
2222
}
2323

24+
static std::pair<DB::SSLMode, String>
25+
get_ssl_context(const DB::StoragePostgreSQL::Configuration & configuration, const DB::SSLMode & ssl_mode_, const String & ssl_root_cert_)
26+
{
27+
DB::SSLMode ssl_mode;
28+
String ssl_root_cert;
29+
if (configuration.ssl_mode)
30+
{
31+
ssl_mode = configuration.ssl_mode.value();
32+
ssl_root_cert = configuration.ssl_root_cert;
33+
}
34+
else
35+
{
36+
ssl_mode = ssl_mode_;
37+
ssl_root_cert = ssl_root_cert_;
38+
}
39+
return {ssl_mode, ssl_root_cert};
40+
}
41+
2442
namespace postgres
2543
{
2644

@@ -89,6 +107,8 @@ PoolWithFailover::PoolWithFailover(
89107
size_t max_tries_,
90108
bool auto_close_connection_,
91109
size_t connection_attempt_timeout_,
110+
const SSLMode & ssl_mode_,
111+
const String & ssl_root_cert_,
92112
bool bg_reconnect_)
93113
: pool_wait_timeout(pool_wait_timeout_)
94114
, max_tries(max_tries_)
@@ -102,13 +122,16 @@ PoolWithFailover::PoolWithFailover(
102122
{
103123
for (const auto & replica_configuration : configurations)
104124
{
125+
const auto& [ssl_mode, ssl_root_cert] = get_ssl_context(replica_configuration, ssl_mode_, ssl_root_cert_);
105126
auto connection_info = formatConnectionString(
106127
replica_configuration.database,
107128
replica_configuration.host,
108129
replica_configuration.port,
109130
replica_configuration.username,
110131
replica_configuration.password,
111-
connection_attempt_timeout_);
132+
connection_attempt_timeout_,
133+
ssl_mode,
134+
ssl_root_cert);
112135
replicas_with_priority[priority].emplace_back(std::make_shared<PoolHolder>(connection_info, pool_size));
113136
if (bg_reconnect)
114137
DB::ReplicasReconnector::instance().add(connectionReestablisher(std::weak_ptr(replicas_with_priority[priority].back()), pool_wait_timeout));
@@ -123,6 +146,8 @@ PoolWithFailover::PoolWithFailover(
123146
size_t max_tries_,
124147
bool auto_close_connection_,
125148
size_t connection_attempt_timeout_,
149+
const SSLMode & ssl_mode_,
150+
const String & ssl_root_cert_,
126151
bool bg_reconnect_)
127152
: pool_wait_timeout(pool_wait_timeout_)
128153
, max_tries(max_tries_)
@@ -136,13 +161,16 @@ PoolWithFailover::PoolWithFailover(
136161
for (const auto & [host, port] : configuration.addresses)
137162
{
138163
LOG_DEBUG(getLogger("PostgreSQLPoolWithFailover"), "Adding address host: {}, port: {} to connection pool", host, port);
164+
const auto& [ssl_mode, ssl_root_cert] = get_ssl_context(configuration, ssl_mode_, ssl_root_cert_);
139165
auto connection_string = formatConnectionString(
140166
configuration.database,
141167
host,
142168
port,
143169
configuration.username,
144170
configuration.password,
145-
connection_attempt_timeout_);
171+
connection_attempt_timeout_,
172+
ssl_mode,
173+
ssl_root_cert);
146174
replicas_with_priority[0].emplace_back(std::make_shared<PoolHolder>(connection_string, pool_size));
147175
if (bg_reconnect)
148176
DB::ReplicasReconnector::instance().add(connectionReestablisher(std::weak_ptr(replicas_with_priority[0].back()), pool_wait_timeout));

src/Core/PostgreSQL/PoolWithFailover.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@
88

99
#include <Core/PostgreSQL/ConnectionHolder.h>
1010
#include <mutex>
11+
#include <Core/SettingsEnums.h>
1112
#include <Poco/Util/AbstractConfiguration.h>
1213
#include <Storages/StoragePostgreSQL.h>
1314

1415

1516
static constexpr inline auto POSTGRESQL_POOL_DEFAULT_SIZE = 16;
1617
static constexpr inline auto POSTGRESQL_POOL_WAIT_TIMEOUT = 5000;
18+
static constexpr inline auto POSTGRESQL_POOL_DEFAULT_CONNECT_TIMEOUT_SEC = 10;
19+
static constexpr inline auto POSTGRESQL_POOL_DEFAULT_SSL_MODE = DB::SSLMode::PREFER;
20+
static constexpr inline auto POSTGRESQL_POOL_DEFAULT_SSL_ROOT_CERT = "";
1721

1822
namespace postgres
1923
{
24+
using SSLMode = DB::SSLMode;
2025

2126
class PoolWithFailover
2227
{
@@ -31,6 +36,8 @@ class PoolWithFailover
3136
size_t max_tries_,
3237
bool auto_close_connection_,
3338
size_t connection_attempt_timeout_,
39+
const SSLMode & ssl_mode_,
40+
const String & ssl_root_cert_,
3441
bool bg_reconnect_ = false);
3542

3643
explicit PoolWithFailover(
@@ -40,6 +47,8 @@ class PoolWithFailover
4047
size_t max_tries_,
4148
bool auto_close_connection_,
4249
size_t connection_attempt_timeout_,
50+
const SSLMode & ssl_mode_,
51+
const String & ssl_root_cert_,
4352
bool bg_reconnect_ = false);
4453

4554
PoolWithFailover(const PoolWithFailover & other) = delete;

0 commit comments

Comments
 (0)