diff --git a/WORKSPACE b/WORKSPACE index 15379e2550..19eaac56fd 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -32,8 +32,8 @@ bind( # When updating envoy sha manually please update the sha in istio.deps file also # # Determine SHA256 `wget https://github.com/envoyproxy/envoy/archive/COMMIT.zip && sha256sum COMMIT.zip` -ENVOY_SHA = "cc991fe653d1918256856ed8dc2323c5f4cd7979" -ENVOY_SHA256 = "800831b406bca1bbc45a86e6700332b8055d1e429ef38b1ec8015981c1c39d17" +ENVOY_SHA = "87d1c78ac483f34e87713628beeccb58b4cfd480" +ENVOY_SHA256 = "0a450928348ef47bf6e3564c07fdce58a5e300d56088ba602bea07216a09e070" http_archive( name = "envoy", diff --git a/istio.deps b/istio.deps index a518187275..53aea145be 100644 --- a/istio.deps +++ b/istio.deps @@ -11,6 +11,6 @@ "name": "ENVOY_SHA", "repoName": "envoyproxy/envoy", "file": "WORKSPACE", - "lastStableSHA": "cc991fe653d1918256856ed8dc2323c5f4cd7979" + "lastStableSHA": "87d1c78ac483f34e87713628beeccb58b4cfd480" } ] diff --git a/src/envoy/BUILD b/src/envoy/BUILD index 61b354a13d..9fbe1c5229 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/http/jwt_auth/jwt_authenticator.cc b/src/envoy/http/jwt_auth/jwt_authenticator.cc index c47b8b6d03..b309b6b169 100644 --- a/src/envoy/http/jwt_auth/jwt_authenticator.cc +++ b/src/envoy/http/jwt_auth/jwt_authenticator.cc @@ -138,7 +138,7 @@ void JwtAuthenticator::FetchPubkey(PubkeyCacheItem* issuer) { ENVOY_LOG(debug, "fetch pubkey from [uri = {}]: start", uri_); request_ = cm_.httpAsyncClientForCluster(cluster).send( - std::move(message), *this, absl::optional()); + std::move(message), *this, Http::AsyncClient::RequestOptions()); } void JwtAuthenticator::onSuccess(MessagePtr&& response) { diff --git a/src/envoy/http/jwt_auth/jwt_authenticator_test.cc b/src/envoy/http/jwt_auth/jwt_authenticator_test.cc index fd35224e7f..b4d9b3ec26 100644 --- a/src/envoy/http/jwt_auth/jwt_authenticator_test.cc +++ b/src/envoy/http/jwt_auth/jwt_authenticator_test.cc @@ -310,18 +310,16 @@ class MockUpstream { const std::string &response_body) : request_(&mock_cm.async_client_), response_body_(response_body) { ON_CALL(mock_cm.async_client_, send_(_, _, _)) - .WillByDefault( - Invoke([this](MessagePtr &, AsyncClient::Callbacks &cb, - const absl::optional &) - -> AsyncClient::Request * { - Http::MessagePtr response_message(new ResponseMessageImpl( - HeaderMapPtr{new TestHeaderMapImpl{{":status", "200"}}})); - response_message->body().reset( - new Buffer::OwnedImpl(response_body_)); - cb.onSuccess(std::move(response_message)); - called_count_++; - return &request_; - })); + .WillByDefault(Invoke([this](MessagePtr &, AsyncClient::Callbacks &cb, + const Http::AsyncClient::RequestOptions &) + -> AsyncClient::Request * { + Http::MessagePtr response_message(new ResponseMessageImpl( + HeaderMapPtr{new TestHeaderMapImpl{{":status", "200"}}})); + response_message->body().reset(new Buffer::OwnedImpl(response_body_)); + cb.onSuccess(std::move(response_message)); + called_count_++; + return &request_; + })); } int called_count() const { return called_count_; } @@ -629,7 +627,7 @@ TEST_F(JwtAuthenticatorTest, TestPubkeyFetchFail) { AsyncClient::Callbacks *callbacks; EXPECT_CALL(async_client, send_(_, _, _)) .WillOnce(Invoke([&](MessagePtr &message, AsyncClient::Callbacks &cb, - const absl::optional &) + const Http::AsyncClient::RequestOptions &) -> AsyncClient::Request * { EXPECT_EQ((TestHeaderMapImpl{ {":method", "GET"}, @@ -665,7 +663,7 @@ TEST_F(JwtAuthenticatorTest, TestInvalidPubkey) { AsyncClient::Callbacks *callbacks; EXPECT_CALL(async_client, send_(_, _, _)) .WillOnce(Invoke([&](MessagePtr &message, AsyncClient::Callbacks &cb, - const absl::optional &) + const Http::AsyncClient::RequestOptions &) -> AsyncClient::Request * { EXPECT_EQ((TestHeaderMapImpl{ {":method", "GET"}, @@ -702,7 +700,7 @@ TEST_F(JwtAuthenticatorTest, TestOnDestroy) { AsyncClient::Callbacks *callbacks; EXPECT_CALL(async_client, send_(_, _, _)) .WillOnce(Invoke([&](MessagePtr &message, AsyncClient::Callbacks &cb, - const absl::optional &) + const Http::AsyncClient::RequestOptions &) -> AsyncClient::Request * { EXPECT_EQ((TestHeaderMapImpl{ {":method", "GET"}, diff --git a/src/envoy/tcp/forward_downstream_sni/BUILD b/src/envoy/tcp/forward_downstream_sni/BUILD new file mode 100644 index 0000000000..f2bcacbc0d --- /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 0000000000..6b62e1a3a3 --- /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 0000000000..06b6419f4e --- /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 0000000000..bb10ffc998 --- /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 0000000000..2513271386 --- /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 0000000000..21c74160a4 --- /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 diff --git a/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite.cc b/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite.cc index b189c40abd..c1306d67a5 100644 --- a/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite.cc +++ b/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite.cc @@ -43,13 +43,13 @@ Network::FilterStatus TcpClusterRewriteFilter::onNewConnection() { .streamInfo() .filterState() .hasData( - TcpProxy::PerConnectionCluster::Key)) { + TcpProxy::PerConnectionCluster::key())) { absl::string_view cluster_name = read_callbacks_->connection() .streamInfo() .filterState() .getDataReadOnly( - TcpProxy::PerConnectionCluster::Key) + TcpProxy::PerConnectionCluster::key()) .value(); ENVOY_CONN_LOG(trace, "tcp_cluster_rewrite: new connection with server name {}", @@ -66,7 +66,7 @@ Network::FilterStatus TcpClusterRewriteFilter::onNewConnection() { // The data is mutable to allow other filters to change it. read_callbacks_->connection().streamInfo().filterState().setData( - TcpProxy::PerConnectionCluster::Key, + TcpProxy::PerConnectionCluster::key(), std::make_unique(final_cluster_name), StreamInfo::FilterState::StateType::Mutable); } diff --git a/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite_test.cc b/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite_test.cc index a63ae3d937..c42aa58cc3 100644 --- a/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite_test.cc +++ b/src/envoy/tcp/tcp_cluster_rewrite/tcp_cluster_rewrite_test.cc @@ -62,7 +62,7 @@ TEST_F(TcpClusterRewriteFilterTest, ClusterRewrite) { // no rewrite { stream_info_.filterState().setData( - TcpProxy::PerConnectionCluster::Key, + TcpProxy::PerConnectionCluster::key(), std::make_unique( "hello.ns1.svc.cluster.local"), StreamInfo::FilterState::StateType::Mutable); @@ -70,12 +70,12 @@ TEST_F(TcpClusterRewriteFilterTest, ClusterRewrite) { EXPECT_TRUE( stream_info_.filterState().hasData( - TcpProxy::PerConnectionCluster::Key)); + TcpProxy::PerConnectionCluster::key())); auto per_connection_cluster = stream_info_.filterState() .getDataReadOnly( - TcpProxy::PerConnectionCluster::Key); + TcpProxy::PerConnectionCluster::key()); EXPECT_EQ(per_connection_cluster.value(), "hello.ns1.svc.cluster.local"); } @@ -87,19 +87,19 @@ TEST_F(TcpClusterRewriteFilterTest, ClusterRewrite) { configure(proto_config); stream_info_.filterState().setData( - TcpProxy::PerConnectionCluster::Key, + TcpProxy::PerConnectionCluster::key(), std::make_unique("hello.ns1.global"), StreamInfo::FilterState::StateType::Mutable); filter_->onNewConnection(); EXPECT_TRUE( stream_info_.filterState().hasData( - TcpProxy::PerConnectionCluster::Key)); + TcpProxy::PerConnectionCluster::key())); auto per_connection_cluster = stream_info_.filterState() .getDataReadOnly( - TcpProxy::PerConnectionCluster::Key); + TcpProxy::PerConnectionCluster::key()); EXPECT_EQ(per_connection_cluster.value(), "hello.ns1.svc.cluster.local"); } @@ -111,19 +111,19 @@ TEST_F(TcpClusterRewriteFilterTest, ClusterRewrite) { configure(proto_config); stream_info_.filterState().setData( - TcpProxy::PerConnectionCluster::Key, + TcpProxy::PerConnectionCluster::key(), std::make_unique("hello.ns1.global"), StreamInfo::FilterState::StateType::Mutable); filter_->onNewConnection(); EXPECT_TRUE( stream_info_.filterState().hasData( - TcpProxy::PerConnectionCluster::Key)); + TcpProxy::PerConnectionCluster::key())); auto per_connection_cluster = stream_info_.filterState() .getDataReadOnly( - TcpProxy::PerConnectionCluster::Key); + TcpProxy::PerConnectionCluster::key()); EXPECT_EQ(per_connection_cluster.value(), "another.svc.cluster.local"); } }