• 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/string.h"
34 #include "src/core/lib/iomgr/load_file.h"
35 #include "src/core/lib/security/credentials/credentials.h"
36 #include "src/core/lib/security/credentials/fake/fake_credentials.h"
37 #include "src/core/lib/slice/slice_internal.h"
38 
39 namespace grpc_core {
40 
41 //
42 // XdsChannelCredsRegistry
43 //
44 
IsSupported(const std::string & creds_type)45 bool XdsChannelCredsRegistry::IsSupported(const std::string& creds_type) {
46   return creds_type == "google_default" || creds_type == "insecure" ||
47          creds_type == "fake";
48 }
49 
IsValidConfig(const std::string &,const Json &)50 bool XdsChannelCredsRegistry::IsValidConfig(const std::string& /*creds_type*/,
51                                             const Json& /*config*/) {
52   // Currently, none of the creds types actually take a config, but we
53   // ignore whatever might be specified in the bootstrap file for
54   // forward compatibility reasons.
55   return true;
56 }
57 
58 RefCountedPtr<grpc_channel_credentials>
MakeChannelCreds(const std::string & creds_type,const Json &)59 XdsChannelCredsRegistry::MakeChannelCreds(const std::string& creds_type,
60                                           const Json& /*config*/) {
61   if (creds_type == "google_default") {
62     return grpc_google_default_credentials_create(nullptr);
63   } else if (creds_type == "insecure") {
64     return grpc_insecure_credentials_create();
65   } else if (creds_type == "fake") {
66     return grpc_fake_transport_security_credentials_create();
67   }
68   return nullptr;
69 }
70 
71 //
72 // XdsBootstrap::XdsServer
73 //
74 
ShouldUseV3() const75 bool XdsBootstrap::XdsServer::ShouldUseV3() const {
76   return server_features.find("xds_v3") != server_features.end();
77 }
78 
79 //
80 // XdsBootstrap
81 //
82 
Create(absl::string_view json_string,grpc_error_handle * error)83 std::unique_ptr<XdsBootstrap> XdsBootstrap::Create(
84     absl::string_view json_string, grpc_error_handle* error) {
85   Json json = Json::Parse(json_string, error);
86   if (*error != GRPC_ERROR_NONE) {
87     grpc_error_handle error_out =
88         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
89             "Failed to parse bootstrap JSON string", error, 1);
90     GRPC_ERROR_UNREF(*error);
91     *error = error_out;
92     return nullptr;
93   }
94   return absl::make_unique<XdsBootstrap>(std::move(json), error);
95 }
96 
XdsBootstrap(Json json,grpc_error_handle * error)97 XdsBootstrap::XdsBootstrap(Json json, grpc_error_handle* error) {
98   if (json.type() != Json::Type::OBJECT) {
99     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
100         "malformed JSON in bootstrap file");
101     return;
102   }
103   std::vector<grpc_error_handle> error_list;
104   auto it = json.mutable_object()->find("xds_servers");
105   if (it == json.mutable_object()->end()) {
106     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
107         "\"xds_servers\" field not present"));
108   } else if (it->second.type() != Json::Type::ARRAY) {
109     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
110         "\"xds_servers\" field is not an array"));
111   } else {
112     grpc_error_handle parse_error = ParseXdsServerList(&it->second);
113     if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
114   }
115   it = json.mutable_object()->find("node");
116   if (it != json.mutable_object()->end()) {
117     if (it->second.type() != Json::Type::OBJECT) {
118       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
119           "\"node\" field is not an object"));
120     } else {
121       grpc_error_handle parse_error = ParseNode(&it->second);
122       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
123     }
124   }
125   it = json.mutable_object()->find("server_listener_resource_name_template");
126   if (it != json.mutable_object()->end()) {
127     if (it->second.type() != Json::Type::STRING) {
128       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
129           "\"server_listener_resource_name_template\" field is not a string"));
130     } else {
131       server_listener_resource_name_template_ =
132           std::move(*it->second.mutable_string_value());
133     }
134   }
135   if (XdsSecurityEnabled()) {
136     it = json.mutable_object()->find("certificate_providers");
137     if (it != json.mutable_object()->end()) {
138       if (it->second.type() != Json::Type::OBJECT) {
139         error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
140             "\"certificate_providers\" field is not an object"));
141       } else {
142         grpc_error_handle parse_error = ParseCertificateProviders(&it->second);
143         if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
144       }
145     }
146   }
147   *error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
148                                          &error_list);
149 }
150 
ParseXdsServerList(Json * json)151 grpc_error_handle XdsBootstrap::ParseXdsServerList(Json* json) {
152   std::vector<grpc_error_handle> error_list;
153   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
154     Json& child = json->mutable_array()->at(i);
155     if (child.type() != Json::Type::OBJECT) {
156       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
157           absl::StrCat("array element ", i, " is not an object").c_str()));
158     } else {
159       grpc_error_handle parse_error = ParseXdsServer(&child, i);
160       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
161     }
162   }
163   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array",
164                                        &error_list);
165 }
166 
ParseXdsServer(Json * json,size_t idx)167 grpc_error_handle XdsBootstrap::ParseXdsServer(Json* json, size_t idx) {
168   std::vector<grpc_error_handle> error_list;
169   servers_.emplace_back();
170   XdsServer& server = servers_[servers_.size() - 1];
171   auto it = json->mutable_object()->find("server_uri");
172   if (it == json->mutable_object()->end()) {
173     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
174         "\"server_uri\" field not present"));
175   } else if (it->second.type() != Json::Type::STRING) {
176     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
177         "\"server_uri\" field is not a string"));
178   } else {
179     server.server_uri = std::move(*it->second.mutable_string_value());
180   }
181   it = json->mutable_object()->find("channel_creds");
182   if (it == json->mutable_object()->end()) {
183     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
184         "\"channel_creds\" field not present"));
185   } else if (it->second.type() != Json::Type::ARRAY) {
186     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
187         "\"channel_creds\" field is not an array"));
188   } else {
189     grpc_error_handle parse_error =
190         ParseChannelCredsArray(&it->second, &server);
191     if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
192   }
193   it = json->mutable_object()->find("server_features");
194   if (it != json->mutable_object()->end()) {
195     if (it->second.type() != Json::Type::ARRAY) {
196       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
197           "\"server_features\" field is not an array"));
198     } else {
199       grpc_error_handle parse_error =
200           ParseServerFeaturesArray(&it->second, &server);
201       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
202     }
203   }
204   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
205   // string is not static in this case.
206   if (error_list.empty()) return GRPC_ERROR_NONE;
207   grpc_error_handle error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
208       absl::StrCat("errors parsing index ", idx).c_str());
209   for (size_t i = 0; i < error_list.size(); ++i) {
210     error = grpc_error_add_child(error, error_list[i]);
211   }
212   return error;
213 }
214 
ParseChannelCredsArray(Json * json,XdsServer * server)215 grpc_error_handle XdsBootstrap::ParseChannelCredsArray(Json* json,
216                                                        XdsServer* server) {
217   std::vector<grpc_error_handle> error_list;
218   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
219     Json& child = json->mutable_array()->at(i);
220     if (child.type() != Json::Type::OBJECT) {
221       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
222           absl::StrCat("array element ", i, " is not an object").c_str()));
223     } else {
224       grpc_error_handle parse_error = ParseChannelCreds(&child, i, server);
225       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
226     }
227   }
228   if (server->channel_creds_type.empty()) {
229     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
230         "no known creds type found in \"channel_creds\""));
231   }
232   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
233                                        &error_list);
234 }
235 
ParseChannelCreds(Json * json,size_t idx,XdsServer * server)236 grpc_error_handle XdsBootstrap::ParseChannelCreds(Json* json, size_t idx,
237                                                   XdsServer* server) {
238   std::vector<grpc_error_handle> error_list;
239   std::string type;
240   auto it = json->mutable_object()->find("type");
241   if (it == json->mutable_object()->end()) {
242     error_list.push_back(
243         GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field not present"));
244   } else if (it->second.type() != Json::Type::STRING) {
245     error_list.push_back(
246         GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field is not a string"));
247   } else {
248     type = std::move(*it->second.mutable_string_value());
249   }
250   Json config;
251   it = json->mutable_object()->find("config");
252   if (it != json->mutable_object()->end()) {
253     if (it->second.type() != Json::Type::OBJECT) {
254       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
255           "\"config\" field is not an object"));
256     } else {
257       config = std::move(it->second);
258     }
259   }
260   // Select the first channel creds type that we support.
261   if (server->channel_creds_type.empty() &&
262       XdsChannelCredsRegistry::IsSupported(type)) {
263     if (!XdsChannelCredsRegistry::IsValidConfig(type, config)) {
264       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
265           absl::StrCat("invalid config for channel creds type \"", type, "\"")
266               .c_str()));
267     }
268     server->channel_creds_type = std::move(type);
269     server->channel_creds_config = std::move(config);
270   }
271   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
272   // string is not static in this case.
273   if (error_list.empty()) return GRPC_ERROR_NONE;
274   grpc_error_handle error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
275       absl::StrCat("errors parsing index ", idx).c_str());
276   for (size_t i = 0; i < error_list.size(); ++i) {
277     error = grpc_error_add_child(error, error_list[i]);
278   }
279   return error;
280 }
281 
ParseServerFeaturesArray(Json * json,XdsServer * server)282 grpc_error_handle XdsBootstrap::ParseServerFeaturesArray(Json* json,
283                                                          XdsServer* server) {
284   std::vector<grpc_error_handle> error_list;
285   for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
286     Json& child = json->mutable_array()->at(i);
287     if (child.type() == Json::Type::STRING &&
288         child.string_value() == "xds_v3") {
289       server->server_features.insert(std::move(*child.mutable_string_value()));
290     }
291   }
292   return GRPC_ERROR_CREATE_FROM_VECTOR(
293       "errors parsing \"server_features\" array", &error_list);
294 }
295 
ParseNode(Json * json)296 grpc_error_handle XdsBootstrap::ParseNode(Json* json) {
297   std::vector<grpc_error_handle> error_list;
298   node_ = absl::make_unique<Node>();
299   auto it = json->mutable_object()->find("id");
300   if (it != json->mutable_object()->end()) {
301     if (it->second.type() != Json::Type::STRING) {
302       error_list.push_back(
303           GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string"));
304     } else {
305       node_->id = std::move(*it->second.mutable_string_value());
306     }
307   }
308   it = json->mutable_object()->find("cluster");
309   if (it != json->mutable_object()->end()) {
310     if (it->second.type() != Json::Type::STRING) {
311       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
312           "\"cluster\" field is not a string"));
313     } else {
314       node_->cluster = std::move(*it->second.mutable_string_value());
315     }
316   }
317   it = json->mutable_object()->find("locality");
318   if (it != json->mutable_object()->end()) {
319     if (it->second.type() != Json::Type::OBJECT) {
320       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
321           "\"locality\" field is not an object"));
322     } else {
323       grpc_error_handle parse_error = ParseLocality(&it->second);
324       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
325     }
326   }
327   it = json->mutable_object()->find("metadata");
328   if (it != json->mutable_object()->end()) {
329     if (it->second.type() != Json::Type::OBJECT) {
330       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
331           "\"metadata\" field is not an object"));
332     } else {
333       node_->metadata = std::move(it->second);
334     }
335   }
336   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
337                                        &error_list);
338 }
339 
ParseLocality(Json * json)340 grpc_error_handle XdsBootstrap::ParseLocality(Json* json) {
341   std::vector<grpc_error_handle> error_list;
342   auto it = json->mutable_object()->find("region");
343   if (it != json->mutable_object()->end()) {
344     if (it->second.type() != Json::Type::STRING) {
345       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
346           "\"region\" field is not a string"));
347     } else {
348       node_->locality_region = std::move(*it->second.mutable_string_value());
349     }
350   }
351   it = json->mutable_object()->find("zone");
352   if (it != json->mutable_object()->end()) {
353     if (it->second.type() != Json::Type::STRING) {
354       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
355           "\"zone\" field is not a string"));
356     } else {
357       node_->locality_zone = std::move(*it->second.mutable_string_value());
358     }
359   }
360   it = json->mutable_object()->find("sub_zone");
361   if (it != json->mutable_object()->end()) {
362     if (it->second.type() != Json::Type::STRING) {
363       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
364           "\"sub_zone\" field is not a string"));
365     } else {
366       node_->locality_sub_zone = std::move(*it->second.mutable_string_value());
367     }
368   }
369   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
370                                        &error_list);
371 }
372 
ParseCertificateProviders(Json * json)373 grpc_error_handle XdsBootstrap::ParseCertificateProviders(Json* json) {
374   std::vector<grpc_error_handle> error_list;
375   for (auto& certificate_provider : *(json->mutable_object())) {
376     if (certificate_provider.second.type() != Json::Type::OBJECT) {
377       error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
378           absl::StrCat("element \"", certificate_provider.first,
379                        "\" is not an object")
380               .c_str()));
381     } else {
382       grpc_error_handle parse_error = ParseCertificateProvider(
383           certificate_provider.first, &certificate_provider.second);
384       if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
385     }
386   }
387   return GRPC_ERROR_CREATE_FROM_VECTOR(
388       "errors parsing \"certificate_providers\" object", &error_list);
389 }
390 
ParseCertificateProvider(const std::string & instance_name,Json * certificate_provider_json)391 grpc_error_handle XdsBootstrap::ParseCertificateProvider(
392     const std::string& instance_name, Json* certificate_provider_json) {
393   std::vector<grpc_error_handle> error_list;
394   auto it = certificate_provider_json->mutable_object()->find("plugin_name");
395   if (it == certificate_provider_json->mutable_object()->end()) {
396     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
397         "\"plugin_name\" field not present"));
398   } else if (it->second.type() != Json::Type::STRING) {
399     error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
400         "\"plugin_name\" field is not a string"));
401   } else {
402     std::string plugin_name = std::move(*(it->second.mutable_string_value()));
403     CertificateProviderFactory* factory =
404         CertificateProviderRegistry::LookupCertificateProviderFactory(
405             plugin_name);
406     if (factory != nullptr) {
407       RefCountedPtr<CertificateProviderFactory::Config> config;
408       it = certificate_provider_json->mutable_object()->find("config");
409       if (it != certificate_provider_json->mutable_object()->end()) {
410         if (it->second.type() != Json::Type::OBJECT) {
411           error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
412               "\"config\" field is not an object"));
413         } else {
414           grpc_error_handle parse_error = GRPC_ERROR_NONE;
415           config = factory->CreateCertificateProviderConfig(it->second,
416                                                             &parse_error);
417           if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
418         }
419       } else {
420         // "config" is an optional field, so create an empty JSON object.
421         grpc_error_handle parse_error = GRPC_ERROR_NONE;
422         config = factory->CreateCertificateProviderConfig(Json::Object(),
423                                                           &parse_error);
424         if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
425       }
426       certificate_providers_.insert(
427           {instance_name, {std::move(plugin_name), std::move(config)}});
428     }
429   }
430   // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
431   // string is not static in this case.
432   if (error_list.empty()) return GRPC_ERROR_NONE;
433   grpc_error_handle error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
434       absl::StrCat("errors parsing element \"", instance_name, "\"").c_str());
435   for (size_t i = 0; i < error_list.size(); ++i) {
436     error = grpc_error_add_child(error, error_list[i]);
437   }
438   return error;
439 }
440 
ToString() const441 std::string XdsBootstrap::ToString() const {
442   std::vector<std::string> parts;
443   if (node_ != nullptr) {
444     parts.push_back(absl::StrFormat(
445         "node={\n"
446         "  id=\"%s\",\n"
447         "  cluster=\"%s\",\n"
448         "  locality={\n"
449         "    region=\"%s\",\n"
450         "    zone=\"%s\",\n"
451         "    sub_zone=\"%s\"\n"
452         "  },\n"
453         "  metadata=%s,\n"
454         "},\n",
455         node_->id, node_->cluster, node_->locality_region, node_->locality_zone,
456         node_->locality_sub_zone, node_->metadata.Dump()));
457   }
458   parts.push_back(
459       absl::StrFormat("servers=[\n"
460                       "  {\n"
461                       "    uri=\"%s\",\n"
462                       "    creds_type=%s,\n",
463                       server().server_uri, server().channel_creds_type));
464   if (server().channel_creds_config.type() != Json::Type::JSON_NULL) {
465     parts.push_back(absl::StrFormat("    creds_config=%s,",
466                                     server().channel_creds_config.Dump()));
467   }
468   if (!server().server_features.empty()) {
469     parts.push_back(absl::StrCat("    server_features=[",
470                                  absl::StrJoin(server().server_features, ", "),
471                                  "],\n"));
472   }
473   parts.push_back("  }\n],\n");
474   if (!server_listener_resource_name_template_.empty()) {
475     parts.push_back(
476         absl::StrFormat("server_listener_resource_name_template=\"%s\",\n",
477                         server_listener_resource_name_template_));
478   }
479   parts.push_back("certificate_providers={\n");
480   for (const auto& entry : certificate_providers_) {
481     parts.push_back(
482         absl::StrFormat("  %s={\n"
483                         "    plugin_name=%s\n"
484                         "    config=%s\n"
485                         "  },\n",
486                         entry.first, entry.second.plugin_name,
487                         entry.second.config->ToString()));
488   }
489   parts.push_back("}");
490   return absl::StrJoin(parts, "");
491 }
492 
493 }  // namespace grpc_core
494