• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 #include "src/core/xds/grpc/xds_metadata_parser.h"
17 
18 #include <memory>
19 #include <utility>
20 
21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/string_view.h"
23 #include "absl/types/variant.h"
24 #include "envoy/config/core/v3/address.upb.h"
25 #include "envoy/config/core/v3/address.upbdefs.h"
26 #include "envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.upb.h"
27 #include "envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.upbdefs.h"
28 #include "src/core/lib/address_utils/sockaddr_utils.h"
29 #include "src/core/util/env.h"
30 #include "src/core/util/string.h"
31 #include "src/core/util/upb_utils.h"
32 #include "src/core/util/validation_errors.h"
33 #include "src/core/xds/grpc/xds_cluster_parser.h"
34 #include "src/core/xds/grpc/xds_common_types.h"
35 #include "src/core/xds/grpc/xds_common_types_parser.h"
36 #include "upb/base/string_view.h"
37 #include "upb/text/encode.h"
38 
39 namespace grpc_core {
40 
41 // TODO(roth): Remove this once GCP auth filter support is stable.
XdsGcpAuthFilterEnabled()42 bool XdsGcpAuthFilterEnabled() {
43   auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER");
44   if (!value.has_value()) return false;
45   bool parsed_value;
46   bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value);
47   return parse_succeeded && parsed_value;
48 }
49 
50 namespace {
51 
ParseGcpAuthnAudience(const XdsResourceType::DecodeContext & context,XdsExtension extension,ValidationErrors * errors)52 std::unique_ptr<XdsMetadataValue> ParseGcpAuthnAudience(
53     const XdsResourceType::DecodeContext& context, XdsExtension extension,
54     ValidationErrors* errors) {
55   absl::string_view* serialized_proto =
56       absl::get_if<absl::string_view>(&extension.value);
57   if (serialized_proto == nullptr) {
58     errors->AddError("could not parse audience metadata");
59     return nullptr;
60   }
61   auto* proto = envoy_extensions_filters_http_gcp_authn_v3_Audience_parse(
62       serialized_proto->data(), serialized_proto->size(), context.arena);
63   if (proto == nullptr) {
64     errors->AddError("could not parse audience metadata");
65     return nullptr;
66   }
67   if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer) && ABSL_VLOG_IS_ON(2)) {
68     const upb_MessageDef* msg_type =
69         envoy_extensions_filters_http_gcp_authn_v3_Audience_getmsgdef(
70             context.symtab);
71     char buf[10240];
72     upb_TextEncode(reinterpret_cast<const upb_Message*>(proto), msg_type,
73                    nullptr, 0, buf, sizeof(buf));
74     VLOG(2) << "[xds_client " << context.client
75             << "] cluster metadata Audience: " << buf;
76   }
77   absl::string_view url = UpbStringToAbsl(
78       envoy_extensions_filters_http_gcp_authn_v3_Audience_url(proto));
79   if (url.empty()) {
80     ValidationErrors::ScopedField field(errors, ".url");
81     errors->AddError("must be non-empty");
82     return nullptr;
83   }
84   return std::make_unique<XdsGcpAuthnAudienceMetadataValue>(url);
85 }
86 
ParseAddress(const XdsResourceType::DecodeContext & context,XdsExtension extension,ValidationErrors * errors)87 std::unique_ptr<XdsMetadataValue> ParseAddress(
88     const XdsResourceType::DecodeContext& context, XdsExtension extension,
89     ValidationErrors* errors) {
90   absl::string_view* serialized_proto =
91       absl::get_if<absl::string_view>(&extension.value);
92   if (serialized_proto == nullptr) {
93     errors->AddError("could not parse address metadata");
94     return nullptr;
95   }
96   auto* proto = envoy_config_core_v3_Address_parse(
97       serialized_proto->data(), serialized_proto->size(), context.arena);
98   if (proto == nullptr) {
99     errors->AddError("could not parse address metadata");
100     return nullptr;
101   }
102   if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer) && ABSL_VLOG_IS_ON(2)) {
103     const upb_MessageDef* msg_type =
104         envoy_config_core_v3_Address_getmsgdef(context.symtab);
105     char buf[10240];
106     upb_TextEncode(reinterpret_cast<const upb_Message*>(proto), msg_type,
107                    nullptr, 0, buf, sizeof(buf));
108     VLOG(2) << "[xds_client " << context.client
109             << "] cluster metadata Address: " << buf;
110   }
111   auto addr = ParseXdsAddress(proto, errors);
112   if (!addr.has_value()) return nullptr;
113   auto addr_uri = grpc_sockaddr_to_string(&*addr, /*normalize=*/false);
114   if (!addr_uri.ok()) {
115     errors->AddError(addr_uri.status().message());
116     return nullptr;
117   }
118   return std::make_unique<XdsAddressMetadataValue>(std::move(*addr_uri));
119 }
120 
121 }  // namespace
122 
ParseXdsMetadataMap(const XdsResourceType::DecodeContext & context,const envoy_config_core_v3_Metadata * metadata,ValidationErrors * errors)123 XdsMetadataMap ParseXdsMetadataMap(
124     const XdsResourceType::DecodeContext& context,
125     const envoy_config_core_v3_Metadata* metadata, ValidationErrors* errors) {
126   XdsMetadataMap metadata_map;
127   if (metadata == nullptr) return metadata_map;  // Not present == empty.
128   // First, try typed_filter_metadata.
129   size_t iter = kUpb_Map_Begin;
130   const envoy_config_core_v3_Metadata_TypedFilterMetadataEntry* typed_entry;
131   while (
132       (typed_entry = envoy_config_core_v3_Metadata_typed_filter_metadata_next(
133            metadata, &iter)) != nullptr) {
134     absl::string_view key = UpbStringToAbsl(
135         envoy_config_core_v3_Metadata_TypedFilterMetadataEntry_key(
136             typed_entry));
137     ValidationErrors::ScopedField field(
138         errors, absl::StrCat(".typed_filter_metadata[", key, "]"));
139     auto extension = ExtractXdsExtension(
140         context,
141         envoy_config_core_v3_Metadata_TypedFilterMetadataEntry_value(
142             typed_entry),
143         errors);
144     if (!extension.has_value()) continue;
145     // TODO(roth): If we start to need a lot of types here, refactor
146     // this into a separate registry.
147     std::unique_ptr<XdsMetadataValue> metadata_value;
148     if (XdsGcpAuthFilterEnabled() &&
149         extension->type == XdsGcpAuthnAudienceMetadataValue::Type()) {
150       metadata_value =
151           ParseGcpAuthnAudience(context, std::move(*extension), errors);
152     } else if (XdsHttpConnectEnabled() &&
153                extension->type == XdsAddressMetadataValue::Type()) {
154       metadata_value = ParseAddress(context, std::move(*extension), errors);
155     }
156     if (metadata_value != nullptr) {
157       metadata_map.Insert(key, std::move(metadata_value));
158     }
159   }
160   // Then, try filter_metadata.
161   iter = kUpb_Map_Begin;
162   const envoy_config_core_v3_Metadata_FilterMetadataEntry* entry;
163   while ((entry = envoy_config_core_v3_Metadata_filter_metadata_next(
164               metadata, &iter)) != nullptr) {
165     absl::string_view key = UpbStringToAbsl(
166         envoy_config_core_v3_Metadata_FilterMetadataEntry_key(entry));
167     auto json = ParseProtobufStructToJson(
168         context,
169         envoy_config_core_v3_Metadata_FilterMetadataEntry_value(entry));
170     if (!json.ok()) {
171       ValidationErrors::ScopedField field(
172           errors, absl::StrCat(".filter_metadata[", key, "]"));
173       errors->AddError(json.status().message());
174     }
175     // Add only if not already added from typed_filter_metadata.
176     else if (metadata_map.Find(key) == nullptr) {
177       metadata_map.Insert(
178           key, std::make_unique<XdsStructMetadataValue>(std::move(*json)));
179     }
180   }
181   return metadata_map;
182 }
183 
184 }  // namespace grpc_core
185