From 8dc2ca92aaea99360611dc2b3360c21f9132e49d Mon Sep 17 00:00:00 2001 From: Kyle Loveless Date: Tue, 25 Apr 2023 13:55:57 -0400 Subject: [PATCH] Add support for client-side cert/key to gRPC exports. NOTE: This has many TODOs to resolve, they are meant as questions to the reviewers. I have not run or written any tests, other than verifying it worked once in my production system. --- .../otlp/otlp_grpc_exporter_options.h | 33 +++++++++++++++++++ exporters/otlp/src/otlp_grpc_client.cc | 31 ++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h index f5a2b9d295..7195289413 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h @@ -3,6 +3,8 @@ #pragma once +#include "absl/types/optional.h" +#include "absl/types/variant.h" #include "opentelemetry/exporters/otlp/otlp_environment.h" #include @@ -13,6 +15,34 @@ namespace exporter namespace otlp { +// This type allows the variants to be default-constructed to neither type. +// This avoids needing deeper template if absl::optional>. +struct Unset {}; + +// TODO: Check if this strong-typing is ok. It seems nice, and allows variant typing. +struct FilePath { + std::string value; +}; + +struct FileContents { + std::string value; +}; + +// TODO: Should this be called TSL if we are already changing things? +// TODO: This is a separate structure to group them together, and is similar +// to gRPC SslCredentialsOptions. +struct SSLCredentials { + // Path to or contents of a .pem file of the root certs used to verify the service side + // certificate. + absl::variant pem_root_certs; + + // Path to or contents of a .pem file containing the client's private key. + absl::variant pem_private_key; + + // Path to or contents of a .pem file containing the client's certificate chain. + absl::variant pem_cert_chain; +}; + /** * Struct to hold OTLP GRPC traces exporter options. */ @@ -23,11 +53,14 @@ struct OtlpGrpcExporterOptions // By default when false, uses grpc::InsecureChannelCredentials(); If true, // uses ssl_credentials_cacert_path if non-empty, else uses ssl_credentials_cacert_as_string bool use_ssl_credentials = GetOtlpDefaultIsSslEnable(); + // TODO: Have a deprecation path for these variables, including a warning. // ssl_credentials_cacert_path specifies path to .pem file to be used for SSL encryption. std::string ssl_credentials_cacert_path = GetOtlpDefaultSslCertificatePath(); // ssl_credentials_cacert_as_string in-memory string representation of .pem file to be used for // SSL encryption. std::string ssl_credentials_cacert_as_string = GetOtlpDefaultSslCertificateString(); + // The credentials to secure the connection. + absl::optional ssl_credentials; // Timeout for grpc deadline std::chrono::system_clock::duration timeout = GetOtlpDefaultTimeout(); // Additional HTTP headers diff --git a/exporters/otlp/src/otlp_grpc_client.cc b/exporters/otlp/src/otlp_grpc_client.cc index 9250f87cc4..053a0abf61 100644 --- a/exporters/otlp/src/otlp_grpc_client.cc +++ b/exporters/otlp/src/otlp_grpc_client.cc @@ -34,6 +34,17 @@ static std::string GetFileContents(const char *fpath) finstream.close(); return contents; } + +static std::optional GetData( + const std::variant& data_reference) { + if (std::holds_alternative(data_reference)) { + return GetFileContents(std::get(data_reference).value.c_str()); + } else if (std::holds_alternative(data_reference)) { + return std::get(data_reference).value; + } + return std::nullopt; +} + } // namespace std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcExporterOptions &options) @@ -58,7 +69,25 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcExporte grpc::ChannelArguments grpc_arguments; grpc_arguments.SetUserAgentPrefix(options.user_agent); - if (options.use_ssl_credentials) + if (options.ssl_credentials) { + grpc::SslCredentialsOptions ssl_opts; + auto pem_root_certs = GetData(options.ssl_credentials->pem_root_certs); + if (pem_root_certs) { + ssl_opts.pem_root_certs = *pem_root_certs; + } + + auto pem_private_key = GetData(options.ssl_credentials->pem_private_key); + if (pem_private_key) { + ssl_opts.pem_private_key = *pem_private_key; + } + + auto pem_cert_chain = GetData(options.ssl_credentials->pem_cert_chain); + if (pem_cert_chain) { + ssl_opts.pem_cert_chain = *pem_cert_chain; + } + channel = + grpc::CreateCustomChannel(grpc_target, grpc::SslCredentials(ssl_opts), grpc_arguments); + } else if (options.use_ssl_credentials) { grpc::SslCredentialsOptions ssl_opts; if (options.ssl_credentials_cacert_path.empty())