Skip to content

Commit 4ac3fe1

Browse files
authored
feat(credentials): add support for creating ComputeEngine credentials explicitly (#15789)
1 parent 81b496f commit 4ac3fe1

15 files changed

+246
-0
lines changed

google/cloud/credentials.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ std::shared_ptr<Credentials> MakeApiKeyCredentials(std::string api_key,
6363
std::move(opts));
6464
}
6565

66+
std::shared_ptr<Credentials> MakeComputeEngineCredentials(Options opts) {
67+
return std::make_shared<internal::ComputeEngineCredentialsConfig>(
68+
std::move(opts));
69+
}
70+
6671
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
6772
} // namespace cloud
6873
} // namespace google

google/cloud/credentials.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,31 @@ std::shared_ptr<Credentials> MakeExternalAccountCredentials(
370370
std::shared_ptr<Credentials> MakeApiKeyCredentials(std::string api_key,
371371
Options opts = {});
372372

373+
/**
374+
* Creates credentials for the VM's specified service account.
375+
*
376+
* When your application is deployed to a GCP environment such as GCE, GKE, or
377+
* Cloud Run, each of these deployment environments provides a default service
378+
* account to the application [aip/4115]. These environments offer mechanisms to
379+
* change the default service account, and thus the credentials based on that
380+
* service account, without any code changes to your application.
381+
*
382+
* @see https://cloud.google.com/docs/authentication for more information on
383+
* authentication in GCP.
384+
*
385+
* @see https://cloud.google.com/docs/authentication/client-libraries for more
386+
* information on authentication for client libraries.
387+
*
388+
* [aip/4115]: https://google.aip.dev/auth/4115
389+
*
390+
* @ingroup guac
391+
*
392+
* @param opts optional configuration values. Note that the effect of these
393+
* parameters depends on the underlying transport. For example,
394+
* `LoggingComponentsOption` is ignored by gRPC-based services.
395+
*/
396+
std::shared_ptr<Credentials> MakeComputeEngineCredentials(Options opts = {});
397+
373398
/**
374399
* Configure the delegates for `MakeImpersonateServiceAccountCredentials()`
375400
*

google/cloud/google_cloud_cpp_grpc_utils.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ google_cloud_cpp_grpc_utils_hdrs = [
6363
"internal/grpc_api_key_authentication.h",
6464
"internal/grpc_async_access_token_cache.h",
6565
"internal/grpc_channel_credentials_authentication.h",
66+
"internal/grpc_compute_engine_authentication.h",
6667
"internal/grpc_impersonate_service_account.h",
6768
"internal/grpc_metadata_view.h",
6869
"internal/grpc_opentelemetry.h",
@@ -102,6 +103,7 @@ google_cloud_cpp_grpc_utils_srcs = [
102103
"internal/grpc_api_key_authentication.cc",
103104
"internal/grpc_async_access_token_cache.cc",
104105
"internal/grpc_channel_credentials_authentication.cc",
106+
"internal/grpc_compute_engine_authentication.cc",
105107
"internal/grpc_impersonate_service_account.cc",
106108
"internal/grpc_opentelemetry.cc",
107109
"internal/grpc_request_metadata.cc",

google/cloud/google_cloud_cpp_grpc_utils.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ add_library(
7878
internal/grpc_async_access_token_cache.h
7979
internal/grpc_channel_credentials_authentication.cc
8080
internal/grpc_channel_credentials_authentication.h
81+
internal/grpc_compute_engine_authentication.cc
82+
internal/grpc_compute_engine_authentication.h
8183
internal/grpc_impersonate_service_account.cc
8284
internal/grpc_impersonate_service_account.h
8385
internal/grpc_metadata_view.h
@@ -261,6 +263,7 @@ if (BUILD_TESTING)
261263
internal/grpc_api_key_authentication_test.cc
262264
internal/grpc_async_access_token_cache_test.cc
263265
internal/grpc_channel_credentials_authentication_test.cc
266+
internal/grpc_compute_engine_authentication_test.cc
264267
internal/grpc_opentelemetry_test.cc
265268
internal/grpc_request_metadata_test.cc
266269
internal/grpc_service_account_authentication_test.cc

google/cloud/google_cloud_cpp_grpc_utils_unit_tests.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ google_cloud_cpp_grpc_utils_unit_tests = [
4949
"internal/grpc_api_key_authentication_test.cc",
5050
"internal/grpc_async_access_token_cache_test.cc",
5151
"internal/grpc_channel_credentials_authentication_test.cc",
52+
"internal/grpc_compute_engine_authentication_test.cc",
5253
"internal/grpc_opentelemetry_test.cc",
5354
"internal/grpc_request_metadata_test.cc",
5455
"internal/grpc_service_account_authentication_test.cc",

google/cloud/internal/credentials_impl.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ ApiKeyConfig::ApiKeyConfig(std::string api_key, Options opts)
101101
: api_key_(std::move(api_key)),
102102
options_(PopulateAuthOptions(std::move(opts))) {}
103103

104+
ComputeEngineCredentialsConfig::ComputeEngineCredentialsConfig(Options opts)
105+
: options_(PopulateAuthOptions(std::move(opts))) {}
106+
104107
} // namespace internal
105108
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
106109
} // namespace cloud

google/cloud/internal/credentials_impl.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ImpersonateServiceAccountConfig;
4040
class ServiceAccountConfig;
4141
class ExternalAccountConfig;
4242
class ApiKeyConfig;
43+
class ComputeEngineCredentialsConfig;
4344

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

@@ -54,6 +55,7 @@ class CredentialsVisitor {
5455
virtual void visit(ServiceAccountConfig const&) = 0;
5556
virtual void visit(ExternalAccountConfig const&) = 0;
5657
virtual void visit(ApiKeyConfig const&) = 0;
58+
virtual void visit(ComputeEngineCredentialsConfig const&) = 0;
5759

5860
static void dispatch(Credentials const& credentials,
5961
CredentialsVisitor& visitor);
@@ -183,6 +185,19 @@ class ApiKeyConfig : public Credentials {
183185
Options options_;
184186
};
185187

188+
class ComputeEngineCredentialsConfig : public Credentials {
189+
public:
190+
explicit ComputeEngineCredentialsConfig(Options opts);
191+
~ComputeEngineCredentialsConfig() override = default;
192+
193+
Options const& options() const { return options_; }
194+
195+
private:
196+
void dispatch(CredentialsVisitor& v) const override { v.visit(*this); }
197+
198+
Options options_;
199+
};
200+
186201
/// A helper function to initialize Auth options.
187202
Options PopulateAuthOptions(Options options);
188203

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/internal/grpc_compute_engine_authentication.h"
16+
17+
namespace google {
18+
namespace cloud {
19+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
20+
namespace internal {
21+
22+
GrpcComputeEngineAuthentication::GrpcComputeEngineAuthentication(
23+
Options const& opts)
24+
: credentials_(grpc::GoogleComputeEngineCredentials()) {
25+
auto cainfo = LoadCAInfo(opts);
26+
if (cainfo) ssl_options_.pem_root_certs = std::move(*cainfo);
27+
}
28+
29+
std::shared_ptr<grpc::Channel> GrpcComputeEngineAuthentication::CreateChannel(
30+
std::string const& endpoint, grpc::ChannelArguments const& arguments) {
31+
return grpc::CreateCustomChannel(endpoint, grpc::SslCredentials(ssl_options_),
32+
arguments);
33+
}
34+
35+
bool GrpcComputeEngineAuthentication::RequiresConfigureContext() const {
36+
return true;
37+
}
38+
39+
Status GrpcComputeEngineAuthentication::ConfigureContext(
40+
grpc::ClientContext& context) {
41+
context.set_credentials(credentials_);
42+
return Status{};
43+
}
44+
45+
future<StatusOr<std::shared_ptr<grpc::ClientContext>>>
46+
GrpcComputeEngineAuthentication::AsyncConfigureContext(
47+
std::shared_ptr<grpc::ClientContext> context) {
48+
context->set_credentials(credentials_);
49+
return make_ready_future(make_status_or(std::move(context)));
50+
}
51+
52+
} // namespace internal
53+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
54+
} // namespace cloud
55+
} // namespace google
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_GRPC_COMPUTE_ENGINE_AUTHENTICATION_H
16+
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_GRPC_COMPUTE_ENGINE_AUTHENTICATION_H
17+
18+
#include "google/cloud/internal/credentials_impl.h"
19+
#include "google/cloud/internal/unified_grpc_credentials.h"
20+
#include "google/cloud/version.h"
21+
#include <grpcpp/grpcpp.h>
22+
23+
namespace google {
24+
namespace cloud {
25+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
26+
namespace internal {
27+
28+
class GrpcComputeEngineAuthentication : public GrpcAuthenticationStrategy {
29+
public:
30+
explicit GrpcComputeEngineAuthentication(Options const& opts = {});
31+
~GrpcComputeEngineAuthentication() override = default;
32+
33+
std::shared_ptr<grpc::Channel> CreateChannel(
34+
std::string const& endpoint,
35+
grpc::ChannelArguments const& arguments) override;
36+
bool RequiresConfigureContext() const override;
37+
Status ConfigureContext(grpc::ClientContext&) override;
38+
future<StatusOr<std::shared_ptr<grpc::ClientContext>>> AsyncConfigureContext(
39+
std::shared_ptr<grpc::ClientContext> context) override;
40+
41+
private:
42+
std::shared_ptr<grpc::CallCredentials> credentials_;
43+
grpc::SslCredentialsOptions ssl_options_;
44+
};
45+
46+
} // namespace internal
47+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
48+
} // namespace cloud
49+
} // namespace google
50+
51+
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_GRPC_COMPUTE_ENGINE_AUTHENTICATION_H
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/internal/grpc_compute_engine_authentication.h"
16+
#include "google/cloud/testing_util/status_matchers.h"
17+
#include <gmock/gmock.h>
18+
19+
namespace google {
20+
namespace cloud {
21+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
22+
namespace internal {
23+
namespace {
24+
25+
using ::google::cloud::testing_util::IsOk;
26+
27+
TEST(GrpcComputeEngineAuthenticationTest, Simple) {
28+
GrpcComputeEngineAuthentication auth;
29+
30+
auto channel = auth.CreateChannel("localhost:1", grpc::ChannelArguments{});
31+
EXPECT_NE(nullptr, channel.get());
32+
33+
for (auto attempt : {1, 2, 3}) {
34+
SCOPED_TRACE("Running attempt " + std::to_string(attempt));
35+
grpc::ClientContext context;
36+
EXPECT_EQ(nullptr, context.credentials());
37+
auto status = auth.ConfigureContext(context);
38+
EXPECT_THAT(status, IsOk());
39+
EXPECT_NE(nullptr, context.credentials());
40+
}
41+
}
42+
43+
} // namespace
44+
} // namespace internal
45+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
46+
} // namespace cloud
47+
} // namespace google

0 commit comments

Comments
 (0)