• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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