1 //
2 // Copyright 2019 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/support/port_platform.h>
18
19 #include "src/core/ext/xds/xds_bootstrap.h"
20
21 #include <vector>
22
23 #include <errno.h>
24 #include <stdlib.h>
25
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/str_format.h"
28 #include "absl/strings/str_join.h"
29 #include "absl/strings/string_view.h"
30
31 #include "src/core/ext/xds/certificate_provider_registry.h"
32 #include "src/core/ext/xds/xds_api.h"
33 #include "src/core/lib/gpr/env.h"
34 #include "src/core/lib/gpr/string.h"
35 #include "src/core/lib/iomgr/load_file.h"
36 #include "src/core/lib/security/credentials/credentials.h"
37 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
38 #include "src/core/lib/slice/slice_internal.h"
39
40 namespace grpc_core {
41
42 //
43 // XdsChannelCredsRegistry
44 //
45
IsSupported(const std::string & creds_type)46 bool XdsChannelCredsRegistry::IsSupported(const std::string& creds_type) {
47 return creds_type == "google_default" || creds_type == "insecure" ||
48 creds_type == "fake";
49 }
50
IsValidConfig(const std::string & creds_type,const Json & config)51 bool XdsChannelCredsRegistry::IsValidConfig(const std::string& creds_type,
52 const Json& config) {
53 // Currently, none of the creds types actually take a config, but we
54 // ignore whatever might be specified in the bootstrap file for
55 // forward compatibility reasons.
56 return true;
57 }
58
59 RefCountedPtr<grpc_channel_credentials>
MakeChannelCreds(const std::string & creds_type,const Json & config)60 XdsChannelCredsRegistry::MakeChannelCreds(const std::string& creds_type,
61 const Json& config) {
62 if (creds_type == "google_default") {
63 return grpc_google_default_credentials_create(nullptr);
64 } else if (creds_type == "insecure") {
65 return grpc_insecure_credentials_create();
66 } else if (creds_type == "fake") {
67 return grpc_fake_transport_security_credentials_create();
68 }
69 return nullptr;
70 }
71
72 //
73 // XdsBootstrap::XdsServer
74 //
75
ShouldUseV3() const76 bool XdsBootstrap::XdsServer::ShouldUseV3() const {
77 return server_features.find("xds_v3") != server_features.end();
78 }
79
80 //
81 // XdsBootstrap
82 //
83
84 namespace {
85
BootstrapString(const XdsBootstrap & bootstrap)86 std::string BootstrapString(const XdsBootstrap& bootstrap) {
87 std::vector<std::string> parts;
88 if (bootstrap.node() != nullptr) {
89 parts.push_back(absl::StrFormat(
90 "node={\n"
91 " id=\"%s\",\n"
92 " cluster=\"%s\",\n"
93 " locality={\n"
94 " region=\"%s\",\n"
95 " zone=\"%s\",\n"
96 " subzone=\"%s\"\n"
97 " },\n"
98 " metadata=%s,\n"
99 "},\n",
100 bootstrap.node()->id, bootstrap.node()->cluster,
101 bootstrap.node()->locality_region, bootstrap.node()->locality_zone,
102 bootstrap.node()->locality_subzone, bootstrap.node()->metadata.Dump()));
103 }
104 parts.push_back(absl::StrFormat(
105 "servers=[\n"
106 " {\n"
107 " uri=\"%s\",\n"
108 " creds_type=%s,\n",
109 bootstrap.server().server_uri, bootstrap.server().channel_creds_type));
110 if (bootstrap.server().channel_creds_config.type() != Json::Type::JSON_NULL) {
111 parts.push_back(
112 absl::StrFormat(" creds_config=%s,",
113 bootstrap.server().channel_creds_config.Dump()));
114 }
115 if (!bootstrap.server().server_features.empty()) {
116 parts.push_back(absl::StrCat(
117 " server_features=[",
118 absl::StrJoin(bootstrap.server().server_features, ", "), "],\n"));
119 }
120 parts.push_back(" }\n],\n");
121 parts.push_back("certificate_providers={\n");
122 for (const auto& entry : bootstrap.certificate_providers()) {
123 parts.push_back(
124 absl::StrFormat(" %s={\n"
125 " plugin_name=%s\n"
126 " config=%s\n"
127 " },\n",
128 entry.first, entry.second.plugin_name,
129 entry.second.config->ToString()));
130 }
131 parts.push_back("}");
132 return absl::StrJoin(parts, "");
133 }
134
135 } // namespace
136
ReadFromFile(XdsClient * client,TraceFlag * tracer,grpc_error ** error)137 std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(XdsClient* client,
138 TraceFlag* tracer,
139 grpc_error** error) {
140 grpc_core::UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
141 if (path == nullptr) {
142 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
143 "Environment variable GRPC_XDS_BOOTSTRAP not defined");
144 return nullptr;
145 }
146 if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
147 gpr_log(GPR_INFO,
148 "[xds_client %p] Got bootstrap file location from "
149 "GRPC_XDS_BOOTSTRAP environment variable: %s",
150 client, path.get());
151 }
152 grpc_slice contents;
153 *error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
154 if (*error != GRPC_ERROR_NONE) return nullptr;
155 absl::string_view contents_str_view = StringViewFromSlice(contents);
156 if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
157 gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client,
158 std::string(contents_str_view).c_str());
159 }
160 Json json = Json::Parse(contents_str_view, error);
161 grpc_slice_unref_internal(contents);
162 if (*error != GRPC_ERROR_NONE) {
163 grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(
164 absl::StrCat("Failed to parse bootstrap file ", path.get()).c_str(),
165 error, 1);
166 GRPC_ERROR_UNREF(*error);
167 *error = error_out;
168 return nullptr;
169 }
170 std::unique_ptr<XdsBootstrap> result =
171 absl::make_unique<XdsBootstrap>(std::move(json), error);
172 if (*error == GRPC_ERROR_NONE && GRPC_TRACE_FLAG_ENABLED(*tracer)) {
173 gpr_log(GPR_INFO,
174 "[xds_client %p] Bootstrap config for creating xds client:\n%s",
175 client, BootstrapString(*result).c_str());
176 }
177 return result;
178 }
179
XdsBootstrap(Json json,grpc_error ** error)180 XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) {
181 if (json.type() != Json::Type::OBJECT) {
182 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
183 "malformed JSON in bootstrap file");
184 return;
185 }
186 std::vector<grpc_error*> error_list;
187 auto it = json.mutable_object()->find("xds_servers");
188 if (it == json.mutable_object()->end()) {
189 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
190 "\"xds_servers\" field not present"));
191 } else if (it->second.type() != Json::Type::ARRAY) {
192 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
193 "\"xds_servers\" field is not an array"));
194 } else {
195 grpc_error* parse_error = ParseXdsServerList(&it->second);
196 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
197 }
198 it = json.mutable_object()->find("node");
199 if (it != json.mutable_object()->end()) {
200 if (it->second.type() != Json::Type::OBJECT) {
201 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
202 "\"node\" field is not an object"));
203 } else {
204 grpc_error* parse_error = ParseNode(&it->second);
205 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
206 }
207 }
208 if (XdsSecurityEnabled()) {
209 it = json.mutable_object()->find("certificate_providers");
210 if (it != json.mutable_object()->end()) {
211 if (it->second.type() != Json::Type::OBJECT) {
212 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
213 "\"certificate_providers\" field is not an object"));
214 } else {
215 grpc_error* parse_error = ParseCertificateProviders(&it->second);
216 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
217 }
218 }
219 }
220 *error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
221 &error_list);
222 }
223
ParseXdsServerList(Json * json)224 grpc_error* XdsBootstrap::ParseXdsServerList(Json* json) {
225 std::vector<grpc_error*> error_list;
226 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
227 Json& child = json->mutable_array()->at(i);
228 if (child.type() != Json::Type::OBJECT) {
229 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
230 absl::StrCat("array element ", i, " is not an object").c_str()));
231 } else {
232 grpc_error* parse_error = ParseXdsServer(&child, i);
233 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
234 }
235 }
236 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array",
237 &error_list);
238 }
239
ParseXdsServer(Json * json,size_t idx)240 grpc_error* XdsBootstrap::ParseXdsServer(Json* json, size_t idx) {
241 std::vector<grpc_error*> error_list;
242 servers_.emplace_back();
243 XdsServer& server = servers_[servers_.size() - 1];
244 auto it = json->mutable_object()->find("server_uri");
245 if (it == json->mutable_object()->end()) {
246 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
247 "\"server_uri\" field not present"));
248 } else if (it->second.type() != Json::Type::STRING) {
249 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
250 "\"server_uri\" field is not a string"));
251 } else {
252 server.server_uri = std::move(*it->second.mutable_string_value());
253 }
254 it = json->mutable_object()->find("channel_creds");
255 if (it == json->mutable_object()->end()) {
256 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
257 "\"channel_creds\" field not present"));
258 } else if (it->second.type() != Json::Type::ARRAY) {
259 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
260 "\"channel_creds\" field is not an array"));
261 } else {
262 grpc_error* parse_error = ParseChannelCredsArray(&it->second, &server);
263 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
264 }
265 it = json->mutable_object()->find("server_features");
266 if (it != json->mutable_object()->end()) {
267 if (it->second.type() != Json::Type::ARRAY) {
268 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
269 "\"server_features\" field is not an array"));
270 } else {
271 grpc_error* parse_error = ParseServerFeaturesArray(&it->second, &server);
272 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
273 }
274 }
275 // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
276 // string is not static in this case.
277 if (error_list.empty()) return GRPC_ERROR_NONE;
278 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
279 absl::StrCat("errors parsing index ", idx).c_str());
280 for (size_t i = 0; i < error_list.size(); ++i) {
281 error = grpc_error_add_child(error, error_list[i]);
282 }
283 return error;
284 }
285
ParseChannelCredsArray(Json * json,XdsServer * server)286 grpc_error* XdsBootstrap::ParseChannelCredsArray(Json* json,
287 XdsServer* server) {
288 std::vector<grpc_error*> error_list;
289 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
290 Json& child = json->mutable_array()->at(i);
291 if (child.type() != Json::Type::OBJECT) {
292 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
293 absl::StrCat("array element ", i, " is not an object").c_str()));
294 } else {
295 grpc_error* parse_error = ParseChannelCreds(&child, i, server);
296 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
297 }
298 }
299 if (server->channel_creds_type.empty()) {
300 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
301 "no known creds type found in \"channel_creds\""));
302 }
303 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
304 &error_list);
305 }
306
ParseChannelCreds(Json * json,size_t idx,XdsServer * server)307 grpc_error* XdsBootstrap::ParseChannelCreds(Json* json, size_t idx,
308 XdsServer* server) {
309 std::vector<grpc_error*> error_list;
310 std::string type;
311 auto it = json->mutable_object()->find("type");
312 if (it == json->mutable_object()->end()) {
313 error_list.push_back(
314 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field not present"));
315 } else if (it->second.type() != Json::Type::STRING) {
316 error_list.push_back(
317 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field is not a string"));
318 } else {
319 type = std::move(*it->second.mutable_string_value());
320 }
321 Json config;
322 it = json->mutable_object()->find("config");
323 if (it != json->mutable_object()->end()) {
324 if (it->second.type() != Json::Type::OBJECT) {
325 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
326 "\"config\" field is not an object"));
327 } else {
328 config = std::move(it->second);
329 }
330 }
331 // Select the first channel creds type that we support.
332 if (server->channel_creds_type.empty() &&
333 XdsChannelCredsRegistry::IsSupported(type)) {
334 if (!XdsChannelCredsRegistry::IsValidConfig(type, config)) {
335 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
336 absl::StrCat("invalid config for channel creds type \"", type, "\"")
337 .c_str()));
338 }
339 server->channel_creds_type = std::move(type);
340 server->channel_creds_config = std::move(config);
341 }
342 // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
343 // string is not static in this case.
344 if (error_list.empty()) return GRPC_ERROR_NONE;
345 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
346 absl::StrCat("errors parsing index ", idx).c_str());
347 for (size_t i = 0; i < error_list.size(); ++i) {
348 error = grpc_error_add_child(error, error_list[i]);
349 }
350 return error;
351 }
352
ParseServerFeaturesArray(Json * json,XdsServer * server)353 grpc_error* XdsBootstrap::ParseServerFeaturesArray(Json* json,
354 XdsServer* server) {
355 std::vector<grpc_error*> error_list;
356 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
357 Json& child = json->mutable_array()->at(i);
358 if (child.type() == Json::Type::STRING &&
359 child.string_value() == "xds_v3") {
360 // TODO(roth): Remove env var check once we do interop testing and
361 // are sure that the v3 code actually works.
362 grpc_core::UniquePtr<char> enable_str(
363 gpr_getenv("GRPC_XDS_EXPERIMENTAL_V3_SUPPORT"));
364 bool enabled = false;
365 if (gpr_parse_bool_value(enable_str.get(), &enabled) && enabled) {
366 server->server_features.insert(
367 std::move(*child.mutable_string_value()));
368 }
369 }
370 }
371 return GRPC_ERROR_CREATE_FROM_VECTOR(
372 "errors parsing \"server_features\" array", &error_list);
373 }
374
ParseNode(Json * json)375 grpc_error* XdsBootstrap::ParseNode(Json* json) {
376 std::vector<grpc_error*> error_list;
377 node_ = absl::make_unique<Node>();
378 auto it = json->mutable_object()->find("id");
379 if (it != json->mutable_object()->end()) {
380 if (it->second.type() != Json::Type::STRING) {
381 error_list.push_back(
382 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string"));
383 } else {
384 node_->id = std::move(*it->second.mutable_string_value());
385 }
386 }
387 it = json->mutable_object()->find("cluster");
388 if (it != json->mutable_object()->end()) {
389 if (it->second.type() != Json::Type::STRING) {
390 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
391 "\"cluster\" field is not a string"));
392 } else {
393 node_->cluster = std::move(*it->second.mutable_string_value());
394 }
395 }
396 it = json->mutable_object()->find("locality");
397 if (it != json->mutable_object()->end()) {
398 if (it->second.type() != Json::Type::OBJECT) {
399 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
400 "\"locality\" field is not an object"));
401 } else {
402 grpc_error* parse_error = ParseLocality(&it->second);
403 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
404 }
405 }
406 it = json->mutable_object()->find("metadata");
407 if (it != json->mutable_object()->end()) {
408 if (it->second.type() != Json::Type::OBJECT) {
409 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
410 "\"metadata\" field is not an object"));
411 } else {
412 node_->metadata = std::move(it->second);
413 }
414 }
415 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
416 &error_list);
417 }
418
ParseLocality(Json * json)419 grpc_error* XdsBootstrap::ParseLocality(Json* json) {
420 std::vector<grpc_error*> error_list;
421 auto it = json->mutable_object()->find("region");
422 if (it != json->mutable_object()->end()) {
423 if (it->second.type() != Json::Type::STRING) {
424 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
425 "\"region\" field is not a string"));
426 } else {
427 node_->locality_region = std::move(*it->second.mutable_string_value());
428 }
429 }
430 it = json->mutable_object()->find("zone");
431 if (it != json->mutable_object()->end()) {
432 if (it->second.type() != Json::Type::STRING) {
433 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
434 "\"zone\" field is not a string"));
435 } else {
436 node_->locality_zone = std::move(*it->second.mutable_string_value());
437 }
438 }
439 it = json->mutable_object()->find("subzone");
440 if (it != json->mutable_object()->end()) {
441 if (it->second.type() != Json::Type::STRING) {
442 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
443 "\"subzone\" field is not a string"));
444 } else {
445 node_->locality_subzone = std::move(*it->second.mutable_string_value());
446 }
447 }
448 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
449 &error_list);
450 }
451
ParseCertificateProviders(Json * json)452 grpc_error* XdsBootstrap::ParseCertificateProviders(Json* json) {
453 std::vector<grpc_error*> error_list;
454 for (auto& certificate_provider : *(json->mutable_object())) {
455 if (certificate_provider.second.type() != Json::Type::OBJECT) {
456 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
457 absl::StrCat("element \"", certificate_provider.first,
458 "\" is not an object")
459 .c_str()));
460 } else {
461 grpc_error* parse_error = ParseCertificateProvider(
462 certificate_provider.first, &certificate_provider.second);
463 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
464 }
465 }
466 return GRPC_ERROR_CREATE_FROM_VECTOR(
467 "errors parsing \"certificate_providers\" object", &error_list);
468 }
469
ParseCertificateProvider(const std::string & instance_name,Json * certificate_provider_json)470 grpc_error* XdsBootstrap::ParseCertificateProvider(
471 const std::string& instance_name, Json* certificate_provider_json) {
472 std::vector<grpc_error*> error_list;
473 auto it = certificate_provider_json->mutable_object()->find("plugin_name");
474 if (it == certificate_provider_json->mutable_object()->end()) {
475 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
476 "\"plugin_name\" field not present"));
477 } else if (it->second.type() != Json::Type::STRING) {
478 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
479 "\"plugin_name\" field is not a string"));
480 } else {
481 std::string plugin_name = std::move(*(it->second.mutable_string_value()));
482 CertificateProviderFactory* factory =
483 CertificateProviderRegistry::LookupCertificateProviderFactory(
484 plugin_name);
485 if (factory != nullptr) {
486 RefCountedPtr<CertificateProviderFactory::Config> config;
487 it = certificate_provider_json->mutable_object()->find("config");
488 if (it != certificate_provider_json->mutable_object()->end()) {
489 if (it->second.type() != Json::Type::OBJECT) {
490 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
491 "\"config\" field is not an object"));
492 } else {
493 grpc_error* parse_error = GRPC_ERROR_NONE;
494 config = factory->CreateCertificateProviderConfig(it->second,
495 &parse_error);
496 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
497 }
498 } else {
499 // "config" is an optional field, so create an empty JSON object.
500 grpc_error* parse_error = GRPC_ERROR_NONE;
501 config = factory->CreateCertificateProviderConfig(Json::Object(),
502 &parse_error);
503 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
504 }
505 certificate_providers_.insert(
506 {instance_name, {std::move(plugin_name), std::move(config)}});
507 }
508 }
509 // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
510 // string is not static in this case.
511 if (error_list.empty()) return GRPC_ERROR_NONE;
512 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
513 absl::StrCat("errors parsing element \"", instance_name, "\"").c_str());
514 for (size_t i = 0; i < error_list.size(); ++i) {
515 error = grpc_error_add_child(error, error_list[i]);
516 }
517 return error;
518 }
519
520 } // namespace grpc_core
521