From bbbb2dfcf21fe1072971f51a73c5f1603bd25376 Mon Sep 17 00:00:00 2001 From: Diem Vu Date: Tue, 7 Aug 2018 16:07:29 -0700 Subject: [PATCH 1/5] Add integration test with JWT+AuthN+Mixer filter chain --- test/integration/BUILD | 37 ++ .../istio_http_integration_test.cc | 328 ++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 test/integration/BUILD create mode 100644 test/integration/istio_http_integration_test.cc diff --git a/test/integration/BUILD b/test/integration/BUILD new file mode 100644 index 00000000000..ba8ff16c51e --- /dev/null +++ b/test/integration/BUILD @@ -0,0 +1,37 @@ +# 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. +# +################################################################################ + +package(default_visibility = ["//visibility:public"]) + +load( + "@envoy//bazel:envoy_build_system.bzl", + "envoy_cc_test", +) + +envoy_cc_test( + name = "istio_http_integration_test", + srcs = ["istio_http_integration_test.cc"], + repository = "@envoy", + deps = [ + "@envoy//source/common/common:utility_lib", + "@envoy//test/integration:http_protocol_integration_lib", + "//src/envoy/http/authn:filter_lib", + "//src/envoy/http/jwt_auth:http_filter_factory", + "//src/envoy/http/jwt_auth:jwt_lib", + "//src/envoy/utils:filter_names_lib", + "//src/envoy/http/mixer:filter_lib", + ], +) \ No newline at end of file diff --git a/test/integration/istio_http_integration_test.cc b/test/integration/istio_http_integration_test.cc new file mode 100644 index 00000000000..223339f8499 --- /dev/null +++ b/test/integration/istio_http_integration_test.cc @@ -0,0 +1,328 @@ +/* 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. + */ + +// This test suite verifies the end-to-end behaviour of the HTTP filter chain with JWT + AuthN + Mixer. +// That chain is used in Istio, when authentication is active. Filters exchanges data between each other +// using request info (dynamic metadata) and that information can only be observed at the end (i.e +// from request to mixer backends). + +#include "fmt/printf.h" +#include "src/envoy/utils/filter_names.h" +#include "test/integration/http_protocol_integration.h" +#include "mixer/v1/report.pb.h" +#include "mixer/v1/check.pb.h" +#include "gmock/gmock.h" + +using ::google::protobuf::util::error::Code; +using google::protobuf::util::MessageDifferencer; +using ::testing::Contains; +using ::testing::Not; + +namespace Envoy { +namespace { + +// From https://github.com/istio/istio/blob/master/security/tools/jwt/samples/demo.jwt +static const char kGoodToken[] = + "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLC" + "J0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidG" + "VzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0v" + "xyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFW" + "jcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgef" + "Sj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPT" + "Aa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"; + +// Generate by gen-jwt.py as described in +// https://github.com/istio/istio/blob/master/security/tools/jwt/samples/README.md +// to generate token with invalid issuer. +// `security/tools/jwt/samples/gen-jwt.py security/tools/jwt/samples/key.pem \ +// --expire=3153600000 --iss "wrong-issuer@secure.istio.io"` +static const char kBadToken[] = + "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ" + "0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODcxODkyNTEsImlhdCI6MTUzMzU4OTI1MSwiaXNzIjoid3JvbmctaXNzdWVyQHN" + "lY3VyZS5pc3Rpby5pbyIsInN1YiI6Indyb25nLWlzc3VlckBzZWN1cmUuaXN0aW8uaW8ifQ.Ye7RKrEgr3mUxRE1OF5" + "sCaaH6kg_OT-mAM1HI3tTUp0ljVuxZLCcTXPvvEAjyeiNUm8fjeeER0fsXv7y8wTaA4FFw9x8NT9xS8pyLi6RsTwdjkq" + "0-Plu93VQk1R98BdbEVT-T5vVz7uACES4LQBqsvvTcLBbBNUvKs_eJyZG71WJuymkkbL5Ki7CB73sQUMl2T3eORC7DJt" + "yn_C9Dxy2cwCzHrLZnnGz839_bX_yi29dI4veYCNBgU-9ZwehqfgSCJWYUoBTrdM06N3jEemlWB83ZY4OXoW0pNx-ecu" + "3asJVbwyxV2_HT6_aUsdHwTYwHv2hXBjdKEfwZxSsBxbKpA"; + +static const char kExpectedPrincipal[] = "testing@secure.istio.io/testing@secure.istio.io"; +static const char kDestinationUID[] = "dest.pod.123"; +static const char kSourceUID[] = "src.pod.xyz"; +static const char kTelemetryBackend[] = "telemetry-backend"; +static const char kPolicyBackend[] = "policy-backend"; + +// Generates basic test request header. +Http::TestHeaderMapImpl BaseRequestHeaders() { + return Http::TestHeaderMapImpl{ + {":method", "GET"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "host"}, + {"x-forwarded-for", "10.0.0.1"} + }; +} + +// Generates test request header with given token. +Http::TestHeaderMapImpl HeadersWithToken(const std::string& token) { + auto headers = BaseRequestHeaders(); + headers.addCopy("Authorization", "Bearer " + token); + return headers; +} + +std::string MakeJwtFilterConfig() { + static const char kJwtFilterTemplate[] = R"( + name: %s + config: + rules: + - issuer: "testing@secure.istio.io" + local_jwks: + inline_string: "%s" + allow_missing_or_failed: true + )"; + // From https://github.com/istio/istio/blob/master/security/tools/jwt/samples/jwks.json + static const char kJwksInline[] = + "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\"," + "\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV" + "_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_" + "DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-" + "4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-" + "YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}"; + + return fmt::sprintf(kJwtFilterTemplate, Utils::IstioFilterName::kJwt, StringUtil::escape(kJwksInline)); +} + +std::string MakeAuthFilterConfig() { + static const char kAuthnFilterWithJwtTemplate[] = R"( + name: %s + config: + policy: + origins: + - jwt: + issuer: testing@secure.istio.io + jwks_uri: http://localhost:8081/ + principalBinding: USE_ORIGIN)"; + return fmt::sprintf(kAuthnFilterWithJwtTemplate, Utils::IstioFilterName::kAuthentication); +} + +std::string MakeMixerFilterConfig() { + static const char kMixerFilterTemplate[] = R"( + name: mixer + config: + defaultDestinationService: "default" + mixerAttributes: + attributes: { + "destination.uid": { + stringValue: %s + } + } + serviceConfigs: { + "default": {} + } + transport: + attributes_for_mixer_proxy: + attributes: { + "source.uid": { + string_value: %s + } + } + report_cluster: %s + check_cluster: %s + )"; + return fmt::sprintf(kMixerFilterTemplate, kDestinationUID, kSourceUID, kTelemetryBackend, kPolicyBackend); +} + +class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { + public: + void createUpstreams() override { + HttpProtocolIntegrationTest::createUpstreams(); + fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); + telemetry_upstream_ = fake_upstreams_.back().get(); + + fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); + policy_upstream_ = fake_upstreams_.back().get(); + } + + void SetUp() override { + config_helper_.addFilter(MakeMixerFilterConfig()); + config_helper_.addFilter(MakeAuthFilterConfig()); + config_helper_.addFilter(MakeJwtFilterConfig()); + + config_helper_.addConfigModifier(addCluster(kTelemetryBackend)); + config_helper_.addConfigModifier(addCluster(kPolicyBackend)); + + HttpProtocolIntegrationTest::initialize(); + } + + void TearDown() override { + cleanupConnection(fake_upstream_connection_); + cleanupConnection(telemetry_connection_); + cleanupConnection(policy_connection_); + } + + ConfigHelper::ConfigModifierFunction addCluster(const std::string& name) { + return [name](envoy::config::bootstrap::v2::Bootstrap& bootstrap) { + auto* cluster = bootstrap.mutable_static_resources()->add_clusters(); + cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); + cluster->mutable_http2_protocol_options(); + cluster->set_name(name); + }; + } + + + void waitForTelemetryRequest(::istio::mixer::v1::ReportRequest* request) { + AssertionResult result = + telemetry_upstream_->waitForHttpConnection(*dispatcher_, telemetry_connection_); + RELEASE_ASSERT(result, result.message()); + result = telemetry_connection_->waitForNewStream(*dispatcher_, telemetry_request_); + RELEASE_ASSERT(result, result.message()); + + result = telemetry_request_->waitForGrpcMessage(*dispatcher_, *request); + RELEASE_ASSERT(result, result.message()); + } + + // Must be called after waitForTelemetryRequest + void waitForTelemetryResponse() { + telemetry_request_->startGrpcStream(); + telemetry_request_->sendGrpcMessage(::istio::mixer::v1::ReportResponse{}); + telemetry_request_->finishGrpcStream(Grpc::Status::Ok); + } + + void waitForPolicyRequest(::istio::mixer::v1::CheckRequest* request) { + AssertionResult result = + policy_upstream_->waitForHttpConnection(*dispatcher_, policy_connection_); + RELEASE_ASSERT(result, result.message()); + result = policy_connection_->waitForNewStream(*dispatcher_, policy_request_); + RELEASE_ASSERT(result, result.message()); + + result = policy_request_->waitForGrpcMessage(*dispatcher_, *request); + RELEASE_ASSERT(result, result.message()); + } + + // Must be called after waitForPolicyRequest + void waitForPolicyResponse() { + policy_request_->startGrpcStream(); + ::istio::mixer::v1::CheckResponse response; + response.mutable_precondition()->mutable_status()->set_code(Code::OK); + policy_request_->sendGrpcMessage(response); + policy_request_->finishGrpcStream(Grpc::Status::Ok); + } + + void cleanupConnection(FakeHttpConnectionPtr& connection) { + if (connection != nullptr) { + AssertionResult result = connection->close(); + RELEASE_ASSERT(result, result.message()); + result = connection->waitForDisconnect(); + RELEASE_ASSERT(result, result.message()); + } + } + + FakeUpstream* telemetry_upstream_{}; + FakeHttpConnectionPtr telemetry_connection_{}; + FakeStreamPtr telemetry_request_{}; + + FakeUpstream* policy_upstream_{}; + FakeHttpConnectionPtr policy_connection_{}; + FakeStreamPtr policy_request_{}; + +}; + +INSTANTIATE_TEST_CASE_P( + Protocols, IstioHttpIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams()), + HttpProtocolIntegrationTest::protocolTestParamsToString); + +TEST_P(IstioHttpIntegrationTest, NoJwt) { + // initialize(); + codec_client_ = + makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeHeaderOnlyRequest(BaseRequestHeaders()); + + ::istio::mixer::v1::ReportRequest report_request; + waitForTelemetryRequest(&report_request); + // As authentication fail, report should not have 'word' that might come authN. + EXPECT_THAT(report_request.default_words(), ::testing::AllOf( + Contains(kDestinationUID), + Contains("10.0.0.1"), + Not(Contains(kExpectedPrincipal)))); + waitForTelemetryResponse(); + + response->waitForEndStream(); + EXPECT_TRUE(response->complete()); + EXPECT_STREQ("401", response->headers().Status()->value().c_str()); +} + +TEST_P(IstioHttpIntegrationTest, BadJwt) { + codec_client_ = + makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kBadToken)); + + ::istio::mixer::v1::ReportRequest report_request; + waitForTelemetryRequest(&report_request); + EXPECT_THAT(report_request.default_words(), ::testing::AllOf( + Contains(kDestinationUID), + Contains("10.0.0.1"), + Not(Contains(kExpectedPrincipal)))); + waitForTelemetryResponse(); + + response->waitForEndStream(); + EXPECT_TRUE(response->complete()); + EXPECT_STREQ("401", response->headers().Status()->value().c_str()); +} + +TEST_P(IstioHttpIntegrationTest, GoodJwt) { + codec_client_ = + makeHttpConnection(makeClientConnection((lookupPort("http")))); + auto response = codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kGoodToken)); + + ::istio::mixer::v1::CheckRequest check_request; + waitForPolicyRequest(&check_request); + // Check request should see authn attributes. + EXPECT_THAT(check_request.attributes().words(), ::testing::AllOf( + Contains(kDestinationUID), + Contains("10.0.0.1"), + Contains(kExpectedPrincipal), + Contains("testing@secure.istio.io"), + Contains("sub"), + Contains("iss"), + Contains("foo"), + Contains("bar"))); + waitForPolicyResponse(); + + waitForNextUpstreamRequest(0); + // Send backend response. + upstream_request_->encodeHeaders(Http::TestHeaderMapImpl{{":status", "200"}}, true); + response->waitForEndStream(); + + // Report (log) is sent after backen response. + ::istio::mixer::v1::ReportRequest report_request; + waitForTelemetryRequest(&report_request); + // Report request should also see the same authn attributes. + EXPECT_THAT(report_request.default_words(), ::testing::AllOf( + Contains(kDestinationUID), + Contains("10.0.0.1"), + Contains(kExpectedPrincipal), + Contains("testing@secure.istio.io"), + Contains("sub"), + Contains("iss"), + Contains("foo"), + Contains("bar"))); + waitForTelemetryResponse(); + + EXPECT_TRUE(response->complete()); + EXPECT_STREQ("200", response->headers().Status()->value().c_str()); +} + +} // namespace +} // namespace Envoy From a1dc19e85460b23544760fc35abbe389814846e2 Mon Sep 17 00:00:00 2001 From: Diem Vu Date: Tue, 7 Aug 2018 16:24:09 -0700 Subject: [PATCH 2/5] Lint --- .../istio_http_integration_test.cc | 187 ++++++++++-------- 1 file changed, 104 insertions(+), 83 deletions(-) diff --git a/test/integration/istio_http_integration_test.cc b/test/integration/istio_http_integration_test.cc index 223339f8499..e9d4117469a 100644 --- a/test/integration/istio_http_integration_test.cc +++ b/test/integration/istio_http_integration_test.cc @@ -13,35 +13,44 @@ * limitations under the License. */ -// This test suite verifies the end-to-end behaviour of the HTTP filter chain with JWT + AuthN + Mixer. -// That chain is used in Istio, when authentication is active. Filters exchanges data between each other -// using request info (dynamic metadata) and that information can only be observed at the end (i.e -// from request to mixer backends). +// This test suite verifies the end-to-end behaviour of the HTTP filter chain +// with JWT + AuthN + Mixer. That chain is used in Istio, when authentication is +// active. Filters exchanges data between each other using request info (dynamic +// metadata) and that information can only be observed at the end (i.e from +// request to mixer backends). #include "fmt/printf.h" +#include "gmock/gmock.h" +#include "mixer/v1/check.pb.h" +#include "mixer/v1/report.pb.h" #include "src/envoy/utils/filter_names.h" #include "test/integration/http_protocol_integration.h" -#include "mixer/v1/report.pb.h" -#include "mixer/v1/check.pb.h" -#include "gmock/gmock.h" using ::google::protobuf::util::error::Code; -using google::protobuf::util::MessageDifferencer; using ::testing::Contains; using ::testing::Not; +using google::protobuf::util::MessageDifferencer; namespace Envoy { namespace { -// From https://github.com/istio/istio/blob/master/security/tools/jwt/samples/demo.jwt +// From +// https://github.com/istio/istio/blob/master/security/tools/jwt/samples/demo.jwt static const char kGoodToken[] = - "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLC" - "J0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidG" - "VzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0v" - "xyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFW" - "jcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgef" - "Sj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPT" - "Aa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"; + "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0Un" + "pIVV8tZW52dlEiLC" + "J0eXAiOiJKV1QifQ." + "eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidG" + "VzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9" + ".CfNnxWP2tcnR9q0v" + "xyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-" + "KC9PJqYpgGbaXhaGx7bEdFW" + "jcwv3nZzvc7M__" + "ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccC" + "gef" + "Sj_GNfwIip3-SsFdlR7BtbVUcqR-yv-" + "XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPT" + "Aa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"; // Generate by gen-jwt.py as described in // https://github.com/istio/istio/blob/master/security/tools/jwt/samples/README.md @@ -49,15 +58,24 @@ static const char kGoodToken[] = // `security/tools/jwt/samples/gen-jwt.py security/tools/jwt/samples/key.pem \ // --expire=3153600000 --iss "wrong-issuer@secure.istio.io"` static const char kBadToken[] = - "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ" - "0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODcxODkyNTEsImlhdCI6MTUzMzU4OTI1MSwiaXNzIjoid3JvbmctaXNzdWVyQHN" - "lY3VyZS5pc3Rpby5pbyIsInN1YiI6Indyb25nLWlzc3VlckBzZWN1cmUuaXN0aW8uaW8ifQ.Ye7RKrEgr3mUxRE1OF5" - "sCaaH6kg_OT-mAM1HI3tTUp0ljVuxZLCcTXPvvEAjyeiNUm8fjeeER0fsXv7y8wTaA4FFw9x8NT9xS8pyLi6RsTwdjkq" - "0-Plu93VQk1R98BdbEVT-T5vVz7uACES4LQBqsvvTcLBbBNUvKs_eJyZG71WJuymkkbL5Ki7CB73sQUMl2T3eORC7DJt" - "yn_C9Dxy2cwCzHrLZnnGz839_bX_yi29dI4veYCNBgU-9ZwehqfgSCJWYUoBTrdM06N3jEemlWB83ZY4OXoW0pNx-ecu" - "3asJVbwyxV2_HT6_aUsdHwTYwHv2hXBjdKEfwZxSsBxbKpA"; - -static const char kExpectedPrincipal[] = "testing@secure.istio.io/testing@secure.istio.io"; + "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0Un" + "pIVV8tZW52dlEiLCJ" + "0eXAiOiJKV1QifQ." + "eyJleHAiOjQ2ODcxODkyNTEsImlhdCI6MTUzMzU4OTI1MSwiaXNzIjoid3JvbmctaXNzdWVyQH" + "N" + "lY3VyZS5pc3Rpby5pbyIsInN1YiI6Indyb25nLWlzc3VlckBzZWN1cmUuaXN0aW8uaW8ifQ." + "Ye7RKrEgr3mUxRE1OF5" + "sCaaH6kg_OT-" + "mAM1HI3tTUp0ljVuxZLCcTXPvvEAjyeiNUm8fjeeER0fsXv7y8wTaA4FFw9x8NT9xS8pyLi6Rs" + "Twdjkq" + "0-Plu93VQk1R98BdbEVT-T5vVz7uACES4LQBqsvvTcLBbBNUvKs_" + "eJyZG71WJuymkkbL5Ki7CB73sQUMl2T3eORC7DJt" + "yn_C9Dxy2cwCzHrLZnnGz839_bX_yi29dI4veYCNBgU-" + "9ZwehqfgSCJWYUoBTrdM06N3jEemlWB83ZY4OXoW0pNx-ecu" + "3asJVbwyxV2_HT6_aUsdHwTYwHv2hXBjdKEfwZxSsBxbKpA"; + +static const char kExpectedPrincipal[] = + "testing@secure.istio.io/testing@secure.istio.io"; static const char kDestinationUID[] = "dest.pod.123"; static const char kSourceUID[] = "src.pod.xyz"; static const char kTelemetryBackend[] = "telemetry-backend"; @@ -65,13 +83,11 @@ static const char kPolicyBackend[] = "policy-backend"; // Generates basic test request header. Http::TestHeaderMapImpl BaseRequestHeaders() { - return Http::TestHeaderMapImpl{ - {":method", "GET"}, - {":path", "/"}, - {":scheme", "http"}, - {":authority", "host"}, - {"x-forwarded-for", "10.0.0.1"} - }; + return Http::TestHeaderMapImpl{{":method", "GET"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "host"}, + {"x-forwarded-for", "10.0.0.1"}}; } // Generates test request header with given token. @@ -91,16 +107,21 @@ std::string MakeJwtFilterConfig() { inline_string: "%s" allow_missing_or_failed: true )"; - // From https://github.com/istio/istio/blob/master/security/tools/jwt/samples/jwks.json + // From + // https://github.com/istio/istio/blob/master/security/tools/jwt/samples/jwks.json static const char kJwksInline[] = - "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\"," - "\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV" - "_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_" - "DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-" - "4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-" - "YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}"; - - return fmt::sprintf(kJwtFilterTemplate, Utils::IstioFilterName::kJwt, StringUtil::escape(kJwksInline)); + "{ \"keys\":[ " + "{\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\"," + "\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-" + "P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV" + "_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_" + "pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_" + "DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-" + "4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-" + "YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}"; + + return fmt::sprintf(kJwtFilterTemplate, Utils::IstioFilterName::kJwt, + StringUtil::escape(kJwksInline)); } std::string MakeAuthFilterConfig() { @@ -113,7 +134,8 @@ std::string MakeAuthFilterConfig() { issuer: testing@secure.istio.io jwks_uri: http://localhost:8081/ principalBinding: USE_ORIGIN)"; - return fmt::sprintf(kAuthnFilterWithJwtTemplate, Utils::IstioFilterName::kAuthentication); + return fmt::sprintf(kAuthnFilterWithJwtTemplate, + Utils::IstioFilterName::kAuthentication); } std::string MakeMixerFilterConfig() { @@ -140,17 +162,20 @@ std::string MakeMixerFilterConfig() { report_cluster: %s check_cluster: %s )"; - return fmt::sprintf(kMixerFilterTemplate, kDestinationUID, kSourceUID, kTelemetryBackend, kPolicyBackend); + return fmt::sprintf(kMixerFilterTemplate, kDestinationUID, kSourceUID, + kTelemetryBackend, kPolicyBackend); } class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { public: void createUpstreams() override { HttpProtocolIntegrationTest::createUpstreams(); - fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); + fake_upstreams_.emplace_back( + new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); telemetry_upstream_ = fake_upstreams_.back().get(); - fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); + fake_upstreams_.emplace_back( + new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_)); policy_upstream_ = fake_upstreams_.back().get(); } @@ -180,12 +205,12 @@ class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { }; } - void waitForTelemetryRequest(::istio::mixer::v1::ReportRequest* request) { - AssertionResult result = - telemetry_upstream_->waitForHttpConnection(*dispatcher_, telemetry_connection_); + AssertionResult result = telemetry_upstream_->waitForHttpConnection( + *dispatcher_, telemetry_connection_); RELEASE_ASSERT(result, result.message()); - result = telemetry_connection_->waitForNewStream(*dispatcher_, telemetry_request_); + result = telemetry_connection_->waitForNewStream(*dispatcher_, + telemetry_request_); RELEASE_ASSERT(result, result.message()); result = telemetry_request_->waitForGrpcMessage(*dispatcher_, *request); @@ -200,10 +225,11 @@ class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { } void waitForPolicyRequest(::istio::mixer::v1::CheckRequest* request) { - AssertionResult result = - policy_upstream_->waitForHttpConnection(*dispatcher_, policy_connection_); + AssertionResult result = policy_upstream_->waitForHttpConnection( + *dispatcher_, policy_connection_); RELEASE_ASSERT(result, result.message()); - result = policy_connection_->waitForNewStream(*dispatcher_, policy_request_); + result = + policy_connection_->waitForNewStream(*dispatcher_, policy_request_); RELEASE_ASSERT(result, result.message()); result = policy_request_->waitForGrpcMessage(*dispatcher_, *request); @@ -235,7 +261,6 @@ class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { FakeUpstream* policy_upstream_{}; FakeHttpConnectionPtr policy_connection_{}; FakeStreamPtr policy_request_{}; - }; INSTANTIATE_TEST_CASE_P( @@ -251,11 +276,11 @@ TEST_P(IstioHttpIntegrationTest, NoJwt) { ::istio::mixer::v1::ReportRequest report_request; waitForTelemetryRequest(&report_request); - // As authentication fail, report should not have 'word' that might come authN. - EXPECT_THAT(report_request.default_words(), ::testing::AllOf( - Contains(kDestinationUID), - Contains("10.0.0.1"), - Not(Contains(kExpectedPrincipal)))); + // As authentication fail, report should not have 'word' that might come + // authN. + EXPECT_THAT(report_request.default_words(), + ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), + Not(Contains(kExpectedPrincipal)))); waitForTelemetryResponse(); response->waitForEndStream(); @@ -266,14 +291,14 @@ TEST_P(IstioHttpIntegrationTest, NoJwt) { TEST_P(IstioHttpIntegrationTest, BadJwt) { codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); - auto response = codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kBadToken)); + auto response = + codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kBadToken)); ::istio::mixer::v1::ReportRequest report_request; waitForTelemetryRequest(&report_request); - EXPECT_THAT(report_request.default_words(), ::testing::AllOf( - Contains(kDestinationUID), - Contains("10.0.0.1"), - Not(Contains(kExpectedPrincipal)))); + EXPECT_THAT(report_request.default_words(), + ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), + Not(Contains(kExpectedPrincipal)))); waitForTelemetryResponse(); response->waitForEndStream(); @@ -284,40 +309,36 @@ TEST_P(IstioHttpIntegrationTest, BadJwt) { TEST_P(IstioHttpIntegrationTest, GoodJwt) { codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); - auto response = codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kGoodToken)); + auto response = + codec_client_->makeHeaderOnlyRequest(HeadersWithToken(kGoodToken)); ::istio::mixer::v1::CheckRequest check_request; waitForPolicyRequest(&check_request); // Check request should see authn attributes. - EXPECT_THAT(check_request.attributes().words(), ::testing::AllOf( - Contains(kDestinationUID), - Contains("10.0.0.1"), - Contains(kExpectedPrincipal), - Contains("testing@secure.istio.io"), - Contains("sub"), - Contains("iss"), - Contains("foo"), - Contains("bar"))); + EXPECT_THAT( + check_request.attributes().words(), + ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), + Contains(kExpectedPrincipal), + Contains("testing@secure.istio.io"), Contains("sub"), + Contains("iss"), Contains("foo"), Contains("bar"))); waitForPolicyResponse(); waitForNextUpstreamRequest(0); // Send backend response. - upstream_request_->encodeHeaders(Http::TestHeaderMapImpl{{":status", "200"}}, true); + upstream_request_->encodeHeaders(Http::TestHeaderMapImpl{{":status", "200"}}, + true); response->waitForEndStream(); // Report (log) is sent after backen response. ::istio::mixer::v1::ReportRequest report_request; waitForTelemetryRequest(&report_request); // Report request should also see the same authn attributes. - EXPECT_THAT(report_request.default_words(), ::testing::AllOf( - Contains(kDestinationUID), - Contains("10.0.0.1"), - Contains(kExpectedPrincipal), - Contains("testing@secure.istio.io"), - Contains("sub"), - Contains("iss"), - Contains("foo"), - Contains("bar"))); + EXPECT_THAT( + report_request.default_words(), + ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), + Contains(kExpectedPrincipal), + Contains("testing@secure.istio.io"), Contains("sub"), + Contains("iss"), Contains("foo"), Contains("bar"))); waitForTelemetryResponse(); EXPECT_TRUE(response->complete()); From ea7d296183688a154bb25866357b8f5044959329 Mon Sep 17 00:00:00 2001 From: Diem Vu Date: Tue, 7 Aug 2018 16:47:26 -0700 Subject: [PATCH 3/5] Rename helper function --- test/integration/istio_http_integration_test.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/istio_http_integration_test.cc b/test/integration/istio_http_integration_test.cc index e9d4117469a..ce9e1e51284 100644 --- a/test/integration/istio_http_integration_test.cc +++ b/test/integration/istio_http_integration_test.cc @@ -218,7 +218,7 @@ class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { } // Must be called after waitForTelemetryRequest - void waitForTelemetryResponse() { + void sendTelemetryResponse() { telemetry_request_->startGrpcStream(); telemetry_request_->sendGrpcMessage(::istio::mixer::v1::ReportResponse{}); telemetry_request_->finishGrpcStream(Grpc::Status::Ok); @@ -237,7 +237,7 @@ class IstioHttpIntegrationTest : public HttpProtocolIntegrationTest { } // Must be called after waitForPolicyRequest - void waitForPolicyResponse() { + void sendPolicyResponse() { policy_request_->startGrpcStream(); ::istio::mixer::v1::CheckResponse response; response.mutable_precondition()->mutable_status()->set_code(Code::OK); @@ -281,7 +281,7 @@ TEST_P(IstioHttpIntegrationTest, NoJwt) { EXPECT_THAT(report_request.default_words(), ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), Not(Contains(kExpectedPrincipal)))); - waitForTelemetryResponse(); + sendTelemetryResponse(); response->waitForEndStream(); EXPECT_TRUE(response->complete()); @@ -299,7 +299,7 @@ TEST_P(IstioHttpIntegrationTest, BadJwt) { EXPECT_THAT(report_request.default_words(), ::testing::AllOf(Contains(kDestinationUID), Contains("10.0.0.1"), Not(Contains(kExpectedPrincipal)))); - waitForTelemetryResponse(); + sendTelemetryResponse(); response->waitForEndStream(); EXPECT_TRUE(response->complete()); @@ -321,7 +321,7 @@ TEST_P(IstioHttpIntegrationTest, GoodJwt) { Contains(kExpectedPrincipal), Contains("testing@secure.istio.io"), Contains("sub"), Contains("iss"), Contains("foo"), Contains("bar"))); - waitForPolicyResponse(); + sendPolicyResponse(); waitForNextUpstreamRequest(0); // Send backend response. @@ -339,7 +339,7 @@ TEST_P(IstioHttpIntegrationTest, GoodJwt) { Contains(kExpectedPrincipal), Contains("testing@secure.istio.io"), Contains("sub"), Contains("iss"), Contains("foo"), Contains("bar"))); - waitForTelemetryResponse(); + sendTelemetryResponse(); EXPECT_TRUE(response->complete()); EXPECT_STREQ("200", response->headers().Status()->value().c_str()); From f10111af495c80c655c6e0e613dcb86878ed725a Mon Sep 17 00:00:00 2001 From: Diem Vu Date: Tue, 7 Aug 2018 21:54:20 -0700 Subject: [PATCH 4/5] Lint --- test/integration/istio_http_integration_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/istio_http_integration_test.cc b/test/integration/istio_http_integration_test.cc index ce9e1e51284..a60aa97f30b 100644 --- a/test/integration/istio_http_integration_test.cc +++ b/test/integration/istio_http_integration_test.cc @@ -55,7 +55,7 @@ static const char kGoodToken[] = // Generate by gen-jwt.py as described in // https://github.com/istio/istio/blob/master/security/tools/jwt/samples/README.md // to generate token with invalid issuer. -// `security/tools/jwt/samples/gen-jwt.py security/tools/jwt/samples/key.pem \ +// `security/tools/jwt/samples/gen-jwt.py security/tools/jwt/samples/key.pem // --expire=3153600000 --iss "wrong-issuer@secure.istio.io"` static const char kBadToken[] = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0Un" From 73d9e44718cc75e1715d34c73f3515dca6badb2a Mon Sep 17 00:00:00 2001 From: Diem Vu Date: Wed, 8 Aug 2018 08:43:53 -0700 Subject: [PATCH 5/5] Review --- .../istio_http_integration_test.cc | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/integration/istio_http_integration_test.cc b/test/integration/istio_http_integration_test.cc index a60aa97f30b..a6f75b59ee7 100644 --- a/test/integration/istio_http_integration_test.cc +++ b/test/integration/istio_http_integration_test.cc @@ -29,14 +29,13 @@ using ::google::protobuf::util::error::Code; using ::testing::Contains; using ::testing::Not; -using google::protobuf::util::MessageDifferencer; namespace Envoy { namespace { // From // https://github.com/istio/istio/blob/master/security/tools/jwt/samples/demo.jwt -static const char kGoodToken[] = +constexpr char kGoodToken[] = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0Un" "pIVV8tZW52dlEiLC" "J0eXAiOiJKV1QifQ." @@ -57,7 +56,7 @@ static const char kGoodToken[] = // to generate token with invalid issuer. // `security/tools/jwt/samples/gen-jwt.py security/tools/jwt/samples/key.pem // --expire=3153600000 --iss "wrong-issuer@secure.istio.io"` -static const char kBadToken[] = +constexpr char kBadToken[] = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0Un" "pIVV8tZW52dlEiLCJ" "0eXAiOiJKV1QifQ." @@ -74,12 +73,12 @@ static const char kBadToken[] = "9ZwehqfgSCJWYUoBTrdM06N3jEemlWB83ZY4OXoW0pNx-ecu" "3asJVbwyxV2_HT6_aUsdHwTYwHv2hXBjdKEfwZxSsBxbKpA"; -static const char kExpectedPrincipal[] = +constexpr char kExpectedPrincipal[] = "testing@secure.istio.io/testing@secure.istio.io"; -static const char kDestinationUID[] = "dest.pod.123"; -static const char kSourceUID[] = "src.pod.xyz"; -static const char kTelemetryBackend[] = "telemetry-backend"; -static const char kPolicyBackend[] = "policy-backend"; +constexpr char kDestinationUID[] = "dest.pod.123"; +constexpr char kSourceUID[] = "src.pod.xyz"; +constexpr char kTelemetryBackend[] = "telemetry-backend"; +constexpr char kPolicyBackend[] = "policy-backend"; // Generates basic test request header. Http::TestHeaderMapImpl BaseRequestHeaders() { @@ -98,7 +97,7 @@ Http::TestHeaderMapImpl HeadersWithToken(const std::string& token) { } std::string MakeJwtFilterConfig() { - static const char kJwtFilterTemplate[] = R"( + constexpr char kJwtFilterTemplate[] = R"( name: %s config: rules: @@ -109,7 +108,7 @@ std::string MakeJwtFilterConfig() { )"; // From // https://github.com/istio/istio/blob/master/security/tools/jwt/samples/jwks.json - static const char kJwksInline[] = + constexpr char kJwksInline[] = "{ \"keys\":[ " "{\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\"," "\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-" @@ -125,7 +124,7 @@ std::string MakeJwtFilterConfig() { } std::string MakeAuthFilterConfig() { - static const char kAuthnFilterWithJwtTemplate[] = R"( + constexpr char kAuthnFilterWithJwtTemplate[] = R"( name: %s config: policy: @@ -139,7 +138,7 @@ std::string MakeAuthFilterConfig() { } std::string MakeMixerFilterConfig() { - static const char kMixerFilterTemplate[] = R"( + constexpr char kMixerFilterTemplate[] = R"( name: mixer config: defaultDestinationService: "default"