File: compute_engine_service_client.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (269 lines) | stat: -rw-r--r-- 10,733 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/base/compute_engine_service_client.h"

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>

#include "base/environment.h"
#include "base/strings/stringprintf.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "remoting/base/http_status.h"
#include "remoting/base/logging.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"

namespace remoting {

namespace {

// Compute Engine VM Instances always have an HTTP metadata server endpoint.
// Shielded Instances also provide an HTTPS endpoint. For compatibility, we
// use the HTTP endpoint for fail-fast decisions or to provide an identity token
// but rely on our backend services to validate the token and metadata. More
// info at: https://cloud.google.com/compute/docs/metadata/querying-metadata.
constexpr char kDefaultMetadataServerHost[] = "metadata.google.internal";
// TODO: joedow - Add support for the HTTPS endpoint:
// https://cloud.google.com/compute/docs/metadata/querying-metadata#query-https-mds
constexpr char kHttpMetadataServerBaseUrlFormat[] =
    "http://%s/computeMetadata/v1/instance/service-accounts/default";
// Cloud users can configure a custom metadata server for the VM and direct CLI
// tools to call it instead by providing a hostname, hostname:port, or IP
// address in this environment variable.
// https://cloud.google.com/nodejs/docs/reference/gcp-metadata/latest#environment-variables
constexpr char kGceMetadataHostVarName[] = "GCE_METADATA_HOST";

constexpr size_t kMaxResponseSize = 4096;

constexpr net::NetworkTrafficAnnotationTag kInstanceIdentityTrafficAnnotation =
    net::DefineNetworkTrafficAnnotation(
        "remoting_compute_engine_instance_identity_token",
        R"(
        semantics {
          sender: "Chrome Remote Desktop"
          description:
            "Retrieves a Compute Engine VM Instance Identity token for use by "
            "Chrome Remote Desktop."
          trigger:
            "The request is made when a Compute Engine VM Instance is "
            "configured for remote acces via Chrome Remote Desktop. It is also "
            "called when the host instance makes a backend service request as "
            "the identity token is used to verify the origin of the request."
          data: "Arbitrary payload data used to prevent replay attacks."
          destination: GOOGLE_OWNED_SERVICE
          internal {
            contacts {
              email: "chromoting-team@google.com"
            }
          }
          user_data {
            type: ARBITRARY_DATA
          }
          last_reviewed: "2025-02-08"
        }
        policy {
          cookies_allowed: NO
          setting:
            "This request will not be sent if Chrome Remote Desktop is not "
            "used within a Compute Engine VM Instance."
          policy_exception_justification:
            "Not implemented."
        })");

constexpr net::NetworkTrafficAnnotationTag kAccessTokenTrafficAnnotation =
    net::DefineNetworkTrafficAnnotation(
        "remoting_compute_engine_instance_access_token",
        R"(
        semantics {
          sender: "Chrome Remote Desktop"
          description:
            "Retrieves an OAuth access token for the default service account "
            "associated with the Compute Engine VM Instance."
          trigger:
            "The request is made when Chrome Remote Desktop is being run in a "
            "Compute Engine Instance and needs to send a request to our API "
            "using the default service account for the Compute Engine Instance."
          data: "None"
          destination: GOOGLE_OWNED_SERVICE
          internal {
            contacts {
              email: "chromoting-team@google.com"
            }
          }
          user_data {
            type: NONE
          }
          last_reviewed: "2025-02-08"
        }
        policy {
          cookies_allowed: NO
          setting:
            "This request will not be sent if Chrome Remote Desktop is not "
            "used within a Compute Engine VM Instance."
          policy_exception_justification:
            "Not implemented."
        })");

constexpr net::NetworkTrafficAnnotationTag kAccessTokenScopesTrafficAnnotation =
    net::DefineNetworkTrafficAnnotation(
        "remoting_compute_engine_instance_access_token_scopes",
        R"(
        semantics {
          sender: "Chrome Remote Desktop"
          description:
            "Retrieves the set of OAuth scopes included in access tokens which "
            "are generated for the default service account associated with the "
            "Compute Engine VM Instance."
          trigger:
            "The request is made when Chrome Remote Desktop is being run in a "
            "Compute Engine Instance and needs to determine if the default "
            "service account has been configured properly to access our API."
          data: "None"
          destination: GOOGLE_OWNED_SERVICE
          internal {
            contacts {
              email: "chromoting-team@google.com"
            }
          }
          user_data {
            type: NONE
          }
          last_reviewed: "2025-02-08"
        }
        policy {
          cookies_allowed: NO
          setting:
            "This request will not be sent if Chrome Remote Desktop is not "
            "used within a Compute Engine VM Instance."
          policy_exception_justification:
            "Not implemented."
        })");

}  // namespace

ComputeEngineServiceClient::ComputeEngineServiceClient(
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
    : url_loader_factory_(url_loader_factory) {
  auto env = base::Environment::Create();
  auto metadata_host_override = env->GetVar(kGceMetadataHostVarName);
  metadata_server_base_url_ = base::StringPrintf(
      kHttpMetadataServerBaseUrlFormat,
      metadata_host_override.has_value() && !metadata_host_override->empty()
          ? *metadata_host_override
          : kDefaultMetadataServerHost);
  HOST_LOG << "Using Metadata server URL: " << metadata_server_base_url_;
}

ComputeEngineServiceClient::~ComputeEngineServiceClient() = default;

void ComputeEngineServiceClient::GetInstanceIdentityToken(
    std::string_view audience,
    ResponseCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  // Use 'format=full' to include project and instance details in the token.
  ExecuteRequest(base::StringPrintf("%s/identity?audience=%s&format=full",
                                    metadata_server_base_url_, audience),
                 kInstanceIdentityTrafficAnnotation, std::move(callback));
}

void ComputeEngineServiceClient::GetServiceAccountAccessToken(
    ResponseCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  ExecuteRequest(base::StringPrintf("%s/token", metadata_server_base_url_),
                 kAccessTokenTrafficAnnotation, std::move(callback));
}

void ComputeEngineServiceClient::GetServiceAccountScopes(
    ResponseCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  ExecuteRequest(base::StringPrintf("%s/scopes", metadata_server_base_url_),
                 kAccessTokenScopesTrafficAnnotation, std::move(callback));
}

void ComputeEngineServiceClient::CancelPendingRequests() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  weak_ptr_factory_.InvalidateWeakPtrs();
  url_loader_.reset();
}

void ComputeEngineServiceClient::ExecuteRequest(
    std::string_view url,
    const net::NetworkTrafficAnnotationTag& network_annotation,
    ResponseCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  // TODO: joedow - Update to handle concurrent requests when needed.
  CHECK(!url_loader_);

  auto resource_request = std::make_unique<network::ResourceRequest>();
  resource_request->url = GURL(url);
  resource_request->load_flags =
      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  resource_request->method = net::HttpRequestHeaders::kGetMethod;
  // All Compute Engine Metadata requests must set this header.
  resource_request->headers.SetHeader("Metadata-Flavor", "Google");

  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                 network_annotation);
  url_loader_->SetTimeoutDuration(base::Seconds(60));
  url_loader_->SetAllowHttpErrorResults(false);
  url_loader_->SetRetryOptions(
      3, network::SimpleURLLoader::RETRY_ON_5XX |
             network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);

  url_loader_->DownloadToString(
      url_loader_factory_.get(),
      base::BindOnce(&ComputeEngineServiceClient::OnRequestComplete,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
      kMaxResponseSize);
}

void ComputeEngineServiceClient::OnRequestComplete(
    ResponseCallback callback,
    std::optional<std::string> response_body) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  net::Error net_error = static_cast<net::Error>(url_loader_->NetError());
  HttpStatus http_status = HttpStatus::OK();
  if (net_error != net::Error::OK &&
      net_error != net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE) {
    http_status = HttpStatus(net_error);
  } else if (!url_loader_->ResponseInfo() ||
             !url_loader_->ResponseInfo()->headers ||
             url_loader_->ResponseInfo()->headers->response_code() <= 0) {
    http_status =
        HttpStatus(HttpStatus::Code::INTERNAL,
                   "Failed to get HTTP status from the response header.");
  } else {
    http_status =
        HttpStatus(static_cast<net::HttpStatusCode>(
                       url_loader_->ResponseInfo()->headers->response_code()),
                   response_body.value_or(std::string()));
  }

  if (!http_status.ok()) {
    LOG(ERROR) << "Compute Engine API request failed. Code: "
               << static_cast<int32_t>(http_status.error_code())
               << ", Message: " << http_status.error_message();
  }

  // Reset |url_loader_| since we've extracted the info we need from it.
  // This will allow the caller to reuse this instance to make another request.
  url_loader_.reset();

  std::move(callback).Run(http_status);
}

}  // namespace remoting