Skip to content

Commit

Permalink
Add support for client-side cert/key to gRPC exports.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kylepl committed Jun 21, 2023
1 parent cfcda57 commit 8dc2ca9
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#pragma once

#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include "opentelemetry/exporters/otlp/otlp_environment.h"

#include <memory>
Expand All @@ -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<absl::variant<>>.
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<Unset, FilePath, FileContents> pem_root_certs;

// Path to or contents of a .pem file containing the client's private key.
absl::variant<Unset, FilePath, FileContents> pem_private_key;

// Path to or contents of a .pem file containing the client's certificate chain.
absl::variant<Unset, FilePath, FileContents> pem_cert_chain;
};

/**
* Struct to hold OTLP GRPC traces exporter options.
*/
Expand All @@ -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<SSLCredentials> ssl_credentials;
// Timeout for grpc deadline
std::chrono::system_clock::duration timeout = GetOtlpDefaultTimeout();
// Additional HTTP headers
Expand Down
31 changes: 30 additions & 1 deletion exporters/otlp/src/otlp_grpc_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ static std::string GetFileContents(const char *fpath)
finstream.close();
return contents;
}

static std::optional<std::string> GetData(
const std::variant<Unset, FilePath, FileContents>& data_reference) {
if (std::holds_alternative<FilePath>(data_reference)) {
return GetFileContents(std::get<FilePath>(data_reference).value.c_str());
} else if (std::holds_alternative<FileContents>(data_reference)) {
return std::get<FileContents>(data_reference).value;
}
return std::nullopt;
}

} // namespace

std::shared_ptr<grpc::Channel> OtlpGrpcClient::MakeChannel(const OtlpGrpcExporterOptions &options)
Expand All @@ -58,7 +69,25 @@ std::shared_ptr<grpc::Channel> 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())
Expand Down

0 comments on commit 8dc2ca9

Please sign in to comment.