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