diff --git a/src/envoy/BUILD b/src/envoy/BUILD index 61b354a13df..9fbe1c5229a 100644 --- a/src/envoy/BUILD +++ b/src/envoy/BUILD @@ -30,6 +30,7 @@ envoy_cc_binary( "//src/envoy/http/mixer:filter_lib", "//src/envoy/tcp/mixer:filter_lib", "//src/envoy/tcp/tcp_cluster_rewrite:config_lib", + "//src/envoy/tcp/forward_downstream_sni:config_lib", "@envoy//source/exe:envoy_main_entry_lib", ], ) diff --git a/src/envoy/tcp/forward_downstream_sni/BUILD b/src/envoy/tcp/forward_downstream_sni/BUILD new file mode 100644 index 00000000000..f2bcacbc0dc --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/BUILD @@ -0,0 +1,56 @@ +# Copyright 2018 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. +# +################################################################################ +# + +load( + "@envoy//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_cc_test", +) + +envoy_cc_library( + name = "config_lib", + srcs = ["config.cc"], + hdrs = ["config.h"], + repository = "@envoy", + visibility = ["//visibility:public"], + deps = [ + ":forward_downstream_sni_lib", + "@envoy//source/exe:envoy_common_lib", + ], +) +envoy_cc_library( + name = "forward_downstream_sni_lib", + srcs = ["forward_downstream_sni.cc"], + hdrs = ["forward_downstream_sni.h"], + repository = "@envoy", + deps = [ + "@envoy//source/exe:envoy_common_lib", + ], +) + +envoy_cc_test( + name = "forward_downstream_sni_test", + srcs = ["forward_downstream_sni_test.cc"], + repository = "@envoy", + deps = [ + ":forward_downstream_sni_lib", + ":config_lib", + "@envoy//test/mocks/network:network_mocks", + "@envoy//test/mocks/server:server_mocks", + "@envoy//test/mocks/stream_info:stream_info_mocks", + ], +) diff --git a/src/envoy/tcp/forward_downstream_sni/config.cc b/src/envoy/tcp/forward_downstream_sni/config.cc new file mode 100644 index 00000000000..6b62e1a3a35 --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/config.cc @@ -0,0 +1,59 @@ +/* Copyright 2018 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/forward_downstream_sni/config.h" + +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h" + +namespace Envoy { +namespace Tcp { +namespace ForwardDownstreamSni { + +Network::FilterFactoryCb +ForwardDownstreamSniNetworkFilterConfigFactory::createFilterFactory( + const Json::Object&, Server::Configuration::FactoryContext&) { + // Only used in v1 filters. + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; +} + +Network::FilterFactoryCb +ForwardDownstreamSniNetworkFilterConfigFactory::createFilterFactoryFromProto( + const Protobuf::Message&, Server::Configuration::FactoryContext&) { + return [](Network::FilterManager& filter_manager) -> void { + filter_manager.addReadFilter( + std::make_shared()); + }; +} + +ProtobufTypes::MessagePtr +ForwardDownstreamSniNetworkFilterConfigFactory::createEmptyConfigProto() { + return std::make_unique(); +} + +/** + * Static registration for the forward_original_sni filter. @see + * RegisterFactory. + */ +static Registry::RegisterFactory< + ForwardDownstreamSniNetworkFilterConfigFactory, + Server::Configuration::NamedNetworkFilterConfigFactory> + registered_; + +} // namespace ForwardDownstreamSni +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/forward_downstream_sni/config.h b/src/envoy/tcp/forward_downstream_sni/config.h new file mode 100644 index 00000000000..06b6419f4ec --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/config.h @@ -0,0 +1,43 @@ +/* Copyright 2018 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 "envoy/server/filter_config.h" + +namespace Envoy { +namespace Tcp { +namespace ForwardDownstreamSni { + +/** + * Config registration for the forward_downstream_sni filter. @see + * NamedNetworkFilterConfigFactory. + */ +class ForwardDownstreamSniNetworkFilterConfigFactory + : public Server::Configuration::NamedNetworkFilterConfigFactory { + public: + // NamedNetworkFilterConfigFactory + Network::FilterFactoryCb createFilterFactory( + const Json::Object&, Server::Configuration::FactoryContext&) override; + Network::FilterFactoryCb createFilterFactoryFromProto( + const Protobuf::Message&, + Server::Configuration::FactoryContext&) override; + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + std::string name() override { return "forward_downstream_sni"; } +}; + +} // namespace ForwardDownstreamSni +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.cc b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.cc new file mode 100644 index 00000000000..bb10ffc998f --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.cc @@ -0,0 +1,41 @@ +/* Copyright 2018 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 "envoy/network/connection.h" + +#include "common/network/upstream_server_name.h" +#include "src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h" + +namespace Envoy { +namespace Tcp { +namespace ForwardDownstreamSni { + +using ::Envoy::Network::UpstreamServerName; + +Network::FilterStatus ForwardDownstreamSniFilter::onNewConnection() { + absl::string_view sni = read_callbacks_->connection().requestedServerName(); + + if (!sni.empty()) { + read_callbacks_->connection().streamInfo().filterState().setData( + UpstreamServerName::key(), std::make_unique(sni), + StreamInfo::FilterState::StateType::ReadOnly); + } + + return Network::FilterStatus::Continue; +} + +} // namespace ForwardDownstreamSni +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h new file mode 100644 index 00000000000..25132713867 --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h @@ -0,0 +1,46 @@ +/* Copyright 2018 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 "envoy/network/filter.h" + +namespace Envoy { +namespace Tcp { +namespace ForwardDownstreamSni { + +/** + * Implementation of the forward_downstream_sni filter that sets the original + * requested server name from the SNI field in the TLS connection. + */ +class ForwardDownstreamSniFilter : public Network::ReadFilter { + public: + // Network::ReadFilter + Network::FilterStatus onData(Buffer::Instance&, bool) override { + return Network::FilterStatus::Continue; + } + Network::FilterStatus onNewConnection() override; + void initializeReadFilterCallbacks( + Network::ReadFilterCallbacks& callbacks) override { + read_callbacks_ = &callbacks; + } + + private: + Network::ReadFilterCallbacks* read_callbacks_{}; +}; + +} // namespace ForwardDownstreamSni +} // namespace Tcp +} // namespace Envoy diff --git a/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni_test.cc b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni_test.cc new file mode 100644 index 00000000000..21c74160a44 --- /dev/null +++ b/src/envoy/tcp/forward_downstream_sni/forward_downstream_sni_test.cc @@ -0,0 +1,93 @@ +/* Copyright 2018 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 "test/mocks/network/mocks.h" +#include "test/mocks/server/mocks.h" +#include "test/mocks/stream_info/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "common/network/upstream_server_name.h" + +#include "src/envoy/tcp/forward_downstream_sni/config.h" +#include "src/envoy/tcp/forward_downstream_sni/forward_downstream_sni.h" + +using testing::_; +using testing::Matcher; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Tcp { +namespace ForwardDownstreamSni { + +using ::Envoy::Network::UpstreamServerName; + +// Test that a ForwardDownstreamSni filter config works. +TEST(ForwardDownstreamSni, ConfigTest) { + NiceMock context; + ForwardDownstreamSniNetworkFilterConfigFactory factory; + + Network::FilterFactoryCb cb = factory.createFilterFactoryFromProto( + *factory.createEmptyConfigProto(), context); + Network::MockConnection connection; + EXPECT_CALL(connection, addReadFilter(_)); + cb(connection); +} + +// Test that forward requested server name is set if SNI is available +TEST(ForwardDownstreamSni, SetUpstreamServerNameOnlyIfSniIsPresent) { + NiceMock filter_callbacks; + + NiceMock stream_info; + ON_CALL(filter_callbacks.connection_, streamInfo()) + .WillByDefault(ReturnRef(stream_info)); + ON_CALL(Const(filter_callbacks.connection_), streamInfo()) + .WillByDefault(ReturnRef(stream_info)); + + ForwardDownstreamSniFilter filter; + filter.initializeReadFilterCallbacks(filter_callbacks); + + // no sni + { + ON_CALL(filter_callbacks.connection_, requestedServerName()) + .WillByDefault(Return(EMPTY_STRING)); + filter.onNewConnection(); + + EXPECT_FALSE(stream_info.filterState().hasData( + UpstreamServerName::key())); + } + + // with sni + { + ON_CALL(filter_callbacks.connection_, requestedServerName()) + .WillByDefault(Return("www.example.com")); + filter.onNewConnection(); + + EXPECT_TRUE(stream_info.filterState().hasData( + UpstreamServerName::key())); + + auto forward_requested_server_name = + stream_info.filterState().getDataReadOnly( + UpstreamServerName::key()); + EXPECT_EQ(forward_requested_server_name.value(), "www.example.com"); + } +} + +} // namespace ForwardDownstreamSni +} // namespace Tcp +} // namespace Envoy