diff --git a/src/envoy/BUILD b/src/envoy/BUILD index 96743a7a96..157daef58b 100644 --- a/src/envoy/BUILD +++ b/src/envoy/BUILD @@ -32,6 +32,7 @@ envoy_cc_binary( "//src/envoy/http/jwt_auth:http_filter_factory", "//src/envoy/http/mixer:filter_lib", "//src/envoy/tcp/forward_downstream_sni:config_lib", + "//src/envoy/tcp/metadata_exchange:config_lib", "//src/envoy/tcp/mixer:filter_lib", "//src/envoy/tcp/sni_verifier:config_lib", "//src/envoy/tcp/tcp_cluster_rewrite:config_lib", diff --git a/src/envoy/tcp/metadata_exchange/BUILD b/src/envoy/tcp/metadata_exchange/BUILD new file mode 100644 index 0000000000..347e61095d --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/BUILD @@ -0,0 +1,87 @@ +# 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. +# +################################################################################ +# + +# Metadata Exchange filter + +load( + "@envoy//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_cc_test", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "metadata_exchange", + srcs = [ + "metadata_exchange.cc", + "metadata_exchange_initial_header.cc", + ], + hdrs = [ + "metadata_exchange.h", + "metadata_exchange_initial_header.h", + ], + repository = "@envoy", + deps = [ + "//src/envoy/tcp/metadata_exchange/config:metadata_exchange_cc_proto", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:endian", + "@com_google_absl//absl/strings", + "@envoy//include/envoy/local_info:local_info_interface", + "@envoy//include/envoy/network:connection_interface", + "@envoy//include/envoy/network:filter_interface", + "@envoy//include/envoy/runtime:runtime_interface", + "@envoy//include/envoy/stats:stats_macros", + "@envoy//source/common/http:utility_lib", + "@envoy//source/common/network:utility_lib", + "@envoy//source/common/protobuf", + "@envoy//source/common/protobuf:utility_lib", + "@envoy//source/extensions/filters/network:well_known_names", + ], +) + +envoy_cc_library( + name = "config_lib", + srcs = ["config.cc"], + hdrs = ["config.h"], + repository = "@envoy", + visibility = ["//visibility:public"], + deps = [ + ":metadata_exchange", + "//src/envoy/tcp/metadata_exchange/config:metadata_exchange_cc_proto", + "//src/envoy/utils:utils_lib", + "@envoy//include/envoy/registry", + "@envoy//source/extensions/filters/network/common:factory_base_lib", + ], +) + +envoy_cc_test( + name = "metadataexchange_test", + srcs = [ + "metadata_exchange_test.cc", + ], + repository = "@envoy", + deps = [ + ":config_lib", + ":metadata_exchange", + "@envoy//source/common/protobuf", + "@envoy//test/mocks/local_info:local_info_mocks", + "@envoy//test/mocks/network:network_mocks", + "@envoy//test/mocks/protobuf:protobuf_mocks", + ], +) diff --git a/src/envoy/tcp/metadata_exchange/config.cc b/src/envoy/tcp/metadata_exchange/config.cc new file mode 100644 index 0000000000..e3926910fd --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/config.cc @@ -0,0 +1,116 @@ +/* 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 "src/envoy/tcp/metadata_exchange/config.h" +#include "envoy/network/connection.h" +#include "envoy/registry/registry.h" +#include "src/envoy/tcp/metadata_exchange/metadata_exchange.h" +#include "src/envoy/utils/config.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { +namespace { + +static constexpr char StatPrefix[] = "metadata_exchange."; + +Network::FilterFactoryCb createFilterFactoryHelper( + const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, + Server::Configuration::CommonFactoryContext& context, + FilterDirection filter_direction) { + ASSERT(!proto_config.protocol().empty()); + + MetadataExchangeConfigSharedPtr filter_config( + std::make_shared( + StatPrefix, proto_config.protocol(), proto_config.node_metadata_id(), + filter_direction, context.scope())); + return [filter_config, + &context](Network::FilterManager& filter_manager) -> void { + filter_manager.addFilter(std::make_shared( + filter_config, context.localInfo())); + }; +} +} // namespace + +Network::FilterFactoryCb +MetadataExchangeConfigFactory::createFilterFactoryFromProto( + const Protobuf::Message& config, + Server::Configuration::FactoryContext& context) { + return createFilterFactory( + dynamic_cast< + const envoy::tcp::metadataexchange::config::MetadataExchange&>( + config), + context); +} + +ProtobufTypes::MessagePtr +MetadataExchangeConfigFactory::createEmptyConfigProto() { + return std::make_unique< + envoy::tcp::metadataexchange::config::MetadataExchange>(); +} + +Network::FilterFactoryCb MetadataExchangeConfigFactory::createFilterFactory( + const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, + Server::Configuration::FactoryContext& context) { + return createFilterFactoryHelper(proto_config, context, + FilterDirection::Downstream); +} + +Network::FilterFactoryCb +MetadataExchangeUpstreamConfigFactory::createFilterFactoryFromProto( + const Protobuf::Message& config, + Server::Configuration::CommonFactoryContext& context) { + return createFilterFactory( + dynamic_cast< + const envoy::tcp::metadataexchange::config::MetadataExchange&>( + config), + context); +} + +ProtobufTypes::MessagePtr +MetadataExchangeUpstreamConfigFactory::createEmptyConfigProto() { + return std::make_unique< + envoy::tcp::metadataexchange::config::MetadataExchange>(); +} + +Network::FilterFactoryCb +MetadataExchangeUpstreamConfigFactory::createFilterFactory( + const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, + Server::Configuration::CommonFactoryContext& context) { + return createFilterFactoryHelper(proto_config, context, + FilterDirection::Upstream); +} + +/** + * Static registration for the MetadataExchange Downstream filter. @see + * RegisterFactory. + */ +static Registry::RegisterFactory< + MetadataExchangeConfigFactory, + Server::Configuration::NamedNetworkFilterConfigFactory> + registered_; + +/** + * Static registration for the MetadataExchange Upstream filter. @see + * RegisterFactory. + */ +static Registry::RegisterFactory< + MetadataExchangeUpstreamConfigFactory, + Server::Configuration::NamedUpstreamNetworkFilterConfigFactory> + registered_upstream_; + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/metadata_exchange/config.h b/src/envoy/tcp/metadata_exchange/config.h new file mode 100644 index 0000000000..10871551e1 --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/config.h @@ -0,0 +1,80 @@ +/* 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. + */ + +#pragma once + +#include "extensions/filters/network/common/factory_base.h" +#include "src/envoy/tcp/metadata_exchange/config/metadata_exchange.pb.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { + +/** + * Config registration for the MetadataExchange filter. @see + * NamedNetworkFilterConfigFactory. + */ +class MetadataExchangeConfigFactory + : public Server::Configuration::NamedNetworkFilterConfigFactory { + public: + Network::FilterFactoryCb createFilterFactory( + const Json::Object&, Server::Configuration::FactoryContext&) override { + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + + Network::FilterFactoryCb createFilterFactoryFromProto( + const Protobuf::Message&, + Server::Configuration::FactoryContext&) override; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + + std::string name() override { + return "envoy.filters.network.metadata_exchange"; + } + + private: + Network::FilterFactoryCb createFilterFactory( + const envoy::tcp::metadataexchange::config::MetadataExchange& + proto_config, + Server::Configuration::FactoryContext& context); +}; + +/** + * Config registration for the MetadataExchange Upstream filter. @see + * NamedUpstreamNetworkFilterConfigFactory. + */ +class MetadataExchangeUpstreamConfigFactory + : public Server::Configuration::NamedUpstreamNetworkFilterConfigFactory { + public: + Network::FilterFactoryCb createFilterFactoryFromProto( + const Protobuf::Message&, + Server::Configuration::CommonFactoryContext&) override; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + + std::string name() override { + return "envoy.filters.network.upstream.metadata_exchange"; + } + + private: + Network::FilterFactoryCb createFilterFactory( + const envoy::tcp::metadataexchange::config::MetadataExchange& + proto_config, + Server::Configuration::CommonFactoryContext& context); +}; + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/metadata_exchange/config/BUILD b/src/envoy/tcp/metadata_exchange/config/BUILD new file mode 100644 index 0000000000..f9a9602242 --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/config/BUILD @@ -0,0 +1,27 @@ +# 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. +# +################################################################################ +# + +proto_library( + name = "metadata_exchange_proto", + srcs = ["metadata_exchange.proto"], +) + +cc_proto_library( + name = "metadata_exchange_cc_proto", + visibility = ["//visibility:public"], + deps = ["metadata_exchange_proto"], +) diff --git a/src/envoy/tcp/metadata_exchange/config/metadata_exchange.proto b/src/envoy/tcp/metadata_exchange/config/metadata_exchange.proto new file mode 100644 index 0000000000..f69c46520e --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/config/metadata_exchange.proto @@ -0,0 +1,35 @@ +/* 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. + */ + +syntax = "proto3"; + +package envoy.tcp.metadataexchange.config; + +option java_outer_classname = "MetadataExchangeProto"; +option java_multiple_files = true; +option java_package = "io.envoyproxy.envoy.tcp.metadataexchange.config"; +option go_package = "MetadataExchange"; + +// [#protodoc-title: MetadataExchange protocol match and data transfer] +// MetadataExchange protocol match and data transfer +message MetadataExchange { + // Protocol that Alpn should support on the server. + // [#comment:TODO(GargNupur): Make it a list.] + string protocol = 1; + + // The node metadata id whose data will be written to connection data. + // [#comment: TODO(GargNupur): Remove this and use bootstrap node.id] + string node_metadata_id = 2; +} diff --git a/src/envoy/tcp/metadata_exchange/metadata_exchange.cc b/src/envoy/tcp/metadata_exchange/metadata_exchange.cc new file mode 100644 index 0000000000..983c8a56a5 --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/metadata_exchange.cc @@ -0,0 +1,256 @@ +/* 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 +#include + +#include "absl/base/internal/endian.h" +#include "absl/strings/string_view.h" +#include "common/buffer/buffer_impl.h" +#include "common/protobuf/utility.h" +#include "envoy/network/connection.h" +#include "envoy/stats/scope.h" +#include "src/envoy/tcp/metadata_exchange/metadata_exchange.h" +#include "src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { +namespace { + +std::unique_ptr<::Envoy::Buffer::OwnedImpl> constructProxyHeaderData( + const Envoy::ProtobufWkt::Any& proxy_data) { + MetadataExchangeInitialHeader initial_header; + std::string proxy_data_str = proxy_data.SerializeAsString(); + // Converting from host to network byte order so that most significant byte is + // placed first. + initial_header.magic = + absl::ghtonl(MetadataExchangeInitialHeader::magic_number); + initial_header.data_size = absl::ghtonl(proxy_data_str.length()); + + ::Envoy::Buffer::OwnedImpl initial_header_buffer{ + absl::string_view(reinterpret_cast(&initial_header), + sizeof(MetadataExchangeInitialHeader))}; + auto proxy_data_buffer = + std::make_unique<::Envoy::Buffer::OwnedImpl>(proxy_data_str); + proxy_data_buffer->prepend(initial_header_buffer); + return proxy_data_buffer; +} + +} // namespace + +MetadataExchangeConfig::MetadataExchangeConfig( + const std::string& stat_prefix, const std::string& protocol, + const std::string& node_metadata_id, const FilterDirection filter_direction, + Stats::Scope& scope) + : scope_(scope), + stat_prefix_(stat_prefix), + protocol_(protocol), + node_metadata_id_(node_metadata_id), + filter_direction_(filter_direction), + stats_(generateStats(stat_prefix, scope)) {} + +Network::FilterStatus MetadataExchangeFilter::onData(Buffer::Instance& data, + bool) { + switch (conn_state_) { + case Invalid: + case Done: + // No work needed if connection state is Done or Invalid. + return Network::FilterStatus::Continue; + case ConnProtocolNotRead: { + // If Alpn protocol is not the expected one, then return. + // Else find and write node metadata. + if (read_callbacks_->connection().nextProtocol() != config_->protocol_) { + conn_state_ = Invalid; + config_->stats().alpn_protocol_not_found_.inc(); + return Network::FilterStatus::Continue; + } + conn_state_ = WriteMetadata; + config_->stats().alpn_protocol_found_.inc(); + } + case WriteMetadata: { + // TODO(gargnupur): Try to move this just after alpn protocol is + // determined and first onData is called in Downstream filter. + // If downstream filter, write metadata. + // Otherwise, go ahead and try to read initial header and proxy data. + writeNodeMetadata(); + } + case ReadingInitialHeader: + case NeedMoreDataInitialHeader: { + tryReadInitialProxyHeader(data); + if (conn_state_ == NeedMoreDataInitialHeader) { + return Network::FilterStatus::StopIteration; + } + if (conn_state_ == Invalid) { + return Network::FilterStatus::Continue; + } + } + case ReadingProxyHeader: + case NeedMoreDataProxyHeader: { + tryReadProxyData(data); + if (conn_state_ == NeedMoreDataProxyHeader) { + return Network::FilterStatus::StopIteration; + } + if (conn_state_ == Invalid) { + return Network::FilterStatus::Continue; + } + } + default: + conn_state_ = Done; + return Network::FilterStatus::Continue; + } + + return Network::FilterStatus::Continue; +} + +Network::FilterStatus MetadataExchangeFilter::onNewConnection() { + return Network::FilterStatus::Continue; +} + +Network::FilterStatus MetadataExchangeFilter::onWrite(Buffer::Instance&, bool) { + switch (conn_state_) { + case Invalid: + case Done: + // No work needed if connection state is Done or Invalid. + return Network::FilterStatus::Continue; + case ConnProtocolNotRead: { + if (read_callbacks_->connection().nextProtocol() != config_->protocol_) { + conn_state_ = Invalid; + config_->stats().alpn_protocol_not_found_.inc(); + return Network::FilterStatus::Continue; + } else { + conn_state_ = WriteMetadata; + config_->stats().alpn_protocol_found_.inc(); + } + } + case WriteMetadata: { + // TODO(gargnupur): Try to move this just after alpn protocol is + // determined and first onWrite is called in Upstream filter. + writeNodeMetadata(); + } + case ReadingInitialHeader: + case ReadingProxyHeader: + case NeedMoreDataInitialHeader: + case NeedMoreDataProxyHeader: + // These are to be handled in Reading Pipeline. + return Network::FilterStatus::Continue; + } + + return Network::FilterStatus::Continue; +} + +void MetadataExchangeFilter::writeNodeMetadata() { + if (conn_state_ != WriteMetadata) { + return; + } + + std::unique_ptr metadata = + getMetadata(config_->node_metadata_id_); + if (metadata != nullptr) { + Envoy::ProtobufWkt::Any metadata_any_value; + *metadata_any_value.mutable_type_url() = StructTypeUrl; + *metadata_any_value.mutable_value() = metadata->SerializeAsString(); + std::unique_ptr<::Envoy::Buffer::OwnedImpl> buf = + constructProxyHeaderData(metadata_any_value); + write_callbacks_->injectWriteDataToFilterChain(*buf, false); + + if (config_->filter_direction_ == FilterDirection::Downstream) { + setMetadata(DownstreamDynamicDataKey, *metadata); + } else { + setMetadata(UpstreamDynamicDataKey, *metadata); + } + config_->stats().metadata_added_.inc(); + } + + conn_state_ = ReadingInitialHeader; +} + +void MetadataExchangeFilter::tryReadInitialProxyHeader(Buffer::Instance& data) { + if (conn_state_ != ReadingInitialHeader && + conn_state_ != NeedMoreDataInitialHeader) { + return; + } + const uint32_t initial_header_length = sizeof(MetadataExchangeInitialHeader); + if (data.length() < initial_header_length) { + config_->stats().initial_header_not_found_.inc(); + // Not enough data to read. Wait for it to come. + conn_state_ = NeedMoreDataInitialHeader; + return; + } + MetadataExchangeInitialHeader initial_header; + data.copyOut(0, initial_header_length, &initial_header); + if (absl::gntohl(initial_header.magic) != + MetadataExchangeInitialHeader::magic_number) { + config_->stats().initial_header_not_found_.inc(); + conn_state_ = Invalid; + return; + } + proxy_data_length_ = absl::gntohl(initial_header.data_size); + // Drain the initial header length bytes read. + data.drain(initial_header_length); + conn_state_ = ReadingProxyHeader; +} + +void MetadataExchangeFilter::tryReadProxyData(Buffer::Instance& data) { + if (conn_state_ != ReadingProxyHeader && + conn_state_ != NeedMoreDataProxyHeader) { + return; + } + if (data.length() < proxy_data_length_) { + // Not enough data to read. Wait for it to come. + conn_state_ = NeedMoreDataProxyHeader; + return; + } + std::string proxy_data_buf = + std::string(static_cast(data.linearize(proxy_data_length_)), + proxy_data_length_); + Envoy::ProtobufWkt::Any proxy_data; + if (!proxy_data.ParseFromString(proxy_data_buf)) { + config_->stats().header_not_found_.inc(); + conn_state_ = Invalid; + return; + } + data.drain(proxy_data_length_); + + Envoy::ProtobufWkt::Struct struct_metadata = + Envoy::MessageUtil::anyConvert(proxy_data); + if (config_->filter_direction_ == FilterDirection::Downstream) { + setMetadata(UpstreamDynamicDataKey, struct_metadata); + } else { + setMetadata(DownstreamDynamicDataKey, struct_metadata); + } +} + +void MetadataExchangeFilter::setMetadata(const std::string key, + const ProtobufWkt::Struct& value) { + read_callbacks_->connection().streamInfo().setDynamicMetadata(key, value); +} + +std::unique_ptr +MetadataExchangeFilter::getMetadata(const std::string& key) { + if (local_info_.node().has_metadata()) { + auto metadata_fields = local_info_.node().metadata().fields(); + auto node_metadata = metadata_fields.find(key); + if (node_metadata != metadata_fields.end()) { + return std::make_unique( + node_metadata->second.struct_value()); + } + } + return nullptr; +} + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy \ No newline at end of file diff --git a/src/envoy/tcp/metadata_exchange/metadata_exchange.h b/src/envoy/tcp/metadata_exchange/metadata_exchange.h new file mode 100644 index 0000000000..4c1d87828a --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/metadata_exchange.h @@ -0,0 +1,174 @@ +/* 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. + */ + +#pragma once + +#include + +#include "common/protobuf/protobuf.h" +#include "envoy/local_info/local_info.h" +#include "envoy/network/filter.h" +#include "envoy/runtime/runtime.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" +#include "src/envoy/tcp/metadata_exchange/config/metadata_exchange.pb.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { + +/** + * All MetadataExchange filter stats. @see stats_macros.h + */ +#define ALL_METADATA_EXCHANGE_STATS(COUNTER) \ + COUNTER(alpn_protocol_not_found) \ + COUNTER(alpn_protocol_found) \ + COUNTER(initial_header_not_found) \ + COUNTER(header_not_found) \ + COUNTER(metadata_added) + +/** + * Struct definition for all MetadataExchange stats. @see stats_macros.h + */ +struct MetadataExchangeStats { + ALL_METADATA_EXCHANGE_STATS(GENERATE_COUNTER_STRUCT) +}; + +/** + * Direction of the flow of traffic in which this this MetadataExchange filter + * is placed. + */ +enum FilterDirection { Downstream, Upstream }; + +/** + * Configuration for the MetadataExchange filter. + */ +class MetadataExchangeConfig { + public: + MetadataExchangeConfig(const std::string& stat_prefix, + const std::string& protocol, + const std::string& node_metadata_id, + const FilterDirection filter_direction, + Stats::Scope& scope); + + const MetadataExchangeStats& stats() { return stats_; } + + // Scope for the stats. + Stats::Scope& scope_; + // Stat prefix. + const std::string stat_prefix_; + // Expected Alpn Protocol. + const std::string protocol_; + // Node metadata id to read. + const std::string node_metadata_id_; + // Direction of filter. + const FilterDirection filter_direction_; + // Stats for MetadataExchange Filter. + MetadataExchangeStats stats_; + + private: + MetadataExchangeStats generateStats(const std::string& prefix, + Stats::Scope& scope) { + return MetadataExchangeStats{ + ALL_METADATA_EXCHANGE_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + } +}; + +using MetadataExchangeConfigSharedPtr = std::shared_ptr; + +/** + * A MetadataExchange filter instance. One per connection. + */ +class MetadataExchangeFilter : public Network::Filter { + public: + MetadataExchangeFilter(MetadataExchangeConfigSharedPtr config, + const LocalInfo::LocalInfo& local_info) + : config_(config), + local_info_(local_info), + conn_state_(ConnProtocolNotRead) {} + + // Network::ReadFilter + Network::FilterStatus onData(Buffer::Instance& data, + bool end_stream) override; + Network::FilterStatus onNewConnection() override; + Network::FilterStatus onWrite(Buffer::Instance& data, + bool end_stream) override; + void initializeReadFilterCallbacks( + Network::ReadFilterCallbacks& callbacks) override { + read_callbacks_ = &callbacks; + // read_callbacks_->connection().addConnectionCallbacks(*this); + } + void initializeWriteFilterCallbacks( + Network::WriteFilterCallbacks& callbacks) override { + write_callbacks_ = &callbacks; + } + + private: + // Writes node metadata in write pipeline of the filter chain. + // Also, sets node metadata in Dynamic Metadata to be available for subsequent + // filters. + void writeNodeMetadata(); + + // Tries to read inital proxy header in the data bytes. + void tryReadInitialProxyHeader(Buffer::Instance& data); + + // Tries to read data after initial proxy header. This is currently in the + // form of google::protobuf::any which encapsulates google::protobuf::struct. + void tryReadProxyData(Buffer::Instance& data); + + // Helper function to set Dynamic metadata. + void setMetadata(const std::string key, const ProtobufWkt::Struct& value); + + // Helper function to get Dynamic metadata. + std::unique_ptr getMetadata( + const std::string& key); + + // Config for MetadataExchange filter. + MetadataExchangeConfigSharedPtr config_; + // LocalInfo instance. + const LocalInfo::LocalInfo& local_info_; + // Read callback instance. + Network::ReadFilterCallbacks* read_callbacks_{}; + // Write callback instance. + Network::WriteFilterCallbacks* write_callbacks_{}; + // Stores the length of proxy data that contains node metadata. + uint64_t proxy_data_length_{0}; + + // Key Identifier for dynamic metadata in upstream filter. + const std::string UpstreamDynamicDataKey = + "filters.network.metadata_exchange.upstream"; + // Key Identifier for dynamic metadata in downstream filter. + const std::string DownstreamDynamicDataKey = + "filters.network.metadata_exchange.downstream"; + // Type url of google::protobug::struct. + const std::string StructTypeUrl = + "type.googleapis.com/google.protobuf.Struct"; + + // Captures the state machine of what is going on in the filter. + enum { + ConnProtocolNotRead, // Connection Protocol has not been read yet + WriteMetadata, // Write node metadata + ReadingInitialHeader, // MetadataExchangeInitialHeader is being read + ReadingProxyHeader, // Proxy Header is being read + NeedMoreDataInitialHeader, // Need more data to be read + NeedMoreDataProxyHeader, // Need more data to be read + Done, // Alpn Protocol Found and all the read is done + Invalid, // Invalid state, all operations fail + } conn_state_; +}; + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.cc b/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.cc new file mode 100644 index 0000000000..88fbc785fa --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.cc @@ -0,0 +1,26 @@ +/* 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 "src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { + +const uint32_t MetadataExchangeInitialHeader::magic_number; + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy \ No newline at end of file diff --git a/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h b/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h new file mode 100644 index 0000000000..cdf72cea93 --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h @@ -0,0 +1,37 @@ +/* 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. + */ + +#pragma once + +#include + +#include "envoy/common/platform.h" + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { + +// Used with MetadataExchangeHeaderProto to be extensible. +PACKED_STRUCT(struct MetadataExchangeInitialHeader { + uint32_t magic; // Magic number in network byte order. Most significant byte + // is placed first. + static const uint32_t magic_number = 0x3D230467; // decimal 1025705063 + uint32_t data_size; // Size of the data blob in network byte order. Most + // significant byte is placed first. +}); + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy \ No newline at end of file diff --git a/src/envoy/tcp/metadata_exchange/metadata_exchange_test.cc b/src/envoy/tcp/metadata_exchange/metadata_exchange_test.cc new file mode 100644 index 0000000000..5d61795180 --- /dev/null +++ b/src/envoy/tcp/metadata_exchange/metadata_exchange_test.cc @@ -0,0 +1,148 @@ +/* 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 "src/envoy/tcp/metadata_exchange/metadata_exchange.h" +#include "common/buffer/buffer_impl.h" +#include "common/protobuf/protobuf.h" +#include "gmock/gmock.h" +#include "google/protobuf/util/message_differencer.h" +#include "gtest/gtest.h" +#include "src/envoy/tcp/metadata_exchange/metadata_exchange_initial_header.h" +#include "test/mocks/local_info/mocks.h" +#include "test/mocks/network/mocks.h" +#include "test/mocks/protobuf/mocks.h" + +using ::google::protobuf::util::MessageDifferencer; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Tcp { +namespace MetadataExchange { +namespace { + +MATCHER_P(MapEq, rhs, "") { return MessageDifferencer::Equals(arg, rhs); } + +void ConstructProxyHeaderData(::Envoy::Buffer::OwnedImpl& serialized_header, + Envoy::ProtobufWkt::Any& proxy_header, + MetadataExchangeInitialHeader* initial_header) { + std::string serialized_proxy_header = proxy_header.SerializeAsString(); + memset(initial_header, 0, sizeof(MetadataExchangeInitialHeader)); + initial_header->magic = + absl::ghtonl(MetadataExchangeInitialHeader::magic_number); + initial_header->data_size = absl::ghtonl(serialized_proxy_header.length()); + serialized_header.add(::Envoy::Buffer::OwnedImpl{ + absl::string_view(reinterpret_cast(initial_header), + sizeof(MetadataExchangeInitialHeader))}); + serialized_header.add(::Envoy::Buffer::OwnedImpl{serialized_proxy_header}); +} + +} // namespace + +class MetadataExchangeFilterTest : public testing::Test { + public: + MetadataExchangeFilterTest() { ENVOY_LOG_MISC(info, "test"); } + + void initialize() { + config_ = std::make_shared( + stat_prefix_, "istio2", "istio/metadata", FilterDirection::Downstream, + scope_); + filter_ = std::make_unique(config_, local_info_); + filter_->initializeReadFilterCallbacks(read_filter_callbacks_); + filter_->initializeWriteFilterCallbacks(write_filter_callbacks_); + metadata_node_.set_id("test"); + EXPECT_CALL(read_filter_callbacks_.connection_, streamInfo()) + .WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(local_info_, node()).WillRepeatedly(ReturnRef(metadata_node_)); + } + + void initializeStructValues() { + (*details_value_.mutable_fields())["namespace"].set_string_value("default"); + (*details_value_.mutable_fields())["labels"].set_string_value( + "{app, details}"); + + (*productpage_value_.mutable_fields())["namespace"].set_string_value( + "default"); + (*productpage_value_.mutable_fields())["labels"].set_string_value( + "{app, productpage}"); + } + + Envoy::ProtobufWkt::Struct details_value_; + Envoy::ProtobufWkt::Struct productpage_value_; + MetadataExchangeConfigSharedPtr config_; + std::unique_ptr filter_; + Stats::IsolatedStoreImpl scope_; + std::string stat_prefix_{"test.metadataexchange"}; + NiceMock read_filter_callbacks_; + NiceMock write_filter_callbacks_; + Network::MockConnection connection_; + NiceMock local_info_; + NiceMock stream_info_; + envoy::api::v2::core::Node metadata_node_; +}; + +TEST_F(MetadataExchangeFilterTest, MetadataExchangeFound) { + initialize(); + initializeStructValues(); + + auto node_metadata_map = metadata_node_.mutable_metadata()->mutable_fields(); + google::protobuf::Value& value = (*node_metadata_map)["istio/metadata"]; + (*value.mutable_struct_value()).CopyFrom(details_value_); + + EXPECT_CALL(read_filter_callbacks_.connection_, nextProtocol()) + .WillRepeatedly(Return("istio2")); + EXPECT_CALL(stream_info_, + setDynamicMetadata("filters.network.metadata_exchange.downstream", + MapEq(details_value_))); + EXPECT_CALL(stream_info_, + setDynamicMetadata("filters.network.metadata_exchange.upstream", + MapEq(productpage_value_))); + + ::Envoy::Buffer::OwnedImpl data; + MetadataExchangeInitialHeader initial_header; + Envoy::ProtobufWkt::Any productpage_any_value; + *productpage_any_value.mutable_type_url() = + "type.googleapis.com/google.protobuf.Struct"; + *productpage_any_value.mutable_value() = + productpage_value_.SerializeAsString(); + ConstructProxyHeaderData(data, productpage_any_value, &initial_header); + ::Envoy::Buffer::OwnedImpl world{"world"}; + data.add(world); + + EXPECT_EQ(Envoy::Network::FilterStatus::Continue, + filter_->onData(data, false)); + EXPECT_EQ(data.toString(), "world"); + + EXPECT_EQ(0UL, config_->stats().initial_header_not_found_.value()); + EXPECT_EQ(0UL, config_->stats().header_not_found_.value()); + EXPECT_EQ(1UL, config_->stats().alpn_protocol_found_.value()); +} + +TEST_F(MetadataExchangeFilterTest, MetadataExchangeNotFound) { + initialize(); + + EXPECT_CALL(read_filter_callbacks_.connection_, nextProtocol()) + .WillRepeatedly(Return("istio")); + + ::Envoy::Buffer::OwnedImpl data{}; + EXPECT_EQ(Envoy::Network::FilterStatus::Continue, + filter_->onData(data, false)); + EXPECT_EQ(1UL, config_->stats().alpn_protocol_not_found_.value()); +} + +} // namespace MetadataExchange +} // namespace Tcp +} // namespace Envoy diff --git a/test/envoye2e/basic_tcp_flow/basic_tcp_flow_test.go b/test/envoye2e/basic_tcp_flow/basic_tcp_flow_test.go index e54fff7d9b..da14f5c180 100644 --- a/test/envoye2e/basic_tcp_flow/basic_tcp_flow_test.go +++ b/test/envoye2e/basic_tcp_flow/basic_tcp_flow_test.go @@ -27,15 +27,13 @@ import ( // Stats in Client Envoy proxy. var expectedClientStats = map[string]int{ - // http listener stats - "tcp.inbound_tcp.downstream_cx_total": 1, - "tcp.outbound_tcp.downstream_cx_total": 1, + // tcp listener stats + "tcp.inbound_tcp.downstream_cx_total": 1, } // Stats in Server Envoy proxy. var expectedServerStats = map[string]int{ - // http listener stats - "tcp.inbound_tcp.downstream_cx_total": 1, + // tcp listener stats "tcp.outbound_tcp.downstream_cx_total": 1, } diff --git a/test/envoye2e/env/ports.go b/test/envoye2e/env/ports.go index 2eef6d29d9..ea8c731a74 100644 --- a/test/envoye2e/env/ports.go +++ b/test/envoye2e/env/ports.go @@ -30,6 +30,8 @@ const ( StackdriverPluginTest uint16 = iota + TcpMetadataExchangeTest uint16 = iota + // The number of total tests. has to be the last one. maxTestNum ) diff --git a/test/envoye2e/env/setup.go b/test/envoye2e/env/setup.go index 0dc06494d4..f1c9bcaf7b 100644 --- a/test/envoye2e/env/setup.go +++ b/test/envoye2e/env/setup.go @@ -96,6 +96,21 @@ type TestSetup struct { // Client side Envoy node metadata. ClientNodeMetadata string + + // Whether Tls is Enabled or not. + EnableTls bool + + // Format for accesslog + AccesslogFormat string + + // TlsContext to be used. + TlsContext string + + // ClusterTlsContext to be used. + ClusterTlsContext string + + // UpstreamFilters chain in client. + UpstreamFiltersInClient string } func NewClientServerEnvoyTestSetup(name uint16, t *testing.T) *TestSetup { @@ -149,6 +164,21 @@ func (s *TestSetup) SetFiltersBeforeEnvoyRouterInAppToClient(filters string) { s.FiltersBeforeEnvoyRouterInAppToClient = filters } +// SetEnableTls sets EnableTls. +func (s *TestSetup) SetEnableTls(enableTls bool) { + s.EnableTls = enableTls +} + +// SetTlsContext sets TLS COntext. +func (s *TestSetup) SetTlsContext(tlsContext string) { + s.TlsContext = tlsContext +} + +// SetTlsContext sets TLS COntext. +func (s *TestSetup) SetClusterTlsContext(clusterTlsContext string) { + s.ClusterTlsContext = clusterTlsContext +} + // SetFiltersBeforeEnvoyRouterInClientToProxy sets the configurations of the filters that come before envoy.router http // filter in ClientToProxy listener. func (s *TestSetup) SetFiltersBeforeEnvoyRouterInClientToProxy(filters string) { @@ -177,6 +207,16 @@ func (s *TestSetup) SetClientNodeMetadata(metadata string) { s.ClientNodeMetadata = metadata } +// SetAccessLogFormat sets the accesslogformat. +func (s *TestSetup) SetAccessLogFormat(accesslogformat string) { + s.AccesslogFormat = accesslogformat +} + +// SetUpstreamFiltersInClient sets upstream filters chain in client envoy.. +func (s *TestSetup) SetUpstreamFiltersInClient(upstreamFiltersInClient string) { + s.UpstreamFiltersInClient = upstreamFiltersInClient +} + func (s *TestSetup) SetUpClientServerEnvoy() error { var err error @@ -218,7 +258,7 @@ func (s *TestSetup) SetUpClientServerEnvoy() error { } } if s.startTcpBackend { - s.tcpBackend, err = NewTCPServer(s.ports.BackendPort, "hello") + s.tcpBackend, err = NewTCPServer(s.ports.BackendPort, "hello", s.EnableTls) if err != nil { log.Printf("unable to create TCP server %v", err) } else { @@ -250,7 +290,7 @@ func (s *TestSetup) TearDownClientServerEnvoy() { s.backend.Stop() } if s.tcpBackend != nil { - s.tcpBackend.Start() + s.tcpBackend.Stop() } } diff --git a/test/envoye2e/env/tcp_envoy_conf.go b/test/envoye2e/env/tcp_envoy_conf.go index e25e8f6c40..d412fa2f36 100644 --- a/test/envoye2e/env/tcp_envoy_conf.go +++ b/test/envoye2e/env/tcp_envoy_conf.go @@ -15,6 +15,11 @@ package env const tcpEnvoyClientConfTemplYAML = ` +node: + id: test + metadata: { +{{.ClientNodeMetadata | indent 4 }} + } admin: access_log_path: {{.ClientAccessLogPath}} address: @@ -35,26 +40,22 @@ static_resources: socket_address: address: 127.0.0.1 port_value: {{.Ports.ClientToServerProxyPort}} - - name: server - connect_timeout: 5s - type: STATIC - load_assignment: - cluster_name: server - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: {{.Ports.ProxyToServerProxyPort}} +{{.UpstreamFiltersInClient | indent 4 }} +{{.ClusterTlsContext | indent 4 }} listeners: - name: app-to-client address: socket_address: address: 127.0.0.1 port_value: {{.Ports.AppToClientProxyPort}} + listener_filters: + - name: "envoy.listener.tls_inspector" + typed_config: {} + - name: "envoy.listener.http_inspector" + typed_config: {} filter_chains: - filters: +{{.FiltersBeforeEnvoyRouterInAppToClient | indent 6 }} - name: envoy.tcp_proxy config: stat_prefix: inbound_tcp @@ -63,24 +64,16 @@ static_resources: - name: envoy.file_access_log config: path: {{.ClientAccessLogPath}} - - name: client-to-proxy - address: - socket_address: - address: 127.0.0.1 - port_value: {{.Ports.ClientToServerProxyPort}} - filter_chains: - - filters: - - name: envoy.tcp_proxy - config: - stat_prefix: outbound_tcp - cluster: server - access_log: - - name: envoy.file_access_log - config: - path: {{.ClientAccessLogPath}} +{{.AccesslogFormat | indent 14 }} +{{.TlsContext | indent 6 }} ` const tcpEnvoyServerConfTemplYAML = ` +node: + id: test + metadata: { +{{.ServerNodeMetadata | indent 4 }} + } admin: access_log_path: {{.ServerAccessLogPath}} address: @@ -101,41 +94,21 @@ static_resources: socket_address: address: 127.0.0.1 port_value: {{.Ports.BackendPort}} - - name: server - connect_timeout: 5s - type: STATIC - load_assignment: - cluster_name: server - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: {{.Ports.ClientToAppProxyPort}} +{{.ClusterTlsContext | indent 4 }} listeners: - - name: proxy-to-server - address: - socket_address: - address: 127.0.0.1 - port_value: {{.Ports.ProxyToServerProxyPort}} - filter_chains: - - filters: - - name: envoy.tcp_proxy - config: - stat_prefix: inbound_tcp - cluster: server - access_log: - - name: envoy.file_access_log - config: - path: {{.ServerAccessLogPath}} - - name: client-to-app + - name: server address: socket_address: address: 127.0.0.1 - port_value: {{.Ports.ClientToAppProxyPort}} + port_value: {{.Ports.ClientToServerProxyPort}} + listener_filters: + - name: "envoy.listener.tls_inspector" + typed_config: {} + - name: "envoy.listener.http_inspector" + typed_config: {} filter_chains: - filters: +{{.FiltersBeforeEnvoyRouterInClientToApp | indent 6 }} - name: envoy.tcp_proxy config: stat_prefix: outbound_tcp @@ -144,6 +117,7 @@ static_resources: - name: envoy.file_access_log config: path: {{.ServerAccessLogPath}} +{{.TlsContext | indent 6 }} ` func GetTcpClientEnvoyConfTmp() string { diff --git a/test/envoye2e/env/tcp_server.go b/test/envoye2e/env/tcp_server.go index 53ba2a0b54..217c0accf0 100644 --- a/test/envoye2e/env/tcp_server.go +++ b/test/envoye2e/env/tcp_server.go @@ -16,8 +16,11 @@ package env import ( "bufio" + "crypto/tls" + "crypto/x509" "fmt" "io" + "io/ioutil" "log" "net" "time" @@ -25,23 +28,54 @@ import ( // TCPServer stores data for a TCP server. type TCPServer struct { - port uint16 - lis net.Listener - prefix string + port uint16 + lis net.Listener + prefix string + enableTLS bool } // NewTCPServer creates a new TCP server. -func NewTCPServer(port uint16, prefix string) (*TCPServer, error) { +func NewTCPServer(port uint16, prefix string, enableTLS bool) (*TCPServer, error) { log.Printf("Tcp server listening on port %v\n", port) - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - log.Fatal(err) - return nil, err + var lis net.Listener + if enableTLS { + certificate, err := tls.LoadX509KeyPair("cert-chain.pem", "key.pem") + if err != nil { + return nil, err + } + caCert, err := ioutil.ReadFile("root-cert.pem") + if err != nil { + return nil, err + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + config := &tls.Config{ + Certificates: []tls.Certificate{certificate}, + NextProtos: []string{"istio2"}, + ClientAuth: tls.RequestClientCert, + ClientCAs: caCertPool, + ServerName: "localhost", + } + lis, err = tls.Listen("tcp", fmt.Sprintf(":%d", port), config) + if err != nil { + log.Fatal(err) + return nil, err + } + } else { + var err error + lis, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + log.Fatal(err) + return nil, err + } } + return &TCPServer{ - port: port, - lis: lis, - prefix: prefix, + port: port, + lis: lis, + prefix: prefix, + enableTLS: enableTLS, }, nil } @@ -68,9 +102,29 @@ func handleConnection(conn net.Conn, prefix string) { } // WaitForTCPServer waits for a TCP server -func WaitForTCPServer(port uint16) error { +func WaitForTCPServer(port uint16, enableTLS bool) error { + var config *tls.Config + + if enableTLS { + certPool := x509.NewCertPool() + bs, err := ioutil.ReadFile("cert-chain.pem") + if err != nil { + return fmt.Errorf("failed to read client ca cert: %s", err) + } + ok := certPool.AppendCertsFromPEM(bs) + if !ok { + return fmt.Errorf("failed to append client certs") + } + config = &tls.Config{RootCAs: certPool, NextProtos: []string{"istio2"}, ServerName: "localhost"} + } for i := 0; i < maxAttempts; i++ { - conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + var conn net.Conn + var err error + if enableTLS { + conn, err = tls.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port), config) + } else { + conn, err = net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + } if err != nil { log.Println("Will wait 200ms and try again.") time.Sleep(200 * time.Millisecond) @@ -91,6 +145,7 @@ func WaitForTCPServer(port uint16) error { return fmt.Errorf("timeout waiting for server startup") } +// Serve tcp requests func Serve(l net.Listener, prefix string) error { for { conn, err := l.Accept() @@ -110,7 +165,7 @@ func (s *TCPServer) Start() <-chan error { errCh <- Serve(s.lis, s.prefix) }() go func() { - errCh <- WaitForTCPServer(s.port) + errCh <- WaitForTCPServer(s.port, s.enableTLS) }() return errCh diff --git a/test/envoye2e/stackdriver_plugin/stackdriver_plugin_test.go b/test/envoye2e/stackdriver_plugin/stackdriver_plugin_test.go index 67b2513410..f52f5d401c 100644 --- a/test/envoye2e/stackdriver_plugin/stackdriver_plugin_test.go +++ b/test/envoye2e/stackdriver_plugin/stackdriver_plugin_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/golang/protobuf/jsonpb" + "istio.io/proxy/test/envoye2e/env" fs "istio.io/proxy/test/envoye2e/stackdriver_plugin/fake_stackdriver" diff --git a/test/envoye2e/tcp_metadata_exchange/cert-chain.pem b/test/envoye2e/tcp_metadata_exchange/cert-chain.pem new file mode 100644 index 0000000000..cb825644a3 --- /dev/null +++ b/test/envoye2e/tcp_metadata_exchange/cert-chain.pem @@ -0,0 +1,60 @@ +-----BEGIN CERTIFICATE----- +MIIFMTCCAxmgAwIBAgIDAxaCMA0GCSqGSIb3DQEBCwUAMCIxDjAMBgNVBAoMBUlz +dGlvMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDcyMjIxMzAzMloXDTIxMDcyMTIx +MzAzMlowNzEOMAwGA1UECgwFSXN0aW8xGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBD +QTELMAkGA1UEBwwCY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4 +rEngnty6lVXmyFqC5DsLpoDXWPaOXr4bmKYHL4PeL5vX/OOn+kbHhm4JjYOxKTmO +YtdLttsQZT7jUd+3WercyVIesWULIO33VbEtNvqKE408J0+5W266+Y+dSmVbrbOf +a6nKP6gpVf1r7Rf0NeS4S3XnUQ0igWo/Pbqn3S2C+ewkR66sCAB5vopKLzdABIN1 +7oLXil2mY4cotk4QPDRgk+AHh+uw1w6JC2c3FcNi3MLh7DIVsLyX//3BWX2bs4jR +FKva6w1KX2nohECj5FqCd7JuFdqtQO+XW5Ihhag3Hzq9VrqDgR2h0XACLqRNJQG4 +0yzP0b0SvOdpOj6JE33IxOBcLGTvrteBadA0sMzWoCfqYeFLOBhFUGSDamHqd0Or +qIAza/dE3Pb3VX0OZzW601PqnWXr4YDIKIdb3tgc97j/zbYvcjp40MQfgik6S/lZ +v8E5ZHHc1Je0zGojL8mAjoklCET1HyP/aRSMIRekdYuCjPqjVyrGeeS3R/Fatigm +gicVYvFDT7iGauyHPA7894CavHVaA40q20Y78bDJSVgsiznNGN7n2oenBZ7P8kbk +Y2pbNnqhn67v5Na1uSHVGMjB+kbVn0WZbbSawKp0W30TCtnuaBdfI1QjOWYdkIEs +pvtdI31V3cLJO9vzegwhcdYS7YG95m6VrdMQbaBE3wIDAQABo1swWTAdBgNVHQ4E +FgQUuTgg1nLlC0d35VPxZh1T6NqkDg8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV +HQ8BAf8EBAMCAuQwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUA +A4ICAQBV0mZPDPDOna692/cRVP2qHoHzEsYLttTioRCmQT8ideW8tpW7IwWozpKr +BlcaCXUc1K8hoMFSgYCcuh+VMH8qNCQHDEcWoPHPBFrr83ALRVdh4cYeMa7ZcIRS +l08Fa5TbVQXDkkj+t0KFr6VIBzXvVw8W/r8bgy4LSu/33WGQg4fRecp9mm0j/P8Y +DaWalN1m8TeRZtN1k7ltHmkeOPH+3NlgZ4YvlZ+ltPMrXowdP+/nCZgeR1BzFmer +0EVZ0Hq35EvXrmrrN5X4cc3b9OmaQpPQxqSlA/8hwyd0ItLZCYv1v4CB+0AI6CvY +P2RtxJ87UCz9wlthIlV2a8/d0NItV08HATfK5nXjuY8Ndm3V+jgEGGivizEaSeso +grBKJ/TbyoUpsfji5Fc2ogzrGkon1EFgR/WJ8FVlty2YVnjTfjVxD8OJ8Znjm1MH +YbisHAdTqTND0Fa2F7GFxtltD0DxQ2zsH3D8W98dxeRRigYCifixqFtk72iE702o +4K3CfPhi7MN4dxbQNFXtjrjnIQn9lN+ih+E1RK0Z4LTrd4WwsJF1MHBm6MRIFu4t +xaJb3fB5Artwn6DJ1vhfLoONDfwbrRL9/QDt0fFKtnCMbHcApsGJmrXskGim8Kma +Cw3FWjtdhpzmgK5L0SVell2IK3gEF3rphETn37YFDCttOUzpCg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL +BQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy +MjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE +AwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH +/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA +BGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW +AYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX +tQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7 +CXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves +G4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S +Ew1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM +fLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP +y5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz +AobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8 +Rm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ +h3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y +2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3 +LuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT +IzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL +Z8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww +oImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8 +dvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G +k5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh +saFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V +6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM +SHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy +Uff0OxD2hzk= +-----END CERTIFICATE----- diff --git a/test/envoye2e/tcp_metadata_exchange/doc.go b/test/envoye2e/tcp_metadata_exchange/doc.go new file mode 100644 index 0000000000..35e5077484 --- /dev/null +++ b/test/envoye2e/tcp_metadata_exchange/doc.go @@ -0,0 +1,16 @@ +// Copyright 2019 Istio Authors +// +// 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. + +// Package tcp_metadata_exchange contains an integration test for metadata exchange in envoy proxy. +package tcp_metadata_exchange diff --git a/test/envoye2e/tcp_metadata_exchange/key.pem b/test/envoye2e/tcp_metadata_exchange/key.pem new file mode 100644 index 0000000000..21e580d356 --- /dev/null +++ b/test/envoye2e/tcp_metadata_exchange/key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/envoye2e/tcp_metadata_exchange/root-cert.pem b/test/envoye2e/tcp_metadata_exchange/root-cert.pem new file mode 100644 index 0000000000..261912d6ea --- /dev/null +++ b/test/envoye2e/tcp_metadata_exchange/root-cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL +BQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy +MjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE +AwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH +/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA +BGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW +AYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX +tQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7 +CXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves +G4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S +Ew1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM +fLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP +y5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz +AobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8 +Rm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ +h3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y +2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3 +LuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT +IzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL +Z8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww +oImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8 +dvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G +k5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh +saFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V +6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM +SHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy +Uff0OxD2hzk= +-----END CERTIFICATE----- diff --git a/test/envoye2e/tcp_metadata_exchange/tcp_metadata_exchange_test.go b/test/envoye2e/tcp_metadata_exchange/tcp_metadata_exchange_test.go new file mode 100644 index 0000000000..47f57efc22 --- /dev/null +++ b/test/envoye2e/tcp_metadata_exchange/tcp_metadata_exchange_test.go @@ -0,0 +1,181 @@ +// Copyright 2019 Istio Authors +// +// 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. + +package tcp_metadata_exchange_test + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + + "testing" + "text/template" + + "istio.io/proxy/test/envoye2e/env" +) + +const metadataExchangeIstioConfigFilter = ` +- name: envoy.filters.network.metadata_exchange + config: + protocol: istio2 + node_metadata_id: istio.io/metadata +` + +const metadataExchangeIstioUpstreamConfigFilterChain = ` +filters: +- name: envoy.filters.network.upstream.metadata_exchange + typed_config: + "@type": type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange + protocol: istio2 + node_metadata_id: istio.io/metadata +` + +const tlsContext = ` +tls_context: + common_tls_context: + alpn_protocols: + - istio2 + tls_certificates: + - certificate_chain: + inline_string: "-----BEGIN CERTIFICATE-----\nMIIFMTCCAxmgAwIBAgIDAxaCMA0GCSqGSIb3DQEBCwUAMCIxDjAMBgNVBAoMBUlz\ndGlvMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDcyMjIxMzAzMloXDTIxMDcyMTIx\nMzAzMlowNzEOMAwGA1UECgwFSXN0aW8xGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBD\nQTELMAkGA1UEBwwCY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4\nrEngnty6lVXmyFqC5DsLpoDXWPaOXr4bmKYHL4PeL5vX/OOn+kbHhm4JjYOxKTmO\nYtdLttsQZT7jUd+3WercyVIesWULIO33VbEtNvqKE408J0+5W266+Y+dSmVbrbOf\na6nKP6gpVf1r7Rf0NeS4S3XnUQ0igWo/Pbqn3S2C+ewkR66sCAB5vopKLzdABIN1\n7oLXil2mY4cotk4QPDRgk+AHh+uw1w6JC2c3FcNi3MLh7DIVsLyX//3BWX2bs4jR\nFKva6w1KX2nohECj5FqCd7JuFdqtQO+XW5Ihhag3Hzq9VrqDgR2h0XACLqRNJQG4\n0yzP0b0SvOdpOj6JE33IxOBcLGTvrteBadA0sMzWoCfqYeFLOBhFUGSDamHqd0Or\nqIAza/dE3Pb3VX0OZzW601PqnWXr4YDIKIdb3tgc97j/zbYvcjp40MQfgik6S/lZ\nv8E5ZHHc1Je0zGojL8mAjoklCET1HyP/aRSMIRekdYuCjPqjVyrGeeS3R/Fatigm\ngicVYvFDT7iGauyHPA7894CavHVaA40q20Y78bDJSVgsiznNGN7n2oenBZ7P8kbk\nY2pbNnqhn67v5Na1uSHVGMjB+kbVn0WZbbSawKp0W30TCtnuaBdfI1QjOWYdkIEs\npvtdI31V3cLJO9vzegwhcdYS7YG95m6VrdMQbaBE3wIDAQABo1swWTAdBgNVHQ4E\nFgQUuTgg1nLlC0d35VPxZh1T6NqkDg8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV\nHQ8BAf8EBAMCAuQwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUA\nA4ICAQBV0mZPDPDOna692/cRVP2qHoHzEsYLttTioRCmQT8ideW8tpW7IwWozpKr\nBlcaCXUc1K8hoMFSgYCcuh+VMH8qNCQHDEcWoPHPBFrr83ALRVdh4cYeMa7ZcIRS\nl08Fa5TbVQXDkkj+t0KFr6VIBzXvVw8W/r8bgy4LSu/33WGQg4fRecp9mm0j/P8Y\nDaWalN1m8TeRZtN1k7ltHmkeOPH+3NlgZ4YvlZ+ltPMrXowdP+/nCZgeR1BzFmer\n0EVZ0Hq35EvXrmrrN5X4cc3b9OmaQpPQxqSlA/8hwyd0ItLZCYv1v4CB+0AI6CvY\nP2RtxJ87UCz9wlthIlV2a8/d0NItV08HATfK5nXjuY8Ndm3V+jgEGGivizEaSeso\ngrBKJ/TbyoUpsfji5Fc2ogzrGkon1EFgR/WJ8FVlty2YVnjTfjVxD8OJ8Znjm1MH\nYbisHAdTqTND0Fa2F7GFxtltD0DxQ2zsH3D8W98dxeRRigYCifixqFtk72iE702o\n4K3CfPhi7MN4dxbQNFXtjrjnIQn9lN+ih+E1RK0Z4LTrd4WwsJF1MHBm6MRIFu4t\nxaJb3fB5Artwn6DJ1vhfLoONDfwbrRL9/QDt0fFKtnCMbHcApsGJmrXskGim8Kma\nCw3FWjtdhpzmgK5L0SVell2IK3gEF3rphETn37YFDCttOUzpCg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL\nBQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy\nMjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE\nAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH\n/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA\nBGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW\nAYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX\ntQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7\nCXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves\nG4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S\nEw1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM\nfLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP\ny5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz\nAobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8\nRm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ\nh3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\nAwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y\n2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3\nLuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT\nIzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL\nZ8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww\noImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8\ndvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G\nk5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh\nsaFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V\n6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM\nSHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy\nUff0OxD2hzk=\n-----END CERTIFICATE-----\n" + private_key: + inline_string: "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n" + validation_context: + trusted_ca: + inline_string: "-----BEGIN CERTIFICATE-----\nMIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL\nBQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy\nMjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE\nAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH\n/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA\nBGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW\nAYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX\ntQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7\nCXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves\nG4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S\nEw1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM\nfLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP\ny5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz\nAobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8\nRm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ\nh3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\nAwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y\n2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3\nLuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT\nIzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL\nZ8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww\noImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8\ndvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G\nk5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh\nsaFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V\n6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM\nSHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy\nUff0OxD2hzk=\n-----END CERTIFICATE-----\n" + require_client_certificate: true +` + +const clusterTlsContext = ` +tls_context: + common_tls_context: + alpn_protocols: + - istio2 + tls_certificates: + - certificate_chain: + inline_string: "-----BEGIN CERTIFICATE-----\nMIIFMTCCAxmgAwIBAgIDAxaCMA0GCSqGSIb3DQEBCwUAMCIxDjAMBgNVBAoMBUlz\ndGlvMRAwDgYDVQQDDAdSb290IENBMB4XDTE5MDcyMjIxMzAzMloXDTIxMDcyMTIx\nMzAzMlowNzEOMAwGA1UECgwFSXN0aW8xGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBD\nQTELMAkGA1UEBwwCY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4\nrEngnty6lVXmyFqC5DsLpoDXWPaOXr4bmKYHL4PeL5vX/OOn+kbHhm4JjYOxKTmO\nYtdLttsQZT7jUd+3WercyVIesWULIO33VbEtNvqKE408J0+5W266+Y+dSmVbrbOf\na6nKP6gpVf1r7Rf0NeS4S3XnUQ0igWo/Pbqn3S2C+ewkR66sCAB5vopKLzdABIN1\n7oLXil2mY4cotk4QPDRgk+AHh+uw1w6JC2c3FcNi3MLh7DIVsLyX//3BWX2bs4jR\nFKva6w1KX2nohECj5FqCd7JuFdqtQO+XW5Ihhag3Hzq9VrqDgR2h0XACLqRNJQG4\n0yzP0b0SvOdpOj6JE33IxOBcLGTvrteBadA0sMzWoCfqYeFLOBhFUGSDamHqd0Or\nqIAza/dE3Pb3VX0OZzW601PqnWXr4YDIKIdb3tgc97j/zbYvcjp40MQfgik6S/lZ\nv8E5ZHHc1Je0zGojL8mAjoklCET1HyP/aRSMIRekdYuCjPqjVyrGeeS3R/Fatigm\ngicVYvFDT7iGauyHPA7894CavHVaA40q20Y78bDJSVgsiznNGN7n2oenBZ7P8kbk\nY2pbNnqhn67v5Na1uSHVGMjB+kbVn0WZbbSawKp0W30TCtnuaBdfI1QjOWYdkIEs\npvtdI31V3cLJO9vzegwhcdYS7YG95m6VrdMQbaBE3wIDAQABo1swWTAdBgNVHQ4E\nFgQUuTgg1nLlC0d35VPxZh1T6NqkDg8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV\nHQ8BAf8EBAMCAuQwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUA\nA4ICAQBV0mZPDPDOna692/cRVP2qHoHzEsYLttTioRCmQT8ideW8tpW7IwWozpKr\nBlcaCXUc1K8hoMFSgYCcuh+VMH8qNCQHDEcWoPHPBFrr83ALRVdh4cYeMa7ZcIRS\nl08Fa5TbVQXDkkj+t0KFr6VIBzXvVw8W/r8bgy4LSu/33WGQg4fRecp9mm0j/P8Y\nDaWalN1m8TeRZtN1k7ltHmkeOPH+3NlgZ4YvlZ+ltPMrXowdP+/nCZgeR1BzFmer\n0EVZ0Hq35EvXrmrrN5X4cc3b9OmaQpPQxqSlA/8hwyd0ItLZCYv1v4CB+0AI6CvY\nP2RtxJ87UCz9wlthIlV2a8/d0NItV08HATfK5nXjuY8Ndm3V+jgEGGivizEaSeso\ngrBKJ/TbyoUpsfji5Fc2ogzrGkon1EFgR/WJ8FVlty2YVnjTfjVxD8OJ8Znjm1MH\nYbisHAdTqTND0Fa2F7GFxtltD0DxQ2zsH3D8W98dxeRRigYCifixqFtk72iE702o\n4K3CfPhi7MN4dxbQNFXtjrjnIQn9lN+ih+E1RK0Z4LTrd4WwsJF1MHBm6MRIFu4t\nxaJb3fB5Artwn6DJ1vhfLoONDfwbrRL9/QDt0fFKtnCMbHcApsGJmrXskGim8Kma\nCw3FWjtdhpzmgK5L0SVell2IK3gEF3rphETn37YFDCttOUzpCg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL\nBQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy\nMjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE\nAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH\n/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA\nBGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW\nAYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX\ntQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7\nCXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves\nG4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S\nEw1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM\nfLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP\ny5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz\nAobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8\nRm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ\nh3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\nAwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y\n2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3\nLuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT\nIzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL\nZ8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww\noImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8\ndvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G\nk5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh\nsaFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V\n6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM\nSHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy\nUff0OxD2hzk=\n-----END CERTIFICATE-----\n" + private_key: + inline_string: "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n" + validation_context: + trusted_ca: + inline_string: "-----BEGIN CERTIFICATE-----\nMIIFFDCCAvygAwIBAgIUZqU0Sviq/wULK6UV7PoAZ7B+nqAwDQYJKoZIhvcNAQEL\nBQAwIjEOMAwGA1UECgwFSXN0aW8xEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTkwNzIy\nMjEzMDA0WhcNMjkwNzE5MjEzMDA0WjAiMQ4wDAYDVQQKDAVJc3RpbzEQMA4GA1UE\nAwwHUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANNl5/pH\n/ktdqEsb83cqHrYJCyzbvWce6k/iud4Czu6FClFX8b+n/Rv9GrZFxJwKAFlUx3iA\nBGlSn/1XYpnhudQhgVGvyuWNO5kX4BfrAJwfWt+7Mn6NcWvunDqwqUPxI07sgCJW\nAYBAwkZH/Nhn6tj571XWNPziUtCwlPNkFMiRu/2nI/tq12IgwimFjVgiCuprNfyX\ntQz/DMVTWpCRQLK5ptlYMfk0P25UKyJdKHnr1MPQBJmPXMfSSqpGjksikV4QnYc7\nCXB3ucq7ty0IWA8QXH+86WqMTh22mosWVXHe0OGbzYtuyVnXc1G7YRv4D87G3Ves\nG4n/8e+RaDTacvwOsYEkuQGk+s8pggPkIqydGy02JNZ4cSRpXJRTzME2BgBZxT8S\nEw1Omr5+iuLNRAKEYRM/eWI7qrs5fxpD6K9JELHS41hWHGdW94PP0wKz70trx5pM\nfLpcVm7BQ5ppgf+t4vgKnrNiACQpfyZbInCBU0doaZaqVMnKH0vgyM7xrC43fsOP\ny5URy3tEH8Uk7Dbvsmj7AXR7IPKlYtgcqcJXmeWa+kLOpx3G55hgJL1ySrxXg/qz\nAobgmV0IycH2ntn5lXvjbwe0cfXAnZgGoALZjJVuEazyBmmVzjBjG2Qcq35nHfp8\nRm6WnCZIaGsZqgoDuSJD280ZLWW7R0PMcnypAgMBAAGjQjBAMB0GA1UdDgQWBBQZ\nh3/ckcK23ZYKO+JsZowd3dIobDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\nAwIC5DANBgkqhkiG9w0BAQsFAAOCAgEAjh4CdrwnLsqwVxyVSgxd7TfSHtKE/J2Y\n2IZ4fJYXGkq3McPk2e9u0zjCH0buvfDwyAItLIacD+YwIP+OC2WxLe+YMZ5KkXl3\nLuhQ2TOoRlrbp5tYLQITZIIl9+vNkgnn1DkdxkLm9cDDag19LSxa9Rjrnb3wwFAT\nIzEhy+d18FpQtdMMhmonU/L8Oy5LqjT5BR3T8VrXYUsaAkcUs/yHNTFAY3iJFBWL\nZ8dFa5v0A1Ryi8quSNo7lK/hSEZvvV9k4XfFAolXSUqe8BCuXe0rbAq3Jq9HgDww\noImGM0uz4Zf89uhTk1O7UOUfQoSTmA0yZICtQkCiOC0J4AlAOTmiEXUC9gicV3R8\ndvVOqNBOcBELglZ+NIMm6FQQqPh1nZ6A3Bh+JRTPerAF12725RZZE6XMxq2MSr3G\nk5yH10QPMH7/DJRQUhRHAhbge+jk2csa7EGSxABcbsPLSV+cEzXRO4cJeItoZQLh\nsaFhIn9lGukXG6lgiperOqZl6DFVcUG6/nogK7KOTAnV9zjR/7vNwvYzPI9iOR3V\n6dbG38KnipcfL885VLJVTnfhvYHlxFklCKTEnOHnmKsM0qjQuky3DBzmDA6iqeOM\nSHRje5LKxi7mllJfu/X0MxYJWiu6i4gMCWZsC3UtAJQ09x7iwcNr/1bl9ApGszOy\nUff0OxD2hzk=\n-----END CERTIFICATE-----\n" +` + +const clientNodeMetadata = ` +"istio.io/metadata": { + namespace: default, + labels: { app: productpage }, + } +` + +const serverNodeMetadata = ` +"istio.io/metadata": { + namespace: default, + labels: { app: details }, + } +` + +// Stats in Client Envoy proxy. +var expectedClientStats = map[string]int{ + "cluster.client.metadata_exchange.alpn_protocol_found": 1, + "cluster.client.metadata_exchange.alpn_protocol_not_found": 0, + "cluster.client.metadata_exchange.initial_header_not_found": 0, + "cluster.client.metadata_exchange.header_not_found": 0, + "cluster.client.metadata_exchange.metadata_added": 1, +} + +// Stats in Server Envoy proxy. +var expectedServerStats = map[string]int{ + "metadata_exchange.alpn_protocol_found": 1, + "metadata_exchange.alpn_protocol_not_found": 0, + "metadata_exchange.initial_header_not_found": 0, + "metadata_exchange.header_not_found": 0, + "metadata_exchange.metadata_added": 1, +} + +func TestTcpMetadataExchange(t *testing.T) { + s := env.NewClientServerEnvoyTestSetup(env.TcpMetadataExchangeTest, t) + s.SetNoBackend(true) + s.SetStartTcpBackend(true) + s.SetTlsContext(tlsContext) + s.SetClusterTlsContext(clusterTlsContext) + s.SetFiltersBeforeEnvoyRouterInClientToApp(metadataExchangeIstioConfigFilter) + s.SetUpstreamFiltersInClient(metadataExchangeIstioUpstreamConfigFilterChain) + s.SetEnableTls(true) + s.SetClientNodeMetadata(clientNodeMetadata) + s.SetServerNodeMetadata(serverNodeMetadata) + s.ClientEnvoyTemplate = env.GetTcpClientEnvoyConfTmp() + s.ServerEnvoyTemplate = env.GetTcpServerEnvoyConfTmp() + if err := s.SetUpClientServerEnvoy(); err != nil { + t.Fatalf("Failed to setup te1 st: %v", err) + } + defer s.TearDownClientServerEnvoy() + + certPool := x509.NewCertPool() + bs, err := ioutil.ReadFile("cert-chain.pem") + if err != nil { + t.Fatalf("failed to read client ca cert: %s", err) + } + ok := certPool.AppendCertsFromPEM(bs) + if !ok { + t.Fatal("failed to append client certs") + } + + certificate, err := tls.LoadX509KeyPair("cert-chain.pem", "key.pem") + if err != nil { + t.Fatal("failed to get certificate") + } + config := &tls.Config{Certificates: []tls.Certificate{certificate}, ServerName: "localhost", NextProtos: []string{"istio2"}, RootCAs: certPool} + + conn, err := tls.Dial("tcp", fmt.Sprintf("localhost:%d", s.Ports().AppToClientProxyPort /*s.Ports().ProxyToServerProxyPort*/), config) + if err != nil { + t.Fatal(err) + } + + conn.Write([]byte("world \n")) + reply := make([]byte, 256) + n, err := conn.Read(reply) + if err != nil { + t.Fatal(err) + } + + if fmt.Sprintf("%s", reply[:n]) != "hello world \n" { + t.Fatalf("Verification Failed. Expected: hello world. Got: %v", fmt.Sprintf("%s", reply[:n])) + } + + _ = conn.Close() + s.VerifyStats(getParsedExpectedStats(expectedClientStats, t, s), s.Ports().ClientAdminPort) + s.VerifyStats(getParsedExpectedStats(expectedServerStats, t, s), s.Ports().ServerAdminPort) +} + +func getParsedExpectedStats(expectedStats map[string]int, t *testing.T, s *env.TestSetup) map[string]int { + parsedExpectedStats := make(map[string]int) + for key, value := range expectedStats { + tmpl, err := template.New("parse_state").Parse(key) + if err != nil { + t.Errorf("failed to parse config template: %v", err) + } + + var tpl bytes.Buffer + err = tmpl.Execute(&tpl, s) + if err != nil { + t.Errorf("failed to execute config template: %v", err) + } + parsedExpectedStats[tpl.String()] = value + } + + return parsedExpectedStats +}