diff --git a/include/istio/utils/attribute_names.h b/include/istio/utils/attribute_names.h index 84e7415837d..67c6df64c98 100644 --- a/include/istio/utils/attribute_names.h +++ b/include/istio/utils/attribute_names.h @@ -27,6 +27,7 @@ struct AttributeName { // https://github.com/istio/istio/issues/4689 static const char kSourceUser[]; static const char kSourcePrincipal[]; + static const char kSourceNamespace[]; static const char kDestinationPrincipal[]; static const char kRequestHeaders[]; diff --git a/src/envoy/http/authn/http_filter_test.cc b/src/envoy/http/authn/http_filter_test.cc index 08376bfb9a3..68d1edbbb74 100644 --- a/src/envoy/http/authn/http_filter_test.cc +++ b/src/envoy/http/authn/http_filter_test.cc @@ -72,7 +72,8 @@ std::unique_ptr createAlwaysPassAuthenticator( _local(FilterContext *filter_context) : AuthenticatorBase(filter_context) {} bool run(Payload *) override { // Set some data to verify authentication result later. - auto payload = TestUtilities::CreateX509Payload("foo"); + auto payload = TestUtilities::CreateX509Payload( + "cluster.local/sa/test_user/ns/test_ns/"); filter_context()->setPeerResult(&payload); return true; } @@ -180,16 +181,22 @@ TEST_F(AuthenticationFilterTest, AllPass) { ProtobufWkt::Struct expected_data; ASSERT_TRUE(Protobuf::TextFormat::ParseFromString(R"( + fields { + key: "source.namespace" + value { + string_value: "test_ns" + } + } fields { key: "source.principal" value { - string_value: "foo" + string_value: "cluster.local/sa/test_user/ns/test_ns/" } } fields { key: "source.user" value { - string_value: "foo" + string_value: "cluster.local/sa/test_user/ns/test_ns/" } })", &expected_data)); diff --git a/src/envoy/utils/BUILD b/src/envoy/utils/BUILD index adf671a1b20..3a3a9d1a11d 100644 --- a/src/envoy/utils/BUILD +++ b/src/envoy/utils/BUILD @@ -32,9 +32,11 @@ envoy_cc_library( repository = "@envoy", visibility = ["//visibility:public"], deps = [ + ":utils_lib", "//include/istio/utils:attribute_names_header", "//src/istio/authn:context_proto", "//src/istio/utils:attribute_names_lib", + "//src/istio/utils:utils_lib", ":filter_names_lib", "@envoy//source/exe:envoy_common_lib", ], diff --git a/src/envoy/utils/authn.cc b/src/envoy/utils/authn.cc index a28f8ceba8f..9c915eaca3d 100644 --- a/src/envoy/utils/authn.cc +++ b/src/envoy/utils/authn.cc @@ -18,6 +18,7 @@ #include "include/istio/utils/attribute_names.h" #include "src/envoy/utils/filter_names.h" #include "src/istio/authn/context.pb.h" +#include "src/istio/utils/utils.h" using istio::authn::Result; @@ -47,6 +48,11 @@ void Authentication::SaveAuthAttributesToStruct( result.peer_user()); setKeyValue(data, istio::utils::AttributeName::kSourcePrincipal, result.peer_user()); + std::string source_ns(""); + if (istio::utils::GetSourceNamespace(result.peer_user(), &source_ns)) { + setKeyValue(data, istio::utils::AttributeName::kSourceNamespace, + source_ns); + } } if (result.has_origin()) { const auto& origin = result.origin(); diff --git a/src/envoy/utils/authn_test.cc b/src/envoy/utils/authn_test.cc index cc97855cda9..93ab445c623 100644 --- a/src/envoy/utils/authn_test.cc +++ b/src/envoy/utils/authn_test.cc @@ -41,7 +41,7 @@ TEST_F(AuthenticationTest, SaveAuthAttributesToStruct) { EXPECT_TRUE(data.mutable_fields()->empty()); result.set_principal("principal"); - result.set_peer_user("peeruser"); + result.set_peer_user("cluster.local/sa/peeruser/ns/abc/"); auto origin = result.mutable_origin(); origin->add_audiences("audiences0"); origin->add_audiences("audiences1"); @@ -62,11 +62,15 @@ TEST_F(AuthenticationTest, SaveAuthAttributesToStruct) { "principal"); EXPECT_EQ( data.fields().at(istio::utils::AttributeName::kSourceUser).string_value(), - "peeruser"); + "cluster.local/sa/peeruser/ns/abc/"); EXPECT_EQ(data.fields() .at(istio::utils::AttributeName::kSourcePrincipal) .string_value(), - "peeruser"); + "cluster.local/sa/peeruser/ns/abc/"); + EXPECT_EQ(data.fields() + .at(istio::utils::AttributeName::kSourceNamespace) + .string_value(), + "abc"); EXPECT_EQ(data.fields() .at(istio::utils::AttributeName::kRequestAuthAudiences) .string_value(), diff --git a/src/istio/control/http/attributes_builder.cc b/src/istio/control/http/attributes_builder.cc index 91835c7df67..ac1e00fc92a 100644 --- a/src/istio/control/http/attributes_builder.cc +++ b/src/istio/control/http/attributes_builder.cc @@ -90,6 +90,7 @@ void AttributesBuilder::ExtractAuthAttributes(CheckData *check_data) { utils::AttributeName::kRequestAuthPrincipal, utils::AttributeName::kSourceUser, utils::AttributeName::kSourcePrincipal, + utils::AttributeName::kSourceNamespace, utils::AttributeName::kRequestAuthAudiences, utils::AttributeName::kRequestAuthPresenter, utils::AttributeName::kRequestAuthRawClaims, diff --git a/src/istio/control/http/attributes_builder_test.cc b/src/istio/control/http/attributes_builder_test.cc index 305fac3b44b..5f2d2429b3b 100644 --- a/src/istio/control/http/attributes_builder_test.cc +++ b/src/istio/control/http/attributes_builder_test.cc @@ -139,7 +139,7 @@ attributes { attributes { key: "source.principal" value { - string_value: "test_user" + string_value: "sa/test_user/ns/ns_ns/" } } )"; @@ -224,16 +224,22 @@ attributes { string_value: "www.google.com" } } +attributes { + key: "source.namespace" + value { + string_value: "ns_ns" + } +} attributes { key: "source.principal" value { - string_value: "test_user" + string_value: "sa/test_user/ns/ns_ns/" } } attributes { key: "source.user" value { - string_value: "test_user" + string_value: "sa/test_user/ns/ns_ns/" } } attributes { @@ -489,16 +495,22 @@ fields { string_value: "test_raw_claims" } } +fields { + key: "source.namespace" + value { + string_value: "ns_ns" + } +} fields { key: "source.principal" value { - string_value: "test_user" + string_value: "sa/test_user/ns/ns_ns/" } } fields { key: "source.user" value { - string_value: "test_user" + string_value: "sa/test_user/ns/ns_ns/" } } )"; @@ -556,7 +568,7 @@ TEST(AttributesBuilderTest, TestCheckAttributesWithoutAuthnFilter) { EXPECT_CALL(mock_data, GetPrincipal(_, _)) .WillRepeatedly(Invoke([](bool peer, std::string *user) -> bool { if (peer) { - *user = "test_user"; + *user = "sa/test_user/ns/ns_ns/"; } else { *user = "destination_user"; } @@ -630,7 +642,7 @@ TEST(AttributesBuilderTest, TestCheckAttributes) { EXPECT_CALL(mock_data, GetPrincipal(_, _)) .WillRepeatedly(Invoke([](bool peer, std::string *user) -> bool { if (peer) { - *user = "test_user"; + *user = "sa/test_user/ns/ns_ns/"; } else { *user = "destination_user"; } diff --git a/src/istio/control/tcp/BUILD b/src/istio/control/tcp/BUILD index 5794abd1771..438f7de4d02 100644 --- a/src/istio/control/tcp/BUILD +++ b/src/istio/control/tcp/BUILD @@ -31,6 +31,7 @@ cc_library( "//include/istio/utils:attribute_names_header", "//src/istio/control:common_lib", "//src/istio/utils:attribute_names_lib", + "//src/istio/utils:utils_lib", ], ) @@ -45,6 +46,7 @@ cc_test( linkstatic = 1, deps = [ ":control_lib", + "//src/istio/utils:utils_lib", "//external:googletest_main", ], ) diff --git a/src/istio/control/tcp/attributes_builder.cc b/src/istio/control/tcp/attributes_builder.cc index e61bc0e3917..595a5d9d7a6 100644 --- a/src/istio/control/tcp/attributes_builder.cc +++ b/src/istio/control/tcp/attributes_builder.cc @@ -14,6 +14,7 @@ */ #include "src/istio/control/tcp/attributes_builder.h" +#include "src/istio/utils/utils.h" #include "include/istio/utils/attribute_names.h" #include "include/istio/utils/attributes_builder.h" @@ -49,6 +50,10 @@ void AttributesBuilder::ExtractCheckAttributes(CheckData* check_data) { // over. https://github.com/istio/istio/issues/4689 builder.AddString(utils::AttributeName::kSourceUser, source_user); builder.AddString(utils::AttributeName::kSourcePrincipal, source_user); + std::string source_ns(""); + if (utils::GetSourceNamespace(source_user, &source_ns)) { + builder.AddString(utils::AttributeName::kSourceNamespace, source_ns); + } } std::string destination_principal; diff --git a/src/istio/control/tcp/attributes_builder_test.cc b/src/istio/control/tcp/attributes_builder_test.cc index 52ee3a984a2..faa78ccc06e 100644 --- a/src/istio/control/tcp/attributes_builder_test.cc +++ b/src/istio/control/tcp/attributes_builder_test.cc @@ -22,6 +22,7 @@ #include "include/istio/utils/attributes_builder.h" #include "src/istio/control/tcp/mock_check_data.h" #include "src/istio/control/tcp/mock_report_data.h" +#include "src/istio/utils/utils.h" using ::google::protobuf::TextFormat; using ::google::protobuf::util::MessageDifferencer; @@ -73,16 +74,22 @@ attributes { string_value: "www.google.com" } } +attributes { + key: "source.namespace" + value { + string_value: "ns_ns" + } +} attributes { key: "source.principal" value { - string_value: "test_user" + string_value: "cluster.local/sa/test_user/ns/ns_ns/" } } attributes { key: "source.user" value { - string_value: "test_user" + string_value: "cluster.local/sa/test_user/ns/ns_ns/" } } attributes { @@ -372,7 +379,7 @@ TEST(AttributesBuilderTest, TestCheckAttributes) { EXPECT_CALL(mock_data, GetPrincipal(_, _)) .WillRepeatedly(Invoke([](bool peer, std::string* user) -> bool { if (peer) { - *user = "test_user"; + *user = "cluster.local/sa/test_user/ns/ns_ns/"; } else { *user = "destination_user"; } diff --git a/src/istio/utils/BUILD b/src/istio/utils/BUILD index a1ec20a2851..1eac6ef4e5e 100644 --- a/src/istio/utils/BUILD +++ b/src/istio/utils/BUILD @@ -19,6 +19,10 @@ cc_library( srcs = [ "protobuf.cc", "status.cc", + "utils.cc" + ], + hdrs = [ + "utils.h", ], visibility = ["//visibility:public"], deps = [ @@ -27,6 +31,16 @@ cc_library( ], ) +cc_test( + name = "utils_test", + size = "small", + srcs = ["utils_test.cc"], + deps = [ + ":utils_lib", + "//external:googletest_main", + ], +) + cc_library( name = "md5_lib", srcs = ["md5.cc"], diff --git a/src/istio/utils/attribute_names.cc b/src/istio/utils/attribute_names.cc index d42e0666b38..eead2de2f58 100644 --- a/src/istio/utils/attribute_names.cc +++ b/src/istio/utils/attribute_names.cc @@ -21,6 +21,7 @@ namespace utils { // Define attribute names const char AttributeName::kSourceUser[] = "source.user"; const char AttributeName::kSourcePrincipal[] = "source.principal"; +const char AttributeName::kSourceNamespace[] = "source.namespace"; const char AttributeName::kDestinationPrincipal[] = "destination.principal"; const char AttributeName::kRequestHeaders[] = "request.headers"; diff --git a/src/istio/utils/utils.cc b/src/istio/utils/utils.cc new file mode 100644 index 00000000000..0d6151a9ca8 --- /dev/null +++ b/src/istio/utils/utils.cc @@ -0,0 +1,49 @@ +/* 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/istio/utils/utils.h" + +#include +#include + +namespace istio { +namespace utils { + +namespace { +const std::string kNamespaceKey("/ns/"); +const char kDelimiter = '/'; +} // namespace + +bool GetSourceNamespace(const std::string& principal, + std::string* source_namespace) { + if (source_namespace) { + // The namespace is a substring in principal with format: + // "/ns//sa/". '/' is not allowed to + // appear in actual content except as delimiter between tokens. + size_t begin = principal.find(kNamespaceKey); + if (begin == std::string::npos) { + return false; + } + begin += kNamespaceKey.length(); + size_t end = principal.find(kDelimiter, begin); + size_t len = (end == std::string::npos ? end : end - begin); + *source_namespace = principal.substr(begin, len); + return true; + } + return false; +} + +} // namespace utils +} // namespace istio diff --git a/src/istio/utils/utils.h b/src/istio/utils/utils.h new file mode 100644 index 00000000000..063c18ca402 --- /dev/null +++ b/src/istio/utils/utils.h @@ -0,0 +1,28 @@ +/* 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 + +namespace istio { +namespace utils { + +// Get source.namespace attribute from principal. +bool GetSourceNamespace(const std::string& principal, + std::string* source_namespace); + +} // namespace utils +} // namespace istio diff --git a/src/istio/utils/utils_test.cc b/src/istio/utils/utils_test.cc new file mode 100644 index 00000000000..4282e4edccd --- /dev/null +++ b/src/istio/utils/utils_test.cc @@ -0,0 +1,68 @@ +/* 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/istio/utils/utils.h" +#include "gtest/gtest.h" + +namespace istio { +namespace utils { +namespace { + +class UtilsTest : public ::testing::Test { + protected: + void checkFalse(const std::string& principal) { + std::string output_ns = "none"; + EXPECT_FALSE(GetSourceNamespace(principal, &output_ns)); + EXPECT_EQ(output_ns, output_ns); + } + + void checkTrue(const std::string& principal, const std::string& ns) { + std::string output_ns = "none"; + EXPECT_TRUE(GetSourceNamespace(principal, &output_ns)); + EXPECT_EQ(ns, output_ns); + } +}; + +TEST_F(UtilsTest, GetSourceNamespace) { + checkFalse(""); + checkFalse("cluster.local"); + checkFalse("cluster.local/"); + checkFalse("cluster.local/ns"); + checkFalse("cluster.local/sa/user"); + checkFalse("cluster.local/sa/user/ns"); + checkFalse("cluster.local/sa/user_ns/"); + checkFalse("cluster.local/sa/user_ns/abc/xyz"); + checkFalse("cluster.local/NS/abc"); + + checkTrue("cluster.local/ns/", ""); + checkTrue("cluster.local/ns//", ""); + checkTrue("cluster.local/sa/user/ns/", ""); + checkTrue("cluster.local/ns//sa/user", ""); + checkTrue("cluster.local/ns//ns/ns", ""); + + checkTrue("cluster.local/ns/ns/ns/ns", "ns"); + checkTrue("cluster.local/ns/abc_ns", "abc_ns"); + checkTrue("cluster.local/ns/abc_ns/", "abc_ns"); + checkTrue("cluster.local/ns/abc_ns/sa/user_ns", "abc_ns"); + checkTrue("cluster.local/ns/abc_ns/sa/user_ns/other/xyz", "abc_ns"); + checkTrue("cluster.local/sa/user_ns/ns/abc", "abc"); + checkTrue("cluster.local/sa/user_ns/ns/abc/", "abc"); + checkTrue("cluster.local/sa/user_ns/ns/abc_ns", "abc_ns"); + checkTrue("cluster.local/sa/user_ns/ns/abc_ns/", "abc_ns"); +} + +} // namespace +} // namespace utils +} // namespace istio