diff --git a/4e214d612359a294312943271abaf40685115fda.patch b/4e214d612359a294312943271abaf40685115fda.patch new file mode 100644 index 0000000..b83930d --- /dev/null +++ b/4e214d612359a294312943271abaf40685115fda.patch @@ -0,0 +1,374 @@ +Taken from: + https://github.com/codership/galera/commit/4e214d612359a294312943271abaf40685115fda.patch + +Reson: + The SSL certifactes expired, which resulted in the test failing: + | 85%: Checks: 77, Failures: 0, Errors: 11 + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1019:E:test_ssl_connect:test_ssl_connect:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1031:E:test_ssl_connect_twice:test_ssl_connect_twice:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1046:E:test_ssl_async_read_write:test_ssl_async_read_write:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1058:E:test_ssl_async_read_write_large:test_ssl_async_read_write_large:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:636:E:test_ssl_async_read_write_small_large:test_ssl_async_read_write_small_large:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1083:E:test_ssl_async_read_from_client_write_from_server:test_ssl_async_read_from_client_write_from_server:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1096:E:test_ssl_write_twice_wo_handling:test_ssl_write_twice_wo_handling:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1109:E:test_ssl_close_client:test_ssl_close_client:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1121:E:test_ssl_close_server:test_ssl_close_server:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1133:E:test_ssl_get_tcp_info:test_ssl_get_tcp_info:0: (after this point) Test timeout expired + | /builddir/build/BUILD/galera-26.4.16/galerautils/tests/gu_asio_test.cpp:1145:E:test_ssl_compression_option:test_ssl_compression_option:0: (after this point) Test timeout expired + +--- + +From 4e214d612359a294312943271abaf40685115fda Mon Sep 17 00:00:00 2001 +From: Teemu Ollakka +Date: Mon, 2 Oct 2023 20:31:37 +0300 +Subject: [PATCH] codership/galera#647 Generate keys and certificates for SSL + tests + +Relying on static certificates causes several problems: +- Certificates eventually expire, which causes surprises + when tests suddenly stop passing without prior warning. +- Standards change, a certificate created on different + platform long time ago may not be usable on some more + modern platform. +- Lack of fine grained control on test scenarios without + generating and storing several certificates in source + repository. + +In order to work around the problems above, generate +certificates programmatically before the tests are run. +The generated certificates use the same versioning and +extensions as the original ones under tests/conf. +--- + galerautils/tests/CMakeLists.txt | 4 +- + galerautils/tests/gu_asio_test.cpp | 269 ++++++++++++++++++++++++++++- + 3 files changed, 266 insertions(+), 8 deletions(-) + +diff --git a/galerautils/tests/CMakeLists.txt b/galerautils/tests/CMakeLists.txt +index f863ce66b..a44b2d6b3 100644 +--- a/galerautils/tests/CMakeLists.txt ++++ b/galerautils/tests/CMakeLists.txt +@@ -47,7 +47,6 @@ add_test( + # + # C++ galerautils tests. + # +- + add_executable(gu_tests++ + gu_atomic_test.cpp + gu_gtid_test.cpp +@@ -75,7 +74,7 @@ add_executable(gu_tests++ + + target_compile_definitions(gu_tests++ + PRIVATE +- -DGU_ASIO_TEST_CERT_DIR="${PROJECT_SOURCE_DIR}/tests/conf") ++ -DGU_ASIO_TEST_CERT_DIR="${CMAKE_CURRENT_BINARY_DIR}/certs") + + # TODO: These should be eventually fixed. + target_compile_options(gu_tests++ +@@ -93,7 +92,6 @@ add_test( + NAME gu_tests++ + COMMAND gu_tests++ + ) +- + # + # Deqmap micro benchmark. + # +diff --git a/galerautils/tests/gu_asio_test.cpp b/galerautils/tests/gu_asio_test.cpp +index c4c948bda..616902eb1 100644 +--- a/galerautils/tests/gu_asio_test.cpp ++++ b/galerautils/tests/gu_asio_test.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2019-2020 Codership Oy ++ * Copyright (C) 2019-2023 Codership Oy + */ + + +@@ -920,18 +920,276 @@ END_TEST + + #ifdef GALERA_HAVE_SSL + ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include + +-// +-// SSL +-// ++#include + + static std::string get_cert_dir() + { +- // This will be set by CMake/preprocessor. ++ static_assert(::strlen(GU_ASIO_TEST_CERT_DIR) > 0); ++ const std::string ret{ GU_ASIO_TEST_CERT_DIR }; ++ auto* dir = opendir(ret.c_str()); ++ if (!dir) ++ { ++ if (mkdir(ret.c_str(), S_IRWXU)) ++ { ++ const auto* errstr = ::strerror(errno); ++ gu_throw_fatal << "Could not create dir " << ret << ": " << errstr; ++ } ++ } ++ else ++ { ++ closedir(dir); ++ } + return GU_ASIO_TEST_CERT_DIR; + } + ++static int password_cb(char*, int, int, void*) { return 0; } ++ ++static void throw_error(const char* msg) ++{ ++ gu_throw_fatal << msg << ": " << ERR_error_string(ERR_get_error(), nullptr); ++} ++ ++static EVP_PKEY* create_key() ++{ ++#if OPENSSL_VERSION_MAJOR < 3 ++ auto* bn = BN_new(); ++ if (!bn) ++ { ++ throw_error("could not create BN"); ++ } ++ BN_set_word(bn, 0x10001); ++ auto* rsa = RSA_new(); ++ if (!rsa) ++ { ++ BN_free(bn); ++ throw_error("could not create RSA"); ++ } ++ RSA_generate_key_ex(rsa, 2048, bn, nullptr); ++ auto* pkey = EVP_PKEY_new(); ++ if (!pkey) ++ { ++ BN_free(bn); ++ RSA_free(rsa); ++ throw_error("could not create PKEY"); ++ } ++ EVP_PKEY_set1_RSA(pkey, rsa); ++ RSA_free(rsa); ++ BN_free(bn); ++ return pkey; ++#else ++ auto* ret = EVP_RSA_gen(2048); ++ if (!ret) ++ { ++ throw_error("could not create RSA"); ++ } ++ return ret; ++#endif /* OPENSSL_VERSION_MAJOR < 3 */ ++} ++ ++static FILE* open_file(const std::string& path, const char* mode) ++{ ++ auto* ret = fopen(path.c_str(), mode); ++ if (!ret) ++ { ++ const auto* errstr = ::strerror(errno); ++ gu_throw_fatal << "Could not open file " << path << ": " ++ << errstr; ++ } ++ return ret; ++} ++ ++static void write_key(EVP_PKEY* pkey, const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string key_file_path = cert_dir + "/" + filename; ++ auto* key_file = open_file(key_file_path, "wb"); ++ if (!PEM_write_PrivateKey(key_file, pkey, nullptr, nullptr, 0, password_cb, ++ nullptr)) ++ { ++ throw_error("Could not write key"); ++ } ++ fclose(key_file); ++} ++ ++static void set_x509v3_extensions(X509* x509, X509* issuer) ++{ ++ auto* conf_bio = BIO_new(BIO_s_mem()); ++ std::string ext{ "[extensions]\n" ++ "authorityKeyIdentifier=keyid,issuer\n" ++ "subjectKeyIdentifier=hash\n" }; ++ if (!issuer) ++ { ++ ext += "basicConstraints=critical,CA:TRUE\n"; ++ } ++ else ++ { ++ ext += "keyUsage=digitalSignature,keyEncipherment\n"; ++ ext += "basicConstraints=CA:FALSE\n"; ++ } ++ BIO_printf(conf_bio, "%s", ext.c_str()); ++ auto* conf = NCONF_new(nullptr); ++ long errorline = -1; ++ int err; ++ if ((err = NCONF_load_bio(conf, conf_bio, &errorline)) <= 0) ++ { ++ gu_throw_fatal << "Could not load conf: " << err; ++ } ++ if (errorline != -1) ++ { ++ gu_throw_fatal << "Could not load conf, errorline: " << errorline; ++ } ++ // TODO: V3 extensions ++ X509V3_CTX ctx; ++ X509V3_set_ctx(&ctx, issuer ? issuer : x509, x509, nullptr, nullptr, 0); ++ X509V3_set_nconf(&ctx, conf); ++ if (!X509V3_EXT_add_nconf(conf, &ctx, "extensions", x509)) ++ { ++ throw_error("Could not add extension"); ++ } ++ NCONF_free(conf); ++ BIO_free(conf_bio); ++} ++ ++static X509* create_x509(EVP_PKEY* pkey, X509* issuer, const char* cn) ++{ ++ auto* x509 = X509_new(); ++ /* According to standard, value 2 means version 3. */ ++ X509_set_version(x509, 2); ++ ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); ++ X509_gmtime_adj(X509_get_notBefore(x509), 0); ++ X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); ++ X509_set_pubkey(x509, pkey); ++ ++ auto* name = X509_get_subject_name(x509); ++ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"FI", ++ -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, ++ (unsigned char*)"Uusimaa", -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, ++ (unsigned char*)"Helsinki", -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, ++ (unsigned char*)"Codership", -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, ++ (unsigned char*)"Galera Devel", -1, -1, 0); ++ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)cn, -1, ++ -1, 0); ++ if (!issuer) ++ { ++ /* Self signed */ ++ X509_set_issuer_name(x509, name); ++ } ++ else ++ { ++ X509_set_issuer_name(x509, X509_get_subject_name(issuer)); ++ } ++ ++ set_x509v3_extensions(x509, issuer); ++ ++ X509_sign(x509, pkey, EVP_sha256()); ++ ++ return x509; ++} ++ ++static void write_x509(X509* x509, const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string file_path = cert_dir + "/" + filename; ++ auto* file = open_file(file_path, "wb"); ++ if (!PEM_write_X509(file, x509)) ++ { ++ throw_error("Could not write x509"); ++ } ++ fclose(file); ++} ++ ++static void write_x509_list(const std::vector& certs, ++ const std::string& filename) ++{ ++ const std::string cert_dir = get_cert_dir(); ++ const std::string file_path = cert_dir + "/" + filename; ++ auto* file = open_file(file_path, "wb"); ++ for (auto* x509 : certs) ++ { ++ if (!PEM_write_X509(file, x509)) ++ { ++ throw_error("Could not write x509"); ++ } ++ } ++ fclose(file); ++} ++ ++/* Self signed CA + certificate */ ++static void generate_self_signed() ++{ ++ auto* pkey = create_key(); ++ write_key(pkey, "galera_key.pem"); ++ auto* ca = create_x509(pkey, nullptr, "Galera Root"); ++ write_x509(ca, "galera_ca.pem"); ++ ++ auto* cert = create_x509(pkey, ca, "Galera Cert"); ++ write_x509(cert, "galera_cert.pem"); ++ X509_free(cert); ++ X509_free(ca); ++ EVP_PKEY_free(pkey); ++} ++ ++/* ++ ---- Server cert 1 ++ / ++ Root CA - Intermediate CA ++ \---- Server cert 2 ++ ++ Two bundles consisting of intermediate CA and server certificate ++ are created for servers 1 and 2. ++ */ ++static void generate_chains() ++{ ++ auto* root_ca_key = create_key(); ++ auto* root_ca = create_x509(root_ca_key, nullptr, "Galera Root CA"); ++ auto* int_ca_key = create_key(); ++ auto* int_ca = create_x509(int_ca_key, root_ca, "Galera Intermediate CA"); ++ ++ auto* server_1_key = create_key(); ++ auto* server_1_cert = create_x509(server_1_key, int_ca, "Galera Server 1"); ++ auto* server_2_key = create_key(); ++ auto* server_2_cert = create_x509(server_2_key, int_ca, "Galera Server 2"); ++ ++ write_x509(root_ca, "galera-ca.pem"); ++ write_key(server_1_key, "galera-server-1.key"); ++ write_x509_list({ server_1_cert, int_ca }, "bundle-galera-server-1.pem"); ++ write_key(server_2_key, "galera-server-2.key"); ++ write_x509_list({ server_2_cert, int_ca }, "bundle-galera-server-2.pem"); ++ ++ X509_free(server_2_cert); ++ EVP_PKEY_free(server_2_key); ++ X509_free(server_1_cert); ++ EVP_PKEY_free(server_1_key); ++ X509_free(int_ca); ++ EVP_PKEY_free(int_ca_key); ++ X509_free(root_ca); ++ EVP_PKEY_free(root_ca_key); ++} ++ ++static void generate_certificates() ++{ ++ generate_self_signed(); ++ generate_chains(); ++} ++ ++// ++// SSL ++// ++ + static gu::Config get_ssl_config() + { + gu::Config ret; +@@ -2173,6 +2431,7 @@ Suite* gu_asio_suite() + // + // SSL + // ++ generate_certificates(); + + tc = tcase_create("test_ssl_io_service"); + tcase_add_test(tc, test_ssl_io_service); diff --git a/galera.spec b/galera.spec index e9a8ed2..8f17df6 100644 --- a/galera.spec +++ b/galera.spec @@ -16,6 +16,7 @@ Source1: garbd.service Source2: garbd-wrapper Patch0: cmake_paths.patch +Patch1: 4e214d612359a294312943271abaf40685115fda.patch BuildRequires: boost-devel check-devel openssl-devel cmake systemd gcc-c++ asio-devel Requires(pre): /usr/sbin/useradd @@ -35,6 +36,7 @@ description of Galera replication engine see https://www.galeracluster.com web. %prep %setup -q %patch0 -p1 +%patch1 -p1 %build %{set_build_flags}