diff --git a/cmake/GrpcTargets.cmake b/cmake/GrpcTargets.cmake index 50cf9a8f56b9..4d5a5832cd50 100644 --- a/cmake/GrpcTargets.cmake +++ b/cmake/GrpcTargets.cmake @@ -160,6 +160,7 @@ function(userver_generate_grpc_files) "--plugin=protoc-gen-grpc=${PROTO_GRPC_CPP_PLUGIN}" "--plugin=protoc-gen-usrv=${PROTO_GRPC_USRV_PLUGIN}" "--plugin=protoc-gen-grpc_python=${PROTO_GRPC_PYTHON_PLUGIN}" + "--experimental_allow_proto3_optional" ) set(proto_abs_paths) diff --git a/grpc/include/userver/ugrpc/server/impl/service_worker.hpp b/grpc/include/userver/ugrpc/server/impl/service_worker.hpp index 5ccdb836d967..c7b613e3adad 100644 --- a/grpc/include/userver/ugrpc/server/impl/service_worker.hpp +++ b/grpc/include/userver/ugrpc/server/impl/service_worker.hpp @@ -31,6 +31,7 @@ struct ServiceSettings final { Middlewares middlewares; logging::LoggerPtr access_tskv_logger; const dynamic_config::Source config_source; + std::optional end_point; }; /// @brief Listens to requests for a gRPC service, forwarding them to a @@ -49,6 +50,8 @@ class ServiceWorker { /// Get the static per-gRPC-service metadata provided by codegen virtual const ugrpc::impl::StaticServiceMetadata& GetMetadata() const = 0; + virtual const std::optional EndPoint() const = 0; + /// Start serving requests. Should be called after the grpcpp server starts. virtual void Start() = 0; }; diff --git a/grpc/include/userver/ugrpc/server/impl/service_worker_impl.hpp b/grpc/include/userver/ugrpc/server/impl/service_worker_impl.hpp index 3618d789eb82..5eae6390936f 100644 --- a/grpc/include/userver/ugrpc/server/impl/service_worker_impl.hpp +++ b/grpc/include/userver/ugrpc/server/impl/service_worker_impl.hpp @@ -253,6 +253,10 @@ class ServiceWorkerImpl final : public ServiceWorker { return service_data_.metadata; } + const std::optional EndPoint() const override { + return service_data_.settings.end_point; + } + void Start() override { start_(); } private: diff --git a/grpc/include/userver/ugrpc/server/server.hpp b/grpc/include/userver/ugrpc/server/server.hpp index 769d6a941ce1..f2f81f80bcdd 100644 --- a/grpc/include/userver/ugrpc/server/server.hpp +++ b/grpc/include/userver/ugrpc/server/server.hpp @@ -23,10 +23,37 @@ #include #include +#include +#include +#include +#include +#include + USERVER_NAMESPACE_BEGIN namespace ugrpc::server { +struct SslConf +{ + int port; + std::string server_cert; + std::string server_private_key; + std::string client_root_cert; + bool need_verify_client_cert; +}; + +inline SslConf Parse(const userver::yaml_config::YamlConfig& cfg, userver::formats::parse::To) +{ + return SslConf + { + cfg["port"].As(), + cfg["server_cert"].As(), + cfg["server_private_key"].As(), + cfg["client_root_cert"].As(""), + cfg["verify_client_cert"].As(false) + }; +} + /// Settings relating to the whole gRPC server struct ServerConfig final { /// The port to listen to. If `0`, a free port will be picked automatically. @@ -34,6 +61,8 @@ struct ServerConfig final { /// Server::WithServerBuilder. std::optional port{0}; + std::optional sslConf; + /// Absolute path to the unix socket to listen to. /// A server can listen to both port and unix socket simultaneously. std::optional unix_socket_path{std::nullopt}; diff --git a/grpc/include/userver/ugrpc/server/service_base.hpp b/grpc/include/userver/ugrpc/server/service_base.hpp index 929515f613e8..ca278e703a36 100644 --- a/grpc/include/userver/ugrpc/server/service_base.hpp +++ b/grpc/include/userver/ugrpc/server/service_base.hpp @@ -19,6 +19,7 @@ struct ServiceConfig final { /// Server middlewares to use for the gRPC service. Middlewares middlewares; + std::optional end_point; }; /// @brief The type-erased base class for all gRPC service implementations diff --git a/grpc/src/ugrpc/server/impl/parse_config.cpp b/grpc/src/ugrpc/server/impl/parse_config.cpp index ab4cc73eca97..678b341dd436 100644 --- a/grpc/src/ugrpc/server/impl/parse_config.cpp +++ b/grpc/src/ugrpc/server/impl/parse_config.cpp @@ -88,6 +88,7 @@ server::ServiceConfig ParseServiceConfig( MergeField(value[kMiddlewaresKey], defaults.middleware_names, context, ParseMiddlewares), context), + value["end-point"].As>() }; } @@ -97,6 +98,7 @@ ServerConfig ParseServerConfig(const yaml_config::YamlConfig& value, config.unix_socket_path = value["unix-socket-path"].As>(); config.port = value["port"].As>(); + config.sslConf = value["ssl-conf"].As>(); config.completion_queue_num = value["completion-queue-count"].As(2); config.channel_args = value["channel-args"].As({}); diff --git a/grpc/src/ugrpc/server/server.cpp b/grpc/src/ugrpc/server/server.cpp index b8d1b3898e9a..440ff4e6ec42 100644 --- a/grpc/src/ugrpc/server/server.cpp +++ b/grpc/src/ugrpc/server/server.cpp @@ -25,6 +25,14 @@ #include #include + +#include +#include +#include +#include +#include +#include + USERVER_NAMESPACE_BEGIN namespace ugrpc::server { @@ -106,6 +114,8 @@ class Server::Impl final { void AddListeningPort(int port); + void AddSslConfiguration(const SslConf& conf); + void AddListeningUnixSocket(std::string_view path); void DoStart(); @@ -113,6 +123,7 @@ class Server::Impl final { State state_{State::kConfiguration}; std::optional server_builder_; std::optional port_; + std::optional ssl_port_; std::vector> service_workers_; std::optional queue_; std::unique_ptr server_; @@ -148,6 +159,11 @@ Server::Impl::Impl(ServerConfig&& config, if (config.unix_socket_path) AddListeningUnixSocket(*config.unix_socket_path); if (config.port) AddListeningPort(*config.port); + + if (config.sslConf) + { + AddSslConfiguration(*config.sslConf); + } } Server::Impl::~Impl() { @@ -159,6 +175,47 @@ Server::Impl::~Impl() { } } +void Server::Impl::AddSslConfiguration(const SslConf& config) +{ + LOG_INFO() << "Configuring the gRPC server with ssl config"; + UINVARIANT(config.port >= 0 && config.port <= 65535, "Invalid gRPC listening ssl port"); + UASSERT_MSG(!ssl_port_, + "As of now, AddSslConfiguration can be called no more than once"); + ssl_port_ = config.port; + grpc::SslServerCredentialsOptions ssl_opts; + try { + auto server_key = + userver::fs::blocking::ReadFileContents(config.server_private_key); + auto server_cert = + userver::fs::blocking::ReadFileContents(config.server_cert); + if (config.need_verify_client_cert) { + ssl_opts.client_certificate_request = + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY; + if (!config.client_root_cert.empty()) { + ssl_opts.pem_root_certs = + userver::fs::blocking::ReadFileContents(config.client_root_cert); + } else { + LOG_INFO() << "Client root cert is not provided, try to find it in system certs"; + } + } else { + ssl_opts.client_certificate_request = + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; + } + grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {server_key, + server_cert}; + ssl_opts.pem_key_cert_pairs.push_back(pkcp); + } + catch (const std::exception& ex) { + LOG_ERROR() << "The gRPC server failed to add ssl configuration. " << ex; + throw; + } + auto server_creds = SslServerCredentials(ssl_opts); + const auto uri = fmt::format("[::]:{}", ssl_port_.value()); + LOG_INFO() << "Add ssl listening port "<AddListeningPort(ugrpc::impl::ToGrpcString(uri), + server_creds, &*ssl_port_); +} + void Server::Impl::AddListeningPort(int port) { std::lock_guard lock(configuration_mutex_); UASSERT(state_ == State::kConfiguration); @@ -200,6 +257,7 @@ void Server::Impl::AddService(ServiceBase& service, ServiceConfig&& config) { std::move(config.middlewares), access_tskv_logger_, config_source_, + config.end_point })); } @@ -290,7 +348,17 @@ void Server::Impl::DoStart() { "Multiple services have been registered " "for the same gRPC method"); for (auto& worker : service_workers_) { - server_builder_->RegisterService(&worker->GetService()); + auto end_point = worker->EndPoint(); + if(end_point) + { + LOG_INFO() << "Register service to endpoint "<RegisterService(end_point.value(), &worker->GetService()); + } + else + { + LOG_INFO() << "Register service wo endpoint "; + server_builder_->RegisterService(&worker->GetService()); + } } server_ = server_builder_->BuildAndStart(); diff --git a/grpc/src/ugrpc/server/server_component.cpp b/grpc/src/ugrpc/server/server_component.cpp index 5bcb91f6fa7a..6331329c3e9c 100644 --- a/grpc/src/ugrpc/server/server_component.cpp +++ b/grpc/src/ugrpc/server/server_component.cpp @@ -45,6 +45,26 @@ additionalProperties: false port: type: integer description: the port to use for all gRPC services, or 0 to pick any available + ssl-conf: + type: object + description: ssl conf for grpc server + properties: + port: + type: integer + description: the port to use for all gRPC services, or 0 to pick any available + server_cert: + type: string + description: server ssl cert + server_private_key: + type: string + description: server ssl private key + client_root_cert: + type: string + description: client ssl root cert + verify_client_cert: + type: boolean + description: verify client cert + additionalProperties: false unix-socket-path: type: string description: unix socket absolute path diff --git a/grpc/src/ugrpc/server/service_component_base.cpp b/grpc/src/ugrpc/server/service_component_base.cpp index f774d06a9ddb..c62bd0da277d 100644 --- a/grpc/src/ugrpc/server/service_component_base.cpp +++ b/grpc/src/ugrpc/server/service_component_base.cpp @@ -42,6 +42,9 @@ additionalProperties: false items: type: string description: middleware component name + end-point: + type: string + description: service endpoint )"); }