Imported Upstream version 1.36.0
[platform/upstream/grpc.git] / src / core / ext / xds / xds_bootstrap.cc
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
46 bool XdsChannelCredsRegistry::IsSupported(const std::string& creds_type) {
47   return creds_type == "google_default" || creds_type == "insecure" ||
48          creds_type == "fake";
49 }
50
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>
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
76 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
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 std::unique_ptr<XdsBootstrap> ParseJsonAndCreate(
136     XdsClient* client, TraceFlag* tracer, absl::string_view json_string,
137     absl::string_view bootstrap_source, grpc_error** error) {
138   Json json = Json::Parse(json_string, error);
139   if (*error != GRPC_ERROR_NONE) {
140     grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(
141         absl::StrCat("Failed to parse bootstrap from ", bootstrap_source)
142             .c_str(),
143         error, 1);
144     GRPC_ERROR_UNREF(*error);
145     *error = error_out;
146     return nullptr;
147   }
148   std::unique_ptr<XdsBootstrap> result =
149       absl::make_unique<XdsBootstrap>(std::move(json), error);
150   if (*error == GRPC_ERROR_NONE && GRPC_TRACE_FLAG_ENABLED(*tracer)) {
151     gpr_log(GPR_INFO,
152             "[xds_client %p] Bootstrap config for creating xds client:\n%s",
153             client, BootstrapString(*result).c_str());
154   }
155   return result;
156 }
157
158 }  // namespace
159
160 std::unique_ptr<XdsBootstrap> XdsBootstrap::Create(XdsClient* client,
161                                                    TraceFlag* tracer,
162                                                    const char* fallback_config,
163                                                    grpc_error** error) {
164   // First, try GRPC_XDS_BOOTSTRAP env var.
165   grpc_core::UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
166   if (path != nullptr) {
167     if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
168       gpr_log(GPR_INFO,
169               "[xds_client %p] Got bootstrap file location from "
170               "GRPC_XDS_BOOTSTRAP environment variable: %s",
171               client, path.get());
172     }
173     grpc_slice contents;
174     *error =
175         grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
176     if (*error != GRPC_ERROR_NONE) return nullptr;
177     absl::string_view contents_str_view = StringViewFromSlice(contents);
178     if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
179       gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client,
180               std::string(contents_str_view).c_str());
181     }
182     std::string bootstrap_source = absl::StrCat("file ", path.get());
183     auto result = ParseJsonAndCreate(client, tracer, contents_str_view,
184                                      bootstrap_source, error);
185     grpc_slice_unref_internal(contents);
186     return result;
187   }
188   // Next, try GRPC_XDS_BOOTSTRAP_CONFIG env var.
189   grpc_core::UniquePtr<char> env_config(
190       gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG"));
191   if (env_config != nullptr) {
192     return ParseJsonAndCreate(client, tracer, env_config.get(),
193                               "GRPC_XDS_BOOTSTRAP_CONFIG env var", error);
194   }
195   // Finally, try fallback config.
196   if (fallback_config != nullptr) {
197     return ParseJsonAndCreate(client, tracer, fallback_config,
198                               "fallback config", error);
199   }
200   // No bootstrap config found.
201   *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
202       "Environment variables GRPC_XDS_BOOTSTRAP or GRPC_XDS_BOOTSTRAP_CONFIG "
203       "not defined");
204   return nullptr;
205 }
206
207 XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) {
208   if (json.type() != Json::Type::OBJECT) {
209     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
210         "malformed JSON in bootstrap file");
211     return;
212   }
213   std::vector<grpc_error*> error_list;
214   auto it = json.mutable_object()->find("xds_servers");
215   if (it == json.mutable_object()->end()) {
216     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
217         "\"xds_servers\" field not present"));
218   } else if (it->second.type() != Json::Type::ARRAY) {
219     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
220         "\"xds_servers\" field is not an array"));
221   } else {
222     grpc_error* parse_error = ParseXdsServerList(&it->second);
223     if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
224   }
225   it = json.mutable_object()->find("node");
226   if (it != json.mutable_object()->end()) {
227     if (it->second.type() != Json::Type::OBJECT) {
228       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
229           "\"node\" field is not an object"));
230     } else {
231       grpc_error* parse_error = ParseNode(&it->second);
232       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
233     }
234   }
235   if (XdsSecurityEnabled()) {
236     it = json.mutable_object()->find("certificate_providers");
237     if (it != json.mutable_object()->end()) {
238       if (it->second.type() != Json::Type::OBJECT) {
239         error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
240             "\"certificate_providers\" field is not an object"));
241       } else {
242         grpc_error* parse_error = ParseCertificateProviders(&it->second);
243         if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
244       }
245     }
246   }
247   *error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
248                                          &error_list);
249 }
250
251 grpc_error* XdsBootstrap::ParseXdsServerList(Json* json) {
252   std::vector<grpc_error*> error_list;
253   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
254     Json& child = json->mutable_array()->at(i);
255     if (child.type() != Json::Type::OBJECT) {
256       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
257           absl::StrCat("array element ", i, " is not an object").c_str()));
258     } else {
259       grpc_error* parse_error = ParseXdsServer(&child, i);
260       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
261     }
262   }
263   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array",
264                                        &error_list);
265 }
266
267 grpc_error* XdsBootstrap::ParseXdsServer(Json* json, size_t idx) {
268   std::vector<grpc_error*> error_list;
269   servers_.emplace_back();
270   XdsServer& server = servers_[servers_.size() - 1];
271   auto it = json->mutable_object()->find("server_uri");
272   if (it == json->mutable_object()->end()) {
273     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
274         "\"server_uri\" field not present"));
275   } else if (it->second.type() != Json::Type::STRING) {
276     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
277         "\"server_uri\" field is not a string"));
278   } else {
279     server.server_uri = std::move(*it->second.mutable_string_value());
280   }
281   it = json->mutable_object()->find("channel_creds");
282   if (it == json->mutable_object()->end()) {
283     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
284         "\"channel_creds\" field not present"));
285   } else if (it->second.type() != Json::Type::ARRAY) {
286     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
287         "\"channel_creds\" field is not an array"));
288   } else {
289     grpc_error* parse_error = ParseChannelCredsArray(&it->second, &server);
290     if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
291   }
292   it = json->mutable_object()->find("server_features");
293   if (it != json->mutable_object()->end()) {
294     if (it->second.type() != Json::Type::ARRAY) {
295       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
296           "\"server_features\" field is not an array"));
297     } else {
298       grpc_error* parse_error = ParseServerFeaturesArray(&it->second, &server);
299       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
300     }
301   }
302   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
303   // string is not static in this case.
304   if (error_list.empty()) return GRPC_ERROR_NONE;
305   grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
306       absl::StrCat("errors parsing index ", idx).c_str());
307   for (size_t i = 0; i < error_list.size(); ++i) {
308     error = grpc_error_add_child(error, error_list[i]);
309   }
310   return error;
311 }
312
313 grpc_error* XdsBootstrap::ParseChannelCredsArray(Json* json,
314                                                  XdsServer* server) {
315   std::vector<grpc_error*> error_list;
316   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
317     Json& child = json->mutable_array()->at(i);
318     if (child.type() != Json::Type::OBJECT) {
319       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
320           absl::StrCat("array element ", i, " is not an object").c_str()));
321     } else {
322       grpc_error* parse_error = ParseChannelCreds(&child, i, server);
323       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
324     }
325   }
326   if (server->channel_creds_type.empty()) {
327     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
328         "no known creds type found in \"channel_creds\""));
329   }
330   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
331                                        &error_list);
332 }
333
334 grpc_error* XdsBootstrap::ParseChannelCreds(Json* json, size_t idx,
335                                             XdsServer* server) {
336   std::vector<grpc_error*> error_list;
337   std::string type;
338   auto it = json->mutable_object()->find("type");
339   if (it == json->mutable_object()->end()) {
340     error_list.push_back(
341         GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field not present"));
342   } else if (it->second.type() != Json::Type::STRING) {
343     error_list.push_back(
344         GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field is not a string"));
345   } else {
346     type = std::move(*it->second.mutable_string_value());
347   }
348   Json config;
349   it = json->mutable_object()->find("config");
350   if (it != json->mutable_object()->end()) {
351     if (it->second.type() != Json::Type::OBJECT) {
352       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
353           "\"config\" field is not an object"));
354     } else {
355       config = std::move(it->second);
356     }
357   }
358   // Select the first channel creds type that we support.
359   if (server->channel_creds_type.empty() &&
360       XdsChannelCredsRegistry::IsSupported(type)) {
361     if (!XdsChannelCredsRegistry::IsValidConfig(type, config)) {
362       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
363           absl::StrCat("invalid config for channel creds type \"", type, "\"")
364               .c_str()));
365     }
366     server->channel_creds_type = std::move(type);
367     server->channel_creds_config = std::move(config);
368   }
369   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
370   // string is not static in this case.
371   if (error_list.empty()) return GRPC_ERROR_NONE;
372   grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
373       absl::StrCat("errors parsing index ", idx).c_str());
374   for (size_t i = 0; i < error_list.size(); ++i) {
375     error = grpc_error_add_child(error, error_list[i]);
376   }
377   return error;
378 }
379
380 grpc_error* XdsBootstrap::ParseServerFeaturesArray(Json* json,
381                                                    XdsServer* server) {
382   std::vector<grpc_error*> error_list;
383   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
384     Json& child = json->mutable_array()->at(i);
385     if (child.type() == Json::Type::STRING &&
386         child.string_value() == "xds_v3") {
387       server->server_features.insert(std::move(*child.mutable_string_value()));
388     }
389   }
390   return GRPC_ERROR_CREATE_FROM_VECTOR(
391       "errors parsing \"server_features\" array", &error_list);
392 }
393
394 grpc_error* XdsBootstrap::ParseNode(Json* json) {
395   std::vector<grpc_error*> error_list;
396   node_ = absl::make_unique<Node>();
397   auto it = json->mutable_object()->find("id");
398   if (it != json->mutable_object()->end()) {
399     if (it->second.type() != Json::Type::STRING) {
400       error_list.push_back(
401           GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string"));
402     } else {
403       node_->id = std::move(*it->second.mutable_string_value());
404     }
405   }
406   it = json->mutable_object()->find("cluster");
407   if (it != json->mutable_object()->end()) {
408     if (it->second.type() != Json::Type::STRING) {
409       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
410           "\"cluster\" field is not a string"));
411     } else {
412       node_->cluster = std::move(*it->second.mutable_string_value());
413     }
414   }
415   it = json->mutable_object()->find("locality");
416   if (it != json->mutable_object()->end()) {
417     if (it->second.type() != Json::Type::OBJECT) {
418       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
419           "\"locality\" field is not an object"));
420     } else {
421       grpc_error* parse_error = ParseLocality(&it->second);
422       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
423     }
424   }
425   it = json->mutable_object()->find("metadata");
426   if (it != json->mutable_object()->end()) {
427     if (it->second.type() != Json::Type::OBJECT) {
428       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
429           "\"metadata\" field is not an object"));
430     } else {
431       node_->metadata = std::move(it->second);
432     }
433   }
434   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
435                                        &error_list);
436 }
437
438 grpc_error* XdsBootstrap::ParseLocality(Json* json) {
439   std::vector<grpc_error*> error_list;
440   auto it = json->mutable_object()->find("region");
441   if (it != json->mutable_object()->end()) {
442     if (it->second.type() != Json::Type::STRING) {
443       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
444           "\"region\" field is not a string"));
445     } else {
446       node_->locality_region = std::move(*it->second.mutable_string_value());
447     }
448   }
449   it = json->mutable_object()->find("zone");
450   if (it != json->mutable_object()->end()) {
451     if (it->second.type() != Json::Type::STRING) {
452       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
453           "\"zone\" field is not a string"));
454     } else {
455       node_->locality_zone = std::move(*it->second.mutable_string_value());
456     }
457   }
458   it = json->mutable_object()->find("subzone");
459   if (it != json->mutable_object()->end()) {
460     if (it->second.type() != Json::Type::STRING) {
461       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
462           "\"subzone\" field is not a string"));
463     } else {
464       node_->locality_subzone = std::move(*it->second.mutable_string_value());
465     }
466   }
467   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
468                                        &error_list);
469 }
470
471 grpc_error* XdsBootstrap::ParseCertificateProviders(Json* json) {
472   std::vector<grpc_error*> error_list;
473   for (auto& certificate_provider : *(json->mutable_object())) {
474     if (certificate_provider.second.type() != Json::Type::OBJECT) {
475       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
476           absl::StrCat("element \"", certificate_provider.first,
477                        "\" is not an object")
478               .c_str()));
479     } else {
480       grpc_error* parse_error = ParseCertificateProvider(
481           certificate_provider.first, &certificate_provider.second);
482       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
483     }
484   }
485   return GRPC_ERROR_CREATE_FROM_VECTOR(
486       "errors parsing \"certificate_providers\" object", &error_list);
487 }
488
489 grpc_error* XdsBootstrap::ParseCertificateProvider(
490     const std::string& instance_name, Json* certificate_provider_json) {
491   std::vector<grpc_error*> error_list;
492   auto it = certificate_provider_json->mutable_object()->find("plugin_name");
493   if (it == certificate_provider_json->mutable_object()->end()) {
494     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
495         "\"plugin_name\" field not present"));
496   } else if (it->second.type() != Json::Type::STRING) {
497     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
498         "\"plugin_name\" field is not a string"));
499   } else {
500     std::string plugin_name = std::move(*(it->second.mutable_string_value()));
501     CertificateProviderFactory* factory =
502         CertificateProviderRegistry::LookupCertificateProviderFactory(
503             plugin_name);
504     if (factory != nullptr) {
505       RefCountedPtr<CertificateProviderFactory::Config> config;
506       it = certificate_provider_json->mutable_object()->find("config");
507       if (it != certificate_provider_json->mutable_object()->end()) {
508         if (it->second.type() != Json::Type::OBJECT) {
509           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
510               "\"config\" field is not an object"));
511         } else {
512           grpc_error* parse_error = GRPC_ERROR_NONE;
513           config = factory->CreateCertificateProviderConfig(it->second,
514                                                             &parse_error);
515           if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
516         }
517       } else {
518         // "config" is an optional field, so create an empty JSON object.
519         grpc_error* parse_error = GRPC_ERROR_NONE;
520         config = factory->CreateCertificateProviderConfig(Json::Object(),
521                                                           &parse_error);
522         if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
523       }
524       certificate_providers_.insert(
525           {instance_name, {std::move(plugin_name), std::move(config)}});
526     }
527   }
528   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
529   // string is not static in this case.
530   if (error_list.empty()) return GRPC_ERROR_NONE;
531   grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
532       absl::StrCat("errors parsing element \"", instance_name, "\"").c_str());
533   for (size_t i = 0; i < error_list.size(); ++i) {
534     error = grpc_error_add_child(error, error_list[i]);
535   }
536   return error;
537 }
538
539 }  // namespace grpc_core