Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mTLS REST prototype #15054

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions google/cloud/credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ std::shared_ptr<Credentials> MakeApiKeyCredentials(std::string api_key,
std::move(opts));
}

std::shared_ptr<Credentials> MakeMtlsCredentials(
ExperimentalTag, MtlsCredentialsConfig const& config, Options opts) {
return std::make_shared<internal::MtlsConfig>(config, std::move(opts));
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
} // namespace google
5 changes: 5 additions & 0 deletions google/cloud/credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_CREDENTIALS_H

#include "google/cloud/common_options.h"
#include "google/cloud/mtls_credentials_config.h"
#include "google/cloud/options.h"
#include "google/cloud/version.h"
#include "absl/types/variant.h"
#include <chrono>
#include <memory>
#include <string>
Expand Down Expand Up @@ -333,6 +335,9 @@ std::shared_ptr<Credentials> MakeExternalAccountCredentials(
std::shared_ptr<Credentials> MakeApiKeyCredentials(std::string api_key,
Options opts = {});

std::shared_ptr<Credentials> MakeMtlsCredentials(
ExperimentalTag, MtlsCredentialsConfig const& config, Options opts = {});

/**
* Configure the delegates for `MakeImpersonateServiceAccountCredentials()`
*
Expand Down
2 changes: 2 additions & 0 deletions google/cloud/google_cloud_cpp_common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ google_cloud_cpp_common_hdrs = [
"kms_key_name.h",
"location.h",
"log.h",
"mtls_credentials_config.h",
"no_await_tag.h",
"opentelemetry_options.h",
"optional.h",
Expand Down Expand Up @@ -161,6 +162,7 @@ google_cloud_cpp_common_srcs = [
"kms_key_name.cc",
"location.cc",
"log.cc",
"mtls_credentials_config.cc",
"options.cc",
"project.cc",
"status.cc",
Expand Down
3 changes: 3 additions & 0 deletions google/cloud/google_cloud_cpp_common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ add_library(
location.h
log.cc
log.h
mtls_credentials_config.cc
mtls_credentials_config.h
no_await_tag.h
opentelemetry_options.h
optional.h
Expand Down Expand Up @@ -410,6 +412,7 @@ if (BUILD_TESTING)
log_test.cc
mocks/current_options_test.cc
mocks/mock_stream_range_test.cc
mtls_credentials_config_test.cc
options_test.cc
polling_policy_test.cc
project_test.cc
Expand Down
1 change: 1 addition & 0 deletions google/cloud/google_cloud_cpp_common_unit_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ google_cloud_cpp_common_unit_tests = [
"log_test.cc",
"mocks/current_options_test.cc",
"mocks/mock_stream_range_test.cc",
"mtls_credentials_config_test.cc",
"options_test.cc",
"polling_policy_test.cc",
"project_test.cc",
Expand Down
2 changes: 2 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ google_cloud_cpp_rest_internal_hdrs = [
"internal/oauth2_impersonate_service_account_credentials.h",
"internal/oauth2_logging_credentials.h",
"internal/oauth2_minimal_iam_credentials_rest.h",
"internal/oauth2_mtls_credentials.h",
"internal/oauth2_refreshing_credentials_wrapper.h",
"internal/oauth2_service_account_credentials.h",
"internal/oauth2_universe_domain.h",
Expand Down Expand Up @@ -108,6 +109,7 @@ google_cloud_cpp_rest_internal_srcs = [
"internal/oauth2_impersonate_service_account_credentials.cc",
"internal/oauth2_logging_credentials.cc",
"internal/oauth2_minimal_iam_credentials_rest.cc",
"internal/oauth2_mtls_credentials.cc",
"internal/oauth2_refreshing_credentials_wrapper.cc",
"internal/oauth2_service_account_credentials.cc",
"internal/oauth2_universe_domain.cc",
Expand Down
3 changes: 3 additions & 0 deletions google/cloud/google_cloud_cpp_rest_internal.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ add_library(
internal/oauth2_logging_credentials.h
internal/oauth2_minimal_iam_credentials_rest.cc
internal/oauth2_minimal_iam_credentials_rest.h
internal/oauth2_mtls_credentials.cc
internal/oauth2_mtls_credentials.h
internal/oauth2_refreshing_credentials_wrapper.cc
internal/oauth2_refreshing_credentials_wrapper.h
internal/oauth2_service_account_credentials.cc
Expand Down Expand Up @@ -270,6 +272,7 @@ if (BUILD_TESTING)
internal/oauth2_impersonate_service_account_credentials_test.cc
internal/oauth2_logging_credentials_test.cc
internal/oauth2_minimal_iam_credentials_rest_test.cc
internal/oauth2_mtls_credentials_test.cc
internal/oauth2_refreshing_credentials_wrapper_test.cc
internal/oauth2_service_account_credentials_test.cc
internal/oauth2_universe_domain_test.cc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ google_cloud_cpp_rest_internal_unit_tests = [
"internal/oauth2_impersonate_service_account_credentials_test.cc",
"internal/oauth2_logging_credentials_test.cc",
"internal/oauth2_minimal_iam_credentials_rest_test.cc",
"internal/oauth2_mtls_credentials_test.cc",
"internal/oauth2_refreshing_credentials_wrapper_test.cc",
"internal/oauth2_service_account_credentials_test.cc",
"internal/oauth2_universe_domain_test.cc",
Expand Down
5 changes: 5 additions & 0 deletions google/cloud/internal/credentials_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ ApiKeyConfig::ApiKeyConfig(std::string api_key, Options opts)
: api_key_(std::move(api_key)),
options_(PopulateAuthOptions(std::move(opts))) {}

MtlsConfig::MtlsConfig(google::cloud::MtlsCredentialsConfig config,
google::cloud::Options opts)
: config_(std::move(config)),
options_(PopulateAuthOptions(std::move(opts))) {}

} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
Expand Down
19 changes: 19 additions & 0 deletions google/cloud/internal/credentials_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ImpersonateServiceAccountConfig;
class ServiceAccountConfig;
class ExternalAccountConfig;
class ApiKeyConfig;
class MtlsConfig;

std::shared_ptr<Credentials> MakeErrorCredentials(Status error_status);

Expand All @@ -54,6 +55,7 @@ class CredentialsVisitor {
virtual void visit(ServiceAccountConfig const&) = 0;
virtual void visit(ExternalAccountConfig const&) = 0;
virtual void visit(ApiKeyConfig const&) = 0;
virtual void visit(MtlsConfig const&) = 0;

static void dispatch(Credentials const& credentials,
CredentialsVisitor& visitor);
Expand Down Expand Up @@ -183,6 +185,23 @@ class ApiKeyConfig : public Credentials {
Options options_;
};

class MtlsConfig : public Credentials {
public:
MtlsConfig(MtlsCredentialsConfig config, Options opts);
~MtlsConfig() override = default;

MtlsCredentialsConfig const& mtls_credentials_config() const {
return config_;
}
Options const& options() const { return options_; }

private:
void dispatch(CredentialsVisitor& v) const override { v.visit(*this); }

MtlsCredentialsConfig config_;
Options options_;
};

/// A helper function to initialize Auth options.
Options PopulateAuthOptions(Options options);

Expand Down
16 changes: 16 additions & 0 deletions google/cloud/internal/credentials_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ TEST(Credentials, ApiKeyCredentials) {
EXPECT_EQ("api-key", visitor.api_key);
}

TEST(Credentials, MtlsCredentials) {
TestCredentialsVisitor visitor;

MtlsCredentialsConfig::Rest rest_config{"my-cert-file"};
MtlsCredentialsConfig config;
config.config = std::move(rest_config);
auto credentials = MakeMtlsCredentials(ExperimentalTag{}, config);
CredentialsVisitor::dispatch(*credentials, visitor);
EXPECT_EQ("MtlsConfig", visitor.name);
auto mtls = visitor.mtls_config;
ASSERT_TRUE(
absl::holds_alternative<MtlsCredentialsConfig::Rest>(mtls.config));
EXPECT_EQ("my-cert-file", absl::get<MtlsCredentialsConfig::Rest>(mtls.config)
.ssl_client_cert_filename());
}

} // namespace
} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Expand Down
34 changes: 34 additions & 0 deletions google/cloud/internal/curl_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ void CurlImpl::SetHeaders(RestContext const& context,
}
}

void CurlImpl::SetMtlsConfig(
absl::optional<MtlsCredentialsConfig::Rest> mtls_config) {
mtls_config_ = std::move(mtls_config);
}

std::string CurlImpl::MakeEscapedString(std::string const& s) {
return handle_.MakeEscapedString(s).get();
}
Expand Down Expand Up @@ -320,6 +325,35 @@ Status CurlImpl::MakeRequest(HttpMethod method, RestContext& context,
if (!status.ok()) return OnTransferError(context, std::move(status));
}

if (mtls_config_.has_value()) {
static_assert(CURL_AT_LEAST_VERSION(7, 9, 3));
status = handle_.SetOption(CURLOPT_SSL_VERIFYPEER, 1);
if (!status.ok()) return OnTransferError(context, std::move(status));
status = handle_.SetOption(CURLOPT_SSL_VERIFYHOST, 2);
if (!status.ok()) return OnTransferError(context, std::move(status));

status = handle_.SetOption(
CURLOPT_SSLCERTTYPE,
MtlsCredentialsConfig::Rest::ToString(mtls_config_->ssl_cert_type())
.c_str());
if (!status.ok()) return OnTransferError(context, std::move(status));

status = handle_.SetOption(
CURLOPT_SSLCERT, mtls_config_->ssl_client_cert_filename().c_str());
if (!status.ok()) return OnTransferError(context, std::move(status));

if (mtls_config_->ssl_key_filename().has_value()) {
status = handle_.SetOption(CURLOPT_SSLKEY,
mtls_config_->ssl_key_filename()->c_str());
if (!status.ok()) return OnTransferError(context, std::move(status));
if (mtls_config_->ssl_key_file_password().has_value()) {
status = handle_.SetOption(
CURLOPT_KEYPASSWD, mtls_config_->ssl_key_file_password()->c_str());
if (!status.ok()) return OnTransferError(context, std::move(status));
}
}
}

if (method == HttpMethod::kGet) {
status = handle_.SetOption(CURLOPT_NOPROGRESS, 1L);
if (!status.ok()) return OnTransferError(context, std::move(status));
Expand Down
5 changes: 5 additions & 0 deletions google/cloud/internal/curl_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "google/cloud/internal/rest_context.h"
#include "google/cloud/internal/rest_request.h"
#include "google/cloud/internal/rest_response.h"
#include "google/cloud/mtls_credentials_config.h"
#include "google/cloud/options.h"
#include "google/cloud/status_or.h"
#include "google/cloud/version.h"
Expand Down Expand Up @@ -85,6 +86,8 @@ class CurlImpl {
void SetHeader(std::pair<std::string, std::string> const& header);
void SetHeaders(RestContext const& context, RestRequest const& request);

void SetMtlsConfig(absl::optional<MtlsCredentialsConfig::Rest> mtls_config);

std::string MakeEscapedString(std::string const& s);

void SetUrl(std::string const& endpoint, RestRequest const& request,
Expand Down Expand Up @@ -143,6 +146,8 @@ class CurlImpl {
absl::optional<std::string> proxy_username_;
absl::optional<std::string> proxy_password_;

absl::optional<MtlsCredentialsConfig::Rest> mtls_config_;

CurlReceivedHeaders received_headers_;
std::string url_;
HttpStatusCode http_code_;
Expand Down
1 change: 1 addition & 0 deletions google/cloud/internal/curl_rest_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ StatusOr<std::unique_ptr<CurlImpl>> CurlRestClient::CreateCurlImpl(
auto impl =
std::make_unique<CurlImpl>(std::move(handle), handle_factory_, options);
if (credentials_) {
impl->SetMtlsConfig(credentials_->MtlsConfig());
auto auth_header =
credentials_->AuthenticationHeader(std::chrono::system_clock::now());
if (!auth_header.ok()) return std::move(auth_header).status();
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/internal/oauth2_credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ StatusOr<std::pair<std::string, std::string>> Credentials::AuthenticationHeader(
absl::StrCat("Bearer ", token->token));
}

absl::optional<MtlsCredentialsConfig::Rest> Credentials::MtlsConfig() const {
return absl::nullopt;
}

StatusOr<std::string> AuthenticationHeaderJoined(
Credentials& credentials, std::chrono::system_clock::time_point tp) {
auto header = credentials.AuthenticationHeader(tp);
Expand Down
3 changes: 3 additions & 0 deletions google/cloud/internal/oauth2_credentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_CREDENTIALS_H

#include "google/cloud/access_token.h"
#include "google/cloud/mtls_credentials_config.h"
#include "google/cloud/options.h"
#include "google/cloud/status.h"
#include "google/cloud/status_or.h"
Expand Down Expand Up @@ -122,6 +123,8 @@ class Credentials {
*/
virtual StatusOr<std::pair<std::string, std::string>> AuthenticationHeader(
std::chrono::system_clock::time_point tp);

virtual absl::optional<MtlsCredentialsConfig::Rest> MtlsConfig() const;
};

/**
Expand Down
30 changes: 30 additions & 0 deletions google/cloud/internal/oauth2_mtls_credentials.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/internal/oauth2_mtls_credentials.h"

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

StatusOr<AccessToken> MtlsCredentials::GetToken(
std::chrono::system_clock::time_point tp) {
return AccessToken{std::string{}, tp};
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google
59 changes: 59 additions & 0 deletions google/cloud/internal/oauth2_mtls_credentials.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_MTLS_CREDENTIALS_H
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_MTLS_CREDENTIALS_H

#include "google/cloud/internal/oauth2_credentials.h"
#include "google/cloud/mtls_credentials_config.h"
#include "google/cloud/status_or.h"
#include "google/cloud/version.h"
#include <string>
#include <utility>

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

/**
* A `Credentials` type representing mTLS Google OAuth2.0 credentials.
*/
class MtlsCredentials : public oauth2_internal::Credentials {
public:
explicit MtlsCredentials(MtlsCredentialsConfig::Rest mtls_config_rest)
: mtls_config_rest_(std::move(mtls_config_rest)) {}

/**
* While other Credentials subclasses return a string containing an
* Authorization HTTP header from this method, this class always returns an
* empty string as its value.
*/
StatusOr<AccessToken> GetToken(
std::chrono::system_clock::time_point tp) override;

absl::optional<MtlsCredentialsConfig::Rest> MtlsConfig() const override {
return mtls_config_rest_;
}

private:
MtlsCredentialsConfig::Rest mtls_config_rest_;
};

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_MTLS_CREDENTIALS_H
Loading
Loading