diff --git a/source/extensions/filters/http/peer_metadata/filter.cc b/source/extensions/filters/http/peer_metadata/filter.cc index f4b93cad446..8b01e8685e8 100644 --- a/source/extensions/filters/http/peer_metadata/filter.cc +++ b/source/extensions/filters/http/peer_metadata/filter.cc @@ -35,23 +35,38 @@ class XDSMethod : public DiscoveryMethod { public: XDSMethod(bool downstream, Server::Configuration::ServerFactoryContext& factory_context) : downstream_(downstream), - metadata_provider_(Extensions::Common::WorkloadDiscovery::GetProvider(factory_context)) {} + metadata_provider_(Extensions::Common::WorkloadDiscovery::GetProvider(factory_context)), + local_info_(factory_context.localInfo()) {} absl::optional derivePeerInfo(const StreamInfo::StreamInfo&, Http::HeaderMap&, Context&) const override; private: const bool downstream_; Extensions::Common::WorkloadDiscovery::WorkloadMetadataProviderSharedPtr metadata_provider_; + const LocalInfo::LocalInfo& local_info_; }; absl::optional XDSMethod::derivePeerInfo(const StreamInfo::StreamInfo& info, - Http::HeaderMap&, Context&) const { + Http::HeaderMap& headers, Context&) const { if (!metadata_provider_) { return {}; } Network::Address::InstanceConstSharedPtr peer_address; if (downstream_) { - peer_address = info.downstreamAddressProvider().remoteAddress(); + const auto origin_network_header = headers.get(Headers::get().ExchangeMetadataOriginNetwork); + const auto& local_metadata = local_info_.node().metadata(); + const auto& it = local_metadata.fields().find("NETWORK"); + // We might not have a local network configured in the single cluster case, so default to empty. + auto local_network = it != local_metadata.fields().end() ? it->second.string_value() : ""; + if (!origin_network_header.empty() && + origin_network_header[0]->value().getStringView() != local_network) { + ENVOY_LOG_MISC(debug, + "Origin network header present: {}; skipping downstream workload discovery", + origin_network_header[0]->value().getStringView()); + peer_address = {}; + } else { + peer_address = info.downstreamAddressProvider().remoteAddress(); + } } else { if (info.upstreamInfo().has_value()) { auto upstream_host = info.upstreamInfo().value().get().upstreamHost(); @@ -64,6 +79,20 @@ absl::optional XDSMethod::derivePeerInfo(const StreamInfo::StreamInfo& case Network::Address::Type::EnvoyInternal: if (upstream_host->metadata()) { const auto& filter_metadata = upstream_host->metadata()->filter_metadata(); + const auto& istio_it = filter_metadata.find("istio"); + if (istio_it != filter_metadata.end()) { + const auto& double_hbone_it = istio_it->second.fields().find("double_hbone"); + // This is an E/W gateway endpoint, so we should explicitly not use workload discovery + if (double_hbone_it != istio_it->second.fields().end()) { + ENVOY_LOG_MISC( + debug, + "Skipping upstream workload discovery for an endpoint on a remote network"); + peer_address = nullptr; + break; + } + } else { + ENVOY_LOG_MISC(debug, "No istio metadata found on upstream host."); + } const auto& it = filter_metadata.find("envoy.filters.listener.original_dst"); if (it != filter_metadata.end()) { const auto& destination_it = it->second.fields().find("local"); diff --git a/source/extensions/filters/http/peer_metadata/filter.h b/source/extensions/filters/http/peer_metadata/filter.h index 94da2a86c83..f57ac950868 100644 --- a/source/extensions/filters/http/peer_metadata/filter.h +++ b/source/extensions/filters/http/peer_metadata/filter.h @@ -32,6 +32,7 @@ using ::Envoy::Extensions::Filters::Common::Expr::CelStateType; struct HeaderValues { const Http::LowerCaseString ExchangeMetadataHeader{"x-envoy-peer-metadata"}; const Http::LowerCaseString ExchangeMetadataHeaderId{"x-envoy-peer-metadata-id"}; + const Http::LowerCaseString ExchangeMetadataOriginNetwork{"x-istio-origin-network"}; }; using Headers = ConstSingleton; diff --git a/source/extensions/filters/http/peer_metadata/filter_test.cc b/source/extensions/filters/http/peer_metadata/filter_test.cc index 995da18b224..7cacc1f2eea 100644 --- a/source/extensions/filters/http/peer_metadata/filter_test.cc +++ b/source/extensions/filters/http/peer_metadata/filter_test.cc @@ -153,6 +153,30 @@ TEST_F(PeerMetadataTest, DownstreamXDS) { checkShared(false); } +TEST_F(PeerMetadataTest, DownstreamXDSCrossNetwork) { + request_headers_.setReference(Headers::get().ExchangeMetadataOriginNetwork, "remote-network"); + const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", + "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); + EXPECT_CALL(*metadata_provider_, GetMetadata(_)) + .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) + -> std::optional { + if (absl::StartsWith(address->asStringView(), "127.0.0.1")) { + return {pod}; + } + return {}; + })); + initialize(R"EOF( + downstream_discovery: + - workload_discovery: {} + )EOF"); + EXPECT_EQ(1, request_headers_.size()); // We don't remove the header because we terminate the + // tunnel that delivered it + EXPECT_EQ(0, response_headers_.size()); + checkNoPeer(true); // No downstream peer because it's a cross-network request + checkNoPeer(false); + checkShared(false); +} + TEST_F(PeerMetadataTest, UpstreamXDS) { const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); @@ -210,6 +234,45 @@ TEST_F(PeerMetadataTest, UpstreamXDSInternal) { checkPeerNamespace(false, "foo"); } +TEST_F(PeerMetadataTest, UpstreamXDSInternalCrossNetwork) { + Network::Address::InstanceConstSharedPtr upstream_address = + std::make_shared("internal_address", "endpoint_id"); + std::shared_ptr> upstream_host( + new NiceMock()); + EXPECT_CALL(*upstream_host, address()).WillRepeatedly(Return(upstream_address)); + stream_info_.upstreamInfo()->setUpstreamHost(upstream_host); + auto host_metadata = std::make_shared(); + ON_CALL(*upstream_host, metadata()).WillByDefault(testing::Return(host_metadata)); + TestUtility::loadFromYaml(R"EOF( + filter_metadata: + envoy.filters.listener.original_dst: + local: 127.0.0.100:80 + istio: + double_hbone: + hbone_target_address: 10.0.0.1 + )EOF", + *host_metadata); + + const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", + "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); + EXPECT_CALL(*metadata_provider_, GetMetadata(_)) + .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) + -> std::optional { + if (absl::StartsWith(address->asStringView(), "127.0.0.100")) { + return {pod}; + } + return {}; + })); + initialize(R"EOF( + upstream_discovery: + - workload_discovery: {} + )EOF"); + EXPECT_EQ(0, request_headers_.size()); + EXPECT_EQ(0, response_headers_.size()); + checkNoPeer(true); + checkNoPeer(false); // Shouldn't be any upstream filter state since it's a cross-network endpoint +} + TEST_F(PeerMetadataTest, DownstreamMXEmpty) { initialize(R"EOF( downstream_discovery: