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_grpc.h"
20 
21 #include <stdlib.h>
22 
23 #include <algorithm>
24 #include <initializer_list>
25 #include <set>
26 #include <utility>
27 #include <vector>
28 
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/strings/str_join.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/types/optional.h"
37 
38 #include <grpc/support/json.h>
39 
40 #include "src/core/lib/config/core_configuration.h"
41 #include "src/core/lib/gprpp/ref_counted_ptr.h"
42 #include "src/core/lib/json/json.h"
43 #include "src/core/lib/json/json_object_loader.h"
44 #include "src/core/lib/json/json_reader.h"
45 #include "src/core/lib/json/json_writer.h"
46 #include "src/core/lib/security/credentials/channel_creds_registry.h"
47 
48 namespace grpc_core {
49 
50 //
51 // GrpcXdsBootstrap::GrpcNode::Locality
52 //
53 
JsonLoader(const JsonArgs &)54 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::Locality::JsonLoader(
55     const JsonArgs&) {
56   static const auto* loader =
57       JsonObjectLoader<Locality>()
58           .OptionalField("region", &Locality::region)
59           .OptionalField("zone", &Locality::zone)
60           .OptionalField("sub_zone", &Locality::sub_zone)
61           .Finish();
62   return loader;
63 }
64 
65 //
66 // GrpcXdsBootstrap::GrpcNode
67 //
68 
JsonLoader(const JsonArgs &)69 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::JsonLoader(
70     const JsonArgs&) {
71   static const auto* loader =
72       JsonObjectLoader<GrpcNode>()
73           .OptionalField("id", &GrpcNode::id_)
74           .OptionalField("cluster", &GrpcNode::cluster_)
75           .OptionalField("locality", &GrpcNode::locality_)
76           .OptionalField("metadata", &GrpcNode::metadata_)
77           .Finish();
78   return loader;
79 }
80 
81 //
82 // GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds
83 //
84 
85 const JsonLoaderInterface*
JsonLoader(const JsonArgs &)86 GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds::JsonLoader(const JsonArgs&) {
87   static const auto* loader =
88       JsonObjectLoader<ChannelCreds>()
89           .Field("type", &ChannelCreds::type)
90           .OptionalField("config", &ChannelCreds::config)
91           .Finish();
92   return loader;
93 }
94 
95 //
96 // GrpcXdsBootstrap::GrpcXdsServer
97 //
98 
99 namespace {
100 
101 constexpr absl::string_view kServerFeatureIgnoreResourceDeletion =
102     "ignore_resource_deletion";
103 
104 }  // namespace
105 
IgnoreResourceDeletion() const106 bool GrpcXdsBootstrap::GrpcXdsServer::IgnoreResourceDeletion() const {
107   return server_features_.find(std::string(
108              kServerFeatureIgnoreResourceDeletion)) != server_features_.end();
109 }
110 
Equals(const XdsServer & other) const111 bool GrpcXdsBootstrap::GrpcXdsServer::Equals(const XdsServer& other) const {
112   const auto& o = static_cast<const GrpcXdsServer&>(other);
113   return (server_uri_ == o.server_uri_ &&
114           channel_creds_.type == o.channel_creds_.type &&
115           channel_creds_.config == o.channel_creds_.config &&
116           server_features_ == o.server_features_);
117 }
118 
JsonLoader(const JsonArgs &)119 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcXdsServer::JsonLoader(
120     const JsonArgs&) {
121   static const auto* loader =
122       JsonObjectLoader<GrpcXdsServer>()
123           .Field("server_uri", &GrpcXdsServer::server_uri_)
124           .Finish();
125   return loader;
126 }
127 
JsonPostLoad(const Json & json,const JsonArgs & args,ValidationErrors * errors)128 void GrpcXdsBootstrap::GrpcXdsServer::JsonPostLoad(const Json& json,
129                                                    const JsonArgs& args,
130                                                    ValidationErrors* errors) {
131   // Parse "channel_creds".
132   auto channel_creds_list = LoadJsonObjectField<std::vector<ChannelCreds>>(
133       json.object(), args, "channel_creds", errors);
134   if (channel_creds_list.has_value()) {
135     ValidationErrors::ScopedField field(errors, ".channel_creds");
136     for (size_t i = 0; i < channel_creds_list->size(); ++i) {
137       ValidationErrors::ScopedField field(errors, absl::StrCat("[", i, "]"));
138       auto& creds = (*channel_creds_list)[i];
139       // Select the first channel creds type that we support.
140       if (channel_creds_.type.empty() &&
141           CoreConfiguration::Get().channel_creds_registry().IsSupported(
142               creds.type)) {
143         if (!CoreConfiguration::Get().channel_creds_registry().IsValidConfig(
144                 creds.type, Json::FromObject(creds.config))) {
145           errors->AddError(absl::StrCat(
146               "invalid config for channel creds type \"", creds.type, "\""));
147           continue;
148         }
149         channel_creds_.type = std::move(creds.type);
150         channel_creds_.config = std::move(creds.config);
151       }
152     }
153     if (channel_creds_.type.empty()) {
154       errors->AddError("no known creds type found");
155     }
156   }
157   // Parse "server_features".
158   {
159     ValidationErrors::ScopedField field(errors, ".server_features");
160     auto it = json.object().find("server_features");
161     if (it != json.object().end()) {
162       if (it->second.type() != Json::Type::kArray) {
163         errors->AddError("is not an array");
164       } else {
165         const Json::Array& array = it->second.array();
166         for (const Json& feature_json : array) {
167           if (feature_json.type() == Json::Type::kString &&
168               (feature_json.string() == kServerFeatureIgnoreResourceDeletion)) {
169             server_features_.insert(feature_json.string());
170           }
171         }
172       }
173     }
174   }
175 }
176 
ToJson() const177 Json GrpcXdsBootstrap::GrpcXdsServer::ToJson() const {
178   Json::Object channel_creds_json{
179       {"type", Json::FromString(channel_creds_.type)},
180   };
181   if (!channel_creds_.config.empty()) {
182     channel_creds_json["config"] = Json::FromObject(channel_creds_.config);
183   }
184   Json::Object json{
185       {"server_uri", Json::FromString(server_uri_)},
186       {"channel_creds",
187        Json::FromArray({Json::FromObject(std::move(channel_creds_json))})},
188   };
189   if (!server_features_.empty()) {
190     Json::Array server_features_json;
191     for (auto& feature : server_features_) {
192       server_features_json.emplace_back(Json::FromString(feature));
193     }
194     json["server_features"] = Json::FromArray(std::move(server_features_json));
195   }
196   return Json::FromObject(std::move(json));
197 }
198 
199 //
200 // GrpcXdsBootstrap::GrpcAuthority
201 //
202 
JsonLoader(const JsonArgs &)203 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcAuthority::JsonLoader(
204     const JsonArgs&) {
205   static const auto* loader =
206       JsonObjectLoader<GrpcAuthority>()
207           .OptionalField(
208               "client_listener_resource_name_template",
209               &GrpcAuthority::client_listener_resource_name_template_)
210           .OptionalField("xds_servers", &GrpcAuthority::servers_)
211           .Finish();
212   return loader;
213 }
214 
215 //
216 // GrpcXdsBootstrap
217 //
218 
Create(absl::string_view json_string)219 absl::StatusOr<std::unique_ptr<GrpcXdsBootstrap>> GrpcXdsBootstrap::Create(
220     absl::string_view json_string) {
221   auto json = JsonParse(json_string);
222   if (!json.ok()) {
223     return absl::InvalidArgumentError(absl::StrCat(
224         "Failed to parse bootstrap JSON string: ", json.status().ToString()));
225   }
226   // Validate JSON.
227   class XdsJsonArgs : public JsonArgs {
228    public:
229     bool IsEnabled(absl::string_view key) const override {
230       if (key == "federation") return XdsFederationEnabled();
231       return true;
232     }
233   };
234   auto bootstrap = LoadFromJson<GrpcXdsBootstrap>(*json, XdsJsonArgs());
235   if (!bootstrap.ok()) return bootstrap.status();
236   return std::make_unique<GrpcXdsBootstrap>(std::move(*bootstrap));
237 }
238 
JsonLoader(const JsonArgs &)239 const JsonLoaderInterface* GrpcXdsBootstrap::JsonLoader(const JsonArgs&) {
240   static const auto* loader =
241       JsonObjectLoader<GrpcXdsBootstrap>()
242           .Field("xds_servers", &GrpcXdsBootstrap::servers_)
243           .OptionalField("node", &GrpcXdsBootstrap::node_)
244           .OptionalField("certificate_providers",
245                          &GrpcXdsBootstrap::certificate_providers_)
246           .OptionalField(
247               "server_listener_resource_name_template",
248               &GrpcXdsBootstrap::server_listener_resource_name_template_)
249           .OptionalField("authorities", &GrpcXdsBootstrap::authorities_,
250                          "federation")
251           .OptionalField("client_default_listener_resource_name_template",
252                          &GrpcXdsBootstrap::
253                              client_default_listener_resource_name_template_,
254                          "federation")
255           .Finish();
256   return loader;
257 }
258 
JsonPostLoad(const Json &,const JsonArgs &,ValidationErrors * errors)259 void GrpcXdsBootstrap::JsonPostLoad(const Json& /*json*/,
260                                     const JsonArgs& /*args*/,
261                                     ValidationErrors* errors) {
262   // Verify that there is at least one server present.
263   {
264     ValidationErrors::ScopedField field(errors, ".xds_servers");
265     if (servers_.empty() && !errors->FieldHasErrors()) {
266       errors->AddError("must be non-empty");
267     }
268   }
269   // Verify that each authority has the right prefix in the
270   // client_listener_resource_name_template field.
271   {
272     ValidationErrors::ScopedField field(errors, ".authorities");
273     for (const auto& p : authorities_) {
274       const std::string& name = p.first;
275       const GrpcAuthority& authority =
276           static_cast<const GrpcAuthority&>(p.second);
277       ValidationErrors::ScopedField field(
278           errors, absl::StrCat("[\"", name,
279                                "\"].client_listener_resource_name_template"));
280       std::string expected_prefix = absl::StrCat("xdstp://", name, "/");
281       if (!authority.client_listener_resource_name_template().empty() &&
282           !absl::StartsWith(authority.client_listener_resource_name_template(),
283                             expected_prefix)) {
284         errors->AddError(
285             absl::StrCat("field must begin with \"", expected_prefix, "\""));
286       }
287     }
288   }
289 }
290 
ToString() const291 std::string GrpcXdsBootstrap::ToString() const {
292   std::vector<std::string> parts;
293   if (node_.has_value()) {
294     parts.push_back(
295         absl::StrFormat("node={\n"
296                         "  id=\"%s\",\n"
297                         "  cluster=\"%s\",\n"
298                         "  locality={\n"
299                         "    region=\"%s\",\n"
300                         "    zone=\"%s\",\n"
301                         "    sub_zone=\"%s\"\n"
302                         "  },\n"
303                         "  metadata=%s,\n"
304                         "},\n",
305                         node_->id(), node_->cluster(), node_->locality_region(),
306                         node_->locality_zone(), node_->locality_sub_zone(),
307                         JsonDump(Json::FromObject(node_->metadata()))));
308   }
309   parts.push_back(
310       absl::StrFormat("servers=[\n%s\n],\n", JsonDump(servers_[0].ToJson())));
311   if (!client_default_listener_resource_name_template_.empty()) {
312     parts.push_back(absl::StrFormat(
313         "client_default_listener_resource_name_template=\"%s\",\n",
314         client_default_listener_resource_name_template_));
315   }
316   if (!server_listener_resource_name_template_.empty()) {
317     parts.push_back(
318         absl::StrFormat("server_listener_resource_name_template=\"%s\",\n",
319                         server_listener_resource_name_template_));
320   }
321   parts.push_back("authorities={\n");
322   for (const auto& entry : authorities_) {
323     parts.push_back(absl::StrFormat("  %s={\n", entry.first));
324     parts.push_back(
325         absl::StrFormat("    client_listener_resource_name_template=\"%s\",\n",
326                         entry.second.client_listener_resource_name_template()));
327     if (entry.second.server() != nullptr) {
328       parts.push_back(absl::StrFormat(
329           "    servers=[\n%s\n],\n",
330           JsonDump(static_cast<const GrpcXdsServer*>(entry.second.server())
331                        ->ToJson())));
332     }
333     parts.push_back("      },\n");
334   }
335   parts.push_back("}\n");
336   parts.push_back("certificate_providers={\n");
337   for (const auto& entry : certificate_providers_) {
338     parts.push_back(
339         absl::StrFormat("  %s={\n"
340                         "    plugin_name=%s\n"
341                         "    config=%s\n"
342                         "  },\n",
343                         entry.first, entry.second.plugin_name,
344                         entry.second.config->ToString()));
345   }
346   parts.push_back("}");
347   return absl::StrJoin(parts, "");
348 }
349 
LookupAuthority(const std::string & name) const350 const XdsBootstrap::Authority* GrpcXdsBootstrap::LookupAuthority(
351     const std::string& name) const {
352   auto it = authorities_.find(name);
353   if (it != authorities_.end()) {
354     return &it->second;
355   }
356   return nullptr;
357 }
358 
FindXdsServer(const XdsBootstrap::XdsServer & server) const359 const XdsBootstrap::XdsServer* GrpcXdsBootstrap::FindXdsServer(
360     const XdsBootstrap::XdsServer& server) const {
361   if (static_cast<const GrpcXdsServer&>(server) == servers_[0]) {
362     return &servers_[0];
363   }
364   for (auto& p : authorities_) {
365     const auto* authority_server =
366         static_cast<const GrpcXdsServer*>(p.second.server());
367     if (authority_server != nullptr && *authority_server == server) {
368       return authority_server;
369     }
370   }
371   return nullptr;
372 }
373 
374 }  // namespace grpc_core
375