diff --git a/bazel/cert.bzl b/bazel/cert.bzl new file mode 100644 index 000000000000..e414a3cf08f3 --- /dev/null +++ b/bazel/cert.bzl @@ -0,0 +1,128 @@ +""" +This module contains functions to generate a simple CA +""" + +# buildifier: disable=function-docstring-args +def _redpanda_private_key(name, certificate): + private_key = certificate + ".key" + + pr_key_gen = name + "_key_gen" + native.genrule( + name = pr_key_gen, + srcs = [], + outs = [private_key], + cmd = "$(execpath @openssl//:openssl_exe) ecparam " + + "-name prime256v1 " + + "-genkey " + + "-noout " + + "-out \"$@\"", + tools = [ + "@openssl//:openssl_exe", + ], + ) + + return pr_key_gen + +def redpanda_selfsigned_cert(name, certificate, common_name, visibility = None): + """ + Generate a Redpanda self-signed certificate. + + Args: + name: name of the target + certificate: name to use for output files (crt, key, and csr) + common_name: the CN to use when setting the subject name + visibility: visibility setting + """ + + cert = certificate + ".crt" + subj = "/C=US/ST=California/L=San Francisco/O=Redpanda Data/OU=Core/CN=" + common_name + + pr_key_gen = _redpanda_private_key(name, certificate) + + crt_gen = name + "_crt_gen" + native.genrule( + name = crt_gen, + srcs = [ + pr_key_gen, + ], + outs = [cert], + cmd = "$(execpath @openssl//:openssl_exe) req " + + "-new -x509 -sha256 " + + "-key $(SRCS) " + + "-out \"$@\" " + + "-subj \"{}\" ".format(subj) + + "-addext \"subjectAltName = IP:127.0.0.1\"", + tools = [ + "@openssl//:openssl_exe", + ], + ) + + native.filegroup( + name = name, + srcs = [pr_key_gen, crt_gen], + visibility = visibility, + ) + +def redpanda_signed_cert(name, certificate, common_name, ca, serial_number, visibility = None): + """ + Generate a Redpanda signed certificate. + + Args: + name: name of the target + certificate: name to use for output files (crt, key, and csr) + common_name: the CN to use when setting the subject name + ca: the certificate to be used as the signing CA + serial_number: the serial number of cert when issued by CA + visibility: visibility setting + """ + + subj = "/C=US/ST=California/L=San Francisco/O=Redpanda Data/OU=Core/CN=" + common_name + + pr_key_gen = _redpanda_private_key(name, certificate) + + req_gen = name + "_csr_gen" + native.genrule( + name = req_gen, + srcs = [ + pr_key_gen, + ], + outs = [certificate + ".csr"], + cmd = "$(execpath @openssl//:openssl_exe) req " + + "-new -sha256 " + + "-key $(SRCS) " + + "-out \"$@\" " + + "-subj \"{}\" ".format(subj), + tools = [ + "@openssl//:openssl_exe", + ], + ) + + ca_cert = ca + ".crt" + ca_private_key = ca + ".key" + + crt_gen = name + "_crt_gen" + native.genrule( + name = crt_gen, + srcs = [ + ca_cert, + ca_private_key, + req_gen, + ], + outs = [certificate + ".crt"], + cmd = "$(execpath @openssl//:openssl_exe) x509 " + + "-req -days 1000 -sha256 " + + "-set_serial {} ".format(serial_number) + + "-in $(execpath {}) ".format(req_gen) + + "-CA $(execpaths :{}) ".format(ca_cert) + + "-CAkey $(execpaths :{}) ".format(ca_private_key) + + "-out \"$@\" ", + tools = [ + "@openssl//:openssl_exe", + ], + ) + + native.filegroup( + name = name, + srcs = [pr_key_gen, req_gen, crt_gen], + visibility = visibility, + ) diff --git a/bazel/thirdparty/openssl.BUILD b/bazel/thirdparty/openssl.BUILD index dba38b38397f..5eb89bdad6d9 100644 --- a/bazel/thirdparty/openssl.BUILD +++ b/bazel/thirdparty/openssl.BUILD @@ -1,4 +1,5 @@ load("@bazel_skylib//rules:common_settings.bzl", "int_flag", "string_flag") +load("@bazel_skylib//rules:select_file.bzl", "select_file") load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") # Make this build faster by setting `build --@openssl//:build_jobs=16` in user.bazelrc @@ -55,6 +56,9 @@ configure_make( "OPENSSL_BUILD_JOBS": "$(BUILD_JOBS)", }, lib_source = ":srcs", + out_binaries = [ + "openssl", + ], out_shared_libs = [ "libssl.so.3", "libcrypto.so.3", @@ -64,3 +68,21 @@ configure_make( "//visibility:public", ], ) + +filegroup( + name = "gen_dir", + srcs = [":openssl"], + output_group = "gen_dir", + visibility = [ + "//visibility:public", + ], +) + +select_file( + name = "openssl_exe", + srcs = ":openssl", + subpath = "bin/openssl", + visibility = [ + "//visibility:public", + ], +) diff --git a/src/v/rpc/compiler.bzl b/src/v/rpc/compiler.bzl index c98dad3f9ef0..ce53be135b5f 100644 --- a/src/v/rpc/compiler.bzl +++ b/src/v/rpc/compiler.bzl @@ -4,7 +4,7 @@ This module contains functions for working with Redpanda RPC system. load("//bazel:build.bzl", "redpanda_cc_library") -def redpanda_cc_rpc_library(name, src, out = None, include_prefix = None, visibility = None): +def redpanda_cc_rpc_library(name, src, out = None, deps = [], include_prefix = None, visibility = None): """ Generate Redpanda RPC library. @@ -12,6 +12,7 @@ def redpanda_cc_rpc_library(name, src, out = None, include_prefix = None, visibi name: name of the library src: rpc specification json file out: output header name. defaults to src_service.h (without .json extension) + deps: dependencies defined in the json src file include_prefix: include_prefix of generated header visibility: visibility setting """ @@ -30,9 +31,18 @@ def redpanda_cc_rpc_library(name, src, out = None, include_prefix = None, visibi tools = ["//src/v/rpc:compiler"], ) + rpc_template_deps = [ + "//src/v/config", + "//src/v/metrics", + "//src/v/rpc", + "//src/v/finjector", + "//src/v/random:fast_prng", + ] + redpanda_cc_library( name = name, hdrs = [out], + deps = rpc_template_deps + deps, include_prefix = include_prefix, visibility = visibility, ) diff --git a/src/v/rpc/test/BUILD b/src/v/rpc/test/BUILD index 480dbf94a229..6c2105f3468c 100644 --- a/src/v/rpc/test/BUILD +++ b/src/v/rpc/test/BUILD @@ -1,6 +1,23 @@ -load("//bazel:test.bzl", "redpanda_cc_bench", "redpanda_cc_btest") +load("//bazel:build.bzl", "redpanda_cc_library") +load("//bazel:cert.bzl", "redpanda_selfsigned_cert", "redpanda_signed_cert") +load("//bazel:test.bzl", "redpanda_cc_bench", "redpanda_cc_btest", "redpanda_test_cc_library") load("//src/v/rpc:compiler.bzl", "redpanda_cc_rpc_library") +redpanda_test_cc_library( + name = "rpc_integration_fixture", + hdrs = [ + "rpc_integration_fixture.h", + ], + include_prefix = "rpc/test", + deps = [ + "//src/v/base", + "//src/v/config", + "//src/v/net", + "//src/v/rpc", + "@seastar", + ], +) + redpanda_cc_btest( name = "netbuf_test", timeout = "short", @@ -101,6 +118,37 @@ redpanda_cc_btest( ], ) +redpanda_cc_btest( + name = "rpc_gen_cycling_test", + timeout = "short", + srcs = [ + "rpc_gen_cycling_test.cc", + ], + cpu = 1, + data = [ + ":cert", + ":cert_ca", + ":other_cert", + ":other_cert_ca", + ], + tags = ["exclusive"], + deps = [ + ":cycling_rpc", + ":echo_rpc", + ":echo_v2_rpc", + ":rpc_integration_fixture", + "//src/v/bytes:random", + "//src/v/model", + "//src/v/random:generators", + "//src/v/rpc", + "//src/v/test_utils:fixture", + "//src/v/test_utils:seastar_boost", + "@boost//:test", + "@seastar", + "@seastar//:testing", + ], +) + redpanda_cc_bench( name = "rpc_bench", timeout = "short", @@ -114,17 +162,77 @@ redpanda_cc_bench( ], ) +redpanda_cc_library( + name = "rpc_gen_types", + hdrs = [ + "rpc_gen_types.h", + ], + include_prefix = "rpc/test", + deps = [ + "//src/v/base", + "//src/v/reflection:adl", + "//src/v/rpc", + "//src/v/serde", + "//src/v/serde:enum", + "//src/v/serde:sstring", + "@seastar", + ], +) + redpanda_cc_rpc_library( name = "cycling_rpc", src = "cycling_service.json", + out = "cycling_service.h", + include_prefix = "rpc/test", + deps = [ + ":rpc_gen_types", + ], ) redpanda_cc_rpc_library( name = "echo_rpc", src = "echo_service.json", + out = "echo_service.h", + include_prefix = "rpc/test", + deps = [ + ":rpc_gen_types", + ], ) redpanda_cc_rpc_library( name = "echo_v2_rpc", src = "echo_v2_service.json", + out = "echo_v2_service.h", + include_prefix = "rpc/test", + deps = [ + ":rpc_gen_types", + ], +) + +redpanda_selfsigned_cert( + name = "cert_ca", + certificate = "root_certificate_authority", + common_name = "redpanda.com", +) + +redpanda_selfsigned_cert( + name = "other_cert_ca", + certificate = "root_certificate_authority.other", + common_name = "redpanda.other.com", +) + +redpanda_signed_cert( + name = "cert", + ca = "root_certificate_authority", + certificate = "redpanda", + common_name = "cert.com", + serial_number = 1, +) + +redpanda_signed_cert( + name = "other_cert", + ca = "root_certificate_authority.other", + certificate = "redpanda.other", + common_name = "cert.other.com", + serial_number = 2, ) diff --git a/src/v/rpc/test/cycling_service.json b/src/v/rpc/test/cycling_service.json index 3921be13bdbf..ce47533c05c9 100644 --- a/src/v/rpc/test/cycling_service.json +++ b/src/v/rpc/test/cycling_service.json @@ -2,7 +2,7 @@ "namespace": "cycling", "service_name": "team_movistar", "includes": [ - "rpc_gen_types.h" + "rpc/test/rpc_gen_types.h" ], "methods": [ { diff --git a/src/v/rpc/test/echo_service.json b/src/v/rpc/test/echo_service.json index cde1d3e5e817..b73f8eb669f1 100644 --- a/src/v/rpc/test/echo_service.json +++ b/src/v/rpc/test/echo_service.json @@ -2,7 +2,7 @@ "namespace": "echo", "service_name": "echo", "includes": [ - "rpc_gen_types.h" + "rpc/test/rpc_gen_types.h" ], "methods": [ { diff --git a/src/v/rpc/test/echo_v2_service.json b/src/v/rpc/test/echo_v2_service.json index 288d9829c4a7..2d77c67af0d9 100644 --- a/src/v/rpc/test/echo_v2_service.json +++ b/src/v/rpc/test/echo_v2_service.json @@ -2,7 +2,7 @@ "namespace": "echo_v2", "service_name": "echo", "includes": [ - "rpc_gen_types.h" + "rpc/test/rpc_gen_types.h" ], "methods": [ { diff --git a/src/v/rpc/test/rpc_gen_cycling_test.cc b/src/v/rpc/test/rpc_gen_cycling_test.cc index 6b5088a7c07c..f1b87a445e92 100644 --- a/src/v/rpc/test/rpc_gen_cycling_test.cc +++ b/src/v/rpc/test/rpc_gen_cycling_test.cc @@ -18,10 +18,8 @@ #include "rpc/test/cycling_service.h" #include "rpc/test/echo_service.h" #include "rpc/test/echo_v2_service.h" +#include "rpc/test/rpc_integration_fixture.h" #include "rpc/types.h" -#include "rpc_gen_types.h" -#include "rpc_integration_fixture.h" -#include "serde/rw/iobuf.h" #include "test_utils/async.h" #include "test_utils/fixture.h" @@ -135,6 +133,41 @@ class rpc_integration_fixture : public rpc_simple_integration_fixture { static constexpr uint16_t redpanda_rpc_port = 32147; }; +struct certificate { + ss::sstring key; + ss::sstring crt; + ss::sstring ca; +}; + +namespace { +ss::sstring root_path() { + // if this file exists, we are running in cmake + if (std::filesystem::exists("redpanda.key")) { + return ""; + } + + // otherwise we are running in bazel and we need + // the full path + return "src/v/rpc/test/"; +} + +certificate redpanda_cert() { + const auto root = root_path(); + return { + .key = root + "redpanda.key", + .crt = root + "redpanda.crt", + .ca = root + "root_certificate_authority.crt"}; +} + +certificate redpanda_other_cert() { + const auto root = root_path(); + return { + .key = root + "redpanda.other.key", + .crt = root + "redpanda.other.crt", + .ca = root + "root_certificate_authority.other.crt"}; +} +} // namespace + FIXTURE_TEST(echo_round_trip, rpc_integration_fixture) { configure_server(); register_services(); @@ -237,7 +270,7 @@ FIXTURE_TEST(echo_from_cache, rpc_integration_fixture) { // Check that we can create connections from a cache, and moreover that we // can run several clients targeted at the same server, if we provide // multiple node IDs to the cache. - constexpr const size_t num_nodes_ids = 10; + constexpr const int num_nodes_ids = 10; const auto ccfg = client_config(); for (int i = 0; i < num_nodes_ids; ++i) { const auto payload = random_generators::gen_alphanum_string(100); @@ -301,10 +334,11 @@ FIXTURE_TEST(rpc_abort_from_cache, rpc_integration_fixture) { } FIXTURE_TEST(echo_round_trip_tls, rpc_integration_fixture) { + const auto cert = redpanda_cert(); auto creds_builder = config::tls_config( true, - config::key_cert{"redpanda.key", "redpanda.crt"}, - "root_certificate_authority.crt", + config::key_cert{cert.key, cert.crt}, + cert.ca, std::nullopt, /* CRL */ false) .get_credentials_builder() @@ -367,16 +401,18 @@ struct certificate_reload_ctx { }; FIXTURE_TEST(rpcgen_reload_credentials_integration, rpc_integration_fixture) { + const certificate cert = redpanda_cert(); + const certificate other_cert = redpanda_other_cert(); + // Server starts with bad credentials, files are updated on disk and then // client connects. Expected behavior is that client can connect without // issues. Condition variable is used to wait for credentials to reload. auto context = ss::make_lw_shared(); temporary_dir tmp; // client credentials - auto client_key = tmp.copy_file("redpanda.key", "client.key"); - auto client_crt = tmp.copy_file("redpanda.crt", "client.crt"); - auto client_ca = tmp.copy_file( - "root_certificate_authority.crt", "ca_client.pem"); + auto client_key = tmp.copy_file(cert.key.c_str(), "client.key"); + auto client_crt = tmp.copy_file(cert.crt.c_str(), "client.crt"); + auto client_ca = tmp.copy_file(cert.ca.c_str(), "ca_client.pem"); auto client_creds_builder = config::tls_config( true, config::key_cert{ @@ -387,10 +423,9 @@ FIXTURE_TEST(rpcgen_reload_credentials_integration, rpc_integration_fixture) { .get_credentials_builder() .get(); // server credentials - auto server_key = tmp.copy_file("redpanda.other.key", "server.key"); - auto server_crt = tmp.copy_file("redpanda.other.crt", "server.crt"); - auto server_ca = tmp.copy_file( - "root_certificate_authority.other.crt", "ca_server.pem"); + auto server_key = tmp.copy_file(other_cert.key.c_str(), "server.key"); + auto server_crt = tmp.copy_file(other_cert.crt.c_str(), "server.crt"); + auto server_ca = tmp.copy_file(other_cert.ca.c_str(), "ca_server.pem"); auto server_creds_builder = config::tls_config( true, config::key_cert{ @@ -432,9 +467,9 @@ FIXTURE_TEST(rpcgen_reload_credentials_integration, rpc_integration_fixture) { // fix client credentials and reconnect info("replacing files"); - tmp.copy_file("redpanda.key", "server.key"); - tmp.copy_file("redpanda.crt", "server.crt"); - tmp.copy_file("root_certificate_authority.crt", "ca_server.pem"); + tmp.copy_file(cert.key.c_str(), "server.key"); + tmp.copy_file(cert.crt.c_str(), "server.crt"); + tmp.copy_file(cert.ca.c_str(), "ca_server.pem"); context->cvar.wait([context] { return context->updated.size() == 3; }) .get();