mirror of https://github.com/istio/proxy.git
203 lines
7.3 KiB
C++
203 lines
7.3 KiB
C++
/* Copyright 2019 Istio Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "extensions/common/context.h"
|
|
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/str_split.h"
|
|
#include "google/protobuf/util/json_util.h"
|
|
|
|
// WASM_PROLOG
|
|
#ifndef NULL_PLUGIN
|
|
#include "proxy_wasm_intrinsics.h"
|
|
|
|
#else // NULL_PLUGIN
|
|
|
|
#include "absl/strings/str_split.h"
|
|
#include "extensions/common/wasm/null/null_plugin.h"
|
|
|
|
using Envoy::Extensions::Common::Wasm::HeaderMapType;
|
|
using Envoy::Extensions::Common::Wasm::MetadataType;
|
|
using Envoy::Extensions::Common::Wasm::StreamType;
|
|
using Envoy::Extensions::Common::Wasm::WasmResult;
|
|
using Envoy::Extensions::Common::Wasm::Null::Plugin::getCurrentTimeNanoseconds;
|
|
using Envoy::Extensions::Common::Wasm::Null::Plugin::getHeaderMapValue;
|
|
using Envoy::Extensions::Common::Wasm::Null::Plugin::getStringValue;
|
|
using Envoy::Extensions::Common::Wasm::Null::Plugin::getStructValue;
|
|
using Envoy::Extensions::Common::Wasm::Null::Plugin::getValue;
|
|
|
|
#endif // NULL_PLUGIN
|
|
|
|
// END WASM_PROLOG
|
|
|
|
namespace Wasm {
|
|
namespace Common {
|
|
|
|
const char kRbacFilterName[] = "envoy.filters.http.rbac";
|
|
const char kRbacPermissivePolicyIDField[] = "shadow_effective_policy_id";
|
|
const char kRbacPermissiveEngineResultField[] = "shadow_engine_result";
|
|
|
|
namespace {
|
|
|
|
// Extract fqdn from Istio cluster name, e.g.
|
|
// inbound|9080|http|productpage.default.svc.cluster.local. If cluster name does
|
|
// not follow Istio convention, fqdn will be left as empty string.
|
|
void extractFqdn(const std::string& cluster_name, std::string* fqdn) {
|
|
const std::vector<std::string>& parts = absl::StrSplit(cluster_name, '|');
|
|
if (parts.size() == 4) {
|
|
*fqdn = parts[3];
|
|
}
|
|
}
|
|
|
|
// Extract service name from service fqdn.
|
|
void extractServiceName(const std::string& fqdn, std::string* service_name) {
|
|
const std::vector<std::string>& parts = absl::StrSplit(fqdn, '.');
|
|
if (parts.size() > 0) {
|
|
*service_name = parts[0];
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
using google::protobuf::util::JsonStringToMessage;
|
|
using google::protobuf::util::MessageToJsonString;
|
|
|
|
google::protobuf::util::Status extractNodeMetadata(
|
|
const google::protobuf::Struct& metadata,
|
|
wasm::common::NodeInfo* node_info) {
|
|
google::protobuf::util::JsonOptions json_options;
|
|
std::string metadata_json_struct;
|
|
auto status =
|
|
MessageToJsonString(metadata, &metadata_json_struct, json_options);
|
|
if (status != google::protobuf::util::Status::OK) {
|
|
return status;
|
|
}
|
|
google::protobuf::util::JsonParseOptions json_parse_options;
|
|
json_parse_options.ignore_unknown_fields = true;
|
|
return JsonStringToMessage(metadata_json_struct, node_info,
|
|
json_parse_options);
|
|
}
|
|
|
|
google::protobuf::util::Status extractLocalNodeMetadata(
|
|
wasm::common::NodeInfo* node_info) {
|
|
google::protobuf::Struct node;
|
|
if (!getStructValue({"node", "metadata"}, &node)) {
|
|
return google::protobuf::util::Status(
|
|
google::protobuf::util::error::Code::NOT_FOUND, "metadata not found");
|
|
}
|
|
return extractNodeMetadata(node, node_info);
|
|
}
|
|
|
|
void populateHTTPRequestInfo(bool outbound, RequestInfo* request_info) {
|
|
// TODO: switch to stream_info.requestComplete() to avoid extra compute.
|
|
request_info->end_timestamp = getCurrentTimeNanoseconds();
|
|
|
|
// Fill in request info.
|
|
int64_t response_code = 0;
|
|
if (getValue({"response", "code"}, &response_code)) {
|
|
request_info->response_code = response_code;
|
|
}
|
|
|
|
if (kGrpcContentTypes.count(getHeaderMapValue(HeaderMapType::RequestHeaders,
|
|
kContentTypeHeaderKey)
|
|
->toString()) != 0) {
|
|
request_info->request_protocol = kProtocolGRPC;
|
|
} else {
|
|
// TODO Add http/1.1, http/1.0, http/2 in a separate attribute.
|
|
// http|grpc classification is compatible with Mixerclient
|
|
request_info->request_protocol = kProtocolHTTP;
|
|
}
|
|
|
|
// Try to get fqdn of destination service from cluster name. If not found, use
|
|
// host header instead.
|
|
std::string cluster_name = "";
|
|
getStringValue({"cluster_name"}, &cluster_name);
|
|
extractFqdn(cluster_name, &request_info->destination_service_host);
|
|
if (request_info->destination_service_host.empty()) {
|
|
// fallback to host header.
|
|
request_info->destination_service_host =
|
|
getHeaderMapValue(HeaderMapType::RequestHeaders, kAuthorityHeaderKey)
|
|
->toString();
|
|
} else {
|
|
// cluster name follows Istio convention, so extract out service name.
|
|
extractServiceName(request_info->destination_service_host,
|
|
&request_info->destination_service_name);
|
|
}
|
|
|
|
// Get rbac labels from dynamic metadata.
|
|
getStringValue({"metadata", kRbacFilterName, kRbacPermissivePolicyIDField},
|
|
&request_info->rbac_permissive_policy_id);
|
|
getStringValue(
|
|
{"metadata", kRbacFilterName, kRbacPermissiveEngineResultField},
|
|
&request_info->rbac_permissive_engine_result);
|
|
|
|
request_info->request_operation =
|
|
getHeaderMapValue(HeaderMapType::RequestHeaders, kMethodHeaderKey)
|
|
->toString();
|
|
|
|
int64_t destination_port = 0;
|
|
std::string tls_version;
|
|
|
|
if (outbound) {
|
|
getValue({"upstream", "port"}, &destination_port);
|
|
getValue({"upstream", "mtls"}, &request_info->mTLS);
|
|
getStringValue({"upstream", "tls_version"}, &tls_version);
|
|
} else {
|
|
getValue({"destination", "port"}, &destination_port);
|
|
getValue({"connection", "mtls"}, &request_info->mTLS);
|
|
getStringValue({"connection", "tls_version"}, &tls_version);
|
|
}
|
|
request_info->destination_port = destination_port;
|
|
}
|
|
|
|
google::protobuf::util::Status extractNodeMetadataValue(
|
|
const google::protobuf::Struct& node_metadata,
|
|
google::protobuf::Struct* metadata) {
|
|
if (metadata == nullptr) {
|
|
return google::protobuf::util::Status(
|
|
google::protobuf::util::error::INVALID_ARGUMENT,
|
|
"metadata provided is null");
|
|
}
|
|
const auto key_it = node_metadata.fields().find("EXCHANGE_KEYS");
|
|
if (key_it == node_metadata.fields().end()) {
|
|
return google::protobuf::util::Status(
|
|
google::protobuf::util::error::INVALID_ARGUMENT,
|
|
"metadata exchange key is missing");
|
|
}
|
|
|
|
const auto& keys_value = key_it->second;
|
|
if (keys_value.kind_case() != google::protobuf::Value::kStringValue) {
|
|
return google::protobuf::util::Status(
|
|
google::protobuf::util::error::INVALID_ARGUMENT,
|
|
"metadata exchange key is not a string");
|
|
}
|
|
|
|
// select keys from the metadata using the keys
|
|
const std::set<std::string> keys =
|
|
absl::StrSplit(keys_value.string_value(), ',', absl::SkipWhitespace());
|
|
for (auto key : keys) {
|
|
const auto entry_it = node_metadata.fields().find(key);
|
|
if (entry_it == node_metadata.fields().end()) {
|
|
continue;
|
|
}
|
|
(*metadata->mutable_fields())[key] = entry_it->second;
|
|
}
|
|
|
|
return google::protobuf::util::Status(google::protobuf::util::error::OK, "");
|
|
}
|
|
|
|
} // namespace Common
|
|
} // namespace Wasm
|