/** Collection of integration tests that validate TLS-connectivity to a CH server */ #include "readonly_client_test.h" #include "connection_failed_client_test.h" #include "utils.h" #include #include #include namespace { using namespace clickhouse; const auto QUERIES = std::vector { "SELECT version()", "SELECT fqdn()", "SELECT buildId()", "SELECT uptime()", "SELECT now()" }; } #if defined(__linux__) // On Ubuntu 20.04 /etc/ssl/certs is a default directory with the CA files const auto DEFAULT_CA_DIRECTORY_PATH = "/etc/ssl/certs"; #elif defined(__APPLE__) // On macOS we will rely on Homebrew's OpenSSL installation const auto DEFAULT_CA_DIRECTORY_PATH = "/usr/local/etc/openssl@1.1/cert.pem"; #elif defined(_win_) // DEBUG ME: mingw - was not able to make it work. Every time it ends with exception: // "Failed to verify SSL connection, X509_v error: 20 unable to get local issuer certificate" const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/cert.pem"; // const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/certs"; // const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/certs/ca-bundle.crt"; #endif INSTANTIATE_TEST_SUITE_P( RemoteTLS, ReadonlyClientTest, ::testing::Values(ReadonlyClientTest::ParamType { ClientOptions() .SetHost( getEnvOrDefault("CLICKHOUSE_SECURE_HOST", "github.demo.trial.altinity.cloud")) .SetPort( getEnvOrDefault("CLICKHOUSE_SECURE_PORT", "9440")) .SetUser( getEnvOrDefault("CLICKHOUSE_SECURE_USER", "demo")) .SetPassword( getEnvOrDefault("CLICKHOUSE_SECURE_PASSWORD", "demo")) .SetDefaultDatabase(getEnvOrDefault("CLICKHOUSE_SECURE_DB", "default")) .SetSendRetries(1) .SetPingBeforeQuery(true) .SetCompressionMethod(CompressionMethod::None) .SetSSLOptions(ClientOptions::SSLOptions() .SetPathToCADirectory(DEFAULT_CA_DIRECTORY_PATH)), QUERIES } )); const auto ClickHouseExplorerConfig = ClientOptions() .SetHost( getEnvOrDefault("CLICKHOUSE_SECURE2_HOST", "play.clickhouse.com")) .SetPort( getEnvOrDefault("CLICKHOUSE_SECURE2_PORT", "9440")) .SetUser( getEnvOrDefault("CLICKHOUSE_SECURE2_USER", "explorer")) .SetPassword( getEnvOrDefault("CLICKHOUSE_SECURE2_PASSWORD", "")) .SetDefaultDatabase(getEnvOrDefault("CLICKHOUSE_SECURE2_DB", "default")) .SetSendRetries(1) .SetPingBeforeQuery(true) .SetCompressionMethod(CompressionMethod::None); INSTANTIATE_TEST_SUITE_P( Remote_GH_API_TLS, ReadonlyClientTest, ::testing::Values(ReadonlyClientTest::ParamType { ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetPathToCADirectory(DEFAULT_CA_DIRECTORY_PATH)), QUERIES } )); // For some reasons doesn't work on MacOS. // Looks like `VerifyCAPath` has no effect, while parsing and setting value works. // Also for some reason SetPathToCADirectory() + SSL_CTX_load_verify_locations() works. #if !defined(__APPLE__) TEST(OpenSSLConfiguration, DISABLED_ValidValues) { // Verify that Client with valid configuration set via SetConfiguration is able to connect. EXPECT_NO_THROW( Client(ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetConfiguration({ {"VerifyCAPath", DEFAULT_CA_DIRECTORY_PATH}, {"MinProtocol", "TLSv1.3"}, {"VerifyMode", "Peer"}, {"no_comp"} // shorthand for command with no value }) )) ); } #endif TEST(OpenSSLConfiguration, InValidValues) { // Verify that invalid options cause throwing exception. std::vector configurations = { {"VerifyCAPath", std::nullopt}, // missing required value {"VerifyCAPath"}, // missing required value, shorthand {"MinProtocol", "NOT A STRING"}, // invalid value {"MinProtocol", ""}, // invalid value {"unknownCommand"}, // invalid command, shorthand {"unknownCommand", "with unexpected value"}, // invalid command + some value {"", std::nullopt}, // empty command with no value {""}, // empty command with no value {"no_comp", "unexpected value"} // value for command that doesn't require a value. }; // Unfortunately, there is no way of checking configuration validity without // creating a Client and pointing that client to a working CH server. // So we mix valid and invalid configurations by providing the correct // server config but incorrect single Command-Value pair individually. for (const auto & cv : configurations) { EXPECT_ANY_THROW( Client(ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetConfiguration({cv}) )); ) << "On command \'" << cv.command << "\' and value: " << (cv.value ? *cv.value : ""); } } //INSTANTIATE_TEST_SUITE_P( // Remote_GH_API_TLS_WithStringConfig, ReadonlyClientTest, // ::testing::Values(ReadonlyClientTest::ParamType { // ClientOptions(ClickHouseExplorerConfig) // .SetSSLOptions(ClientOptions::SSLOptions() // .SetConfiguration({{"", DEFAULT_CA_DIRECTORY_PATH}}) // QUERIES // } //)); INSTANTIATE_TEST_SUITE_P( Remote_GH_API_TLS_no_CA, ReadonlyClientTest, ::testing::Values(ReadonlyClientTest::ParamType { ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetUseDefaultCALocations(false) .SetSkipVerification(true)), // No CA loaded, but verification is skipped {"SELECT 1;"} } )); INSTANTIATE_TEST_SUITE_P( Remote_GH_API_TLS_no_CA, ConnectionFailedClientTest, ::testing::Values(ConnectionFailedClientTest::ParamType { ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetUseDefaultCALocations(false)), ExpectingException{"X509_v error: 20 unable to get local issuer certificate"} } )); INSTANTIATE_TEST_SUITE_P( Remote_GH_API_TLS_wrong_TLS_version, ConnectionFailedClientTest, ::testing::Values(ConnectionFailedClientTest::ParamType { ClientOptions(ClickHouseExplorerConfig) .SetSSLOptions(ClientOptions::SSLOptions() .SetUseDefaultCALocations(false) .SetMaxProtocolVersion(SSL3_VERSION)), ExpectingException{"no protocols available"} } )); //// Special test that require properly configured TLS-enabled version of CH running locally //INSTANTIATE_TEST_SUITE_P( // LocalhostTLS_None, ReadonlyClientTest, // ::testing::Values(std::tuple > { // ClientOptions() // .SetHost("127.0.0.1") // .SetPort(9440) // .SetUser("default") // .SetPingBeforeQuery(true) // .SetCompressionMethod(CompressionMethod::None) // .SetSSLOptions(ClientOptions::SSLOptions() // .SetPathToCADirectory("./CA/") // .SetUseSNI(false)), // QUERIES // } //)); //INSTANTIATE_TEST_SUITE_P( // LocalhostTLS_LZ4, ReadonlyClientTest, // ::testing::Values(std::tuple > { // ClientOptions() // .SetHost("127.0.0.1") // .SetPort(9440) // .SetUser("default") // .SetPingBeforeQuery(true) // .SetCompressionMethod(CompressionMethod::LZ4) // .SetSSLOptions(ClientOptions::SSLOptions() // .SetPathToCADirectory("./CA/") // .SetUseSNI(false)), // QUERIES // } //));