From 5fbb50c1880b0dafc64892e0c23d188c2eccbec7 Mon Sep 17 00:00:00 2001 From: John Gibson Date: Tue, 26 Dec 2017 16:40:31 -0500 Subject: [PATCH] rgw: Added tests for S3 Policy IP Address feature. Signed-off-by: John Gibson (cherry picked from commit 3d260f26e6826182b4de520878b548a28a96e81b) --- src/test/rgw/test_rgw_iam_policy.cc | 292 ++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) diff --git a/src/test/rgw/test_rgw_iam_policy.cc b/src/test/rgw/test_rgw_iam_policy.cc index 7738869ff37f7..e38fcd757bc6e 100644 --- a/src/test/rgw/test_rgw_iam_policy.cc +++ b/src/test/rgw/test_rgw_iam_policy.cc @@ -19,6 +19,7 @@ #include +#include "include/stringify.h" #include "common/code_environment.h" #include "common/ceph_context.h" #include "global/global_init.h" @@ -514,6 +515,297 @@ string PolicyTest::example3 = R"( } )"; +class IPPolicyTest : public ::testing::Test { +protected: + intrusive_ptr cct; + static const string arbitrary_tenant; + static string ip_address_allow_example; + static string ip_address_deny_example; + static string ip_address_full_example; + // 192.168.1.0/24 + const rgw::IAM::MaskedIP allowedIPv4Range = { false, rgw::IAM::Address("11000000101010000000000100000000"), 24 }; + // 192.168.1.1/32 + const rgw::IAM::MaskedIP blacklistedIPv4 = { false, rgw::IAM::Address("11000000101010000000000100000001"), 32 }; + // 2001:db8:85a3:0:0:8a2e:370:7334/128 + const rgw::IAM::MaskedIP allowedIPv6 = { true, rgw::IAM::Address("00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110100"), 128 }; + // ::1 + const rgw::IAM::MaskedIP blacklistedIPv6 = { true, rgw::IAM::Address(1), 128 }; + // 2001:db8:85a3:0:0:8a2e:370:7330/124 + const rgw::IAM::MaskedIP allowedIPv6Range = { true, rgw::IAM::Address("00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110000"), 124 }; +public: + IPPolicyTest() { + cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT); + } +}; +const string IPPolicyTest::arbitrary_tenant = "arbitrary_tenant"; + +TEST_F(IPPolicyTest, MaskedIPOperations) { + EXPECT_EQ(stringify(allowedIPv4Range), "192.168.1.0/24"); + EXPECT_EQ(stringify(blacklistedIPv4), "192.168.1.1/32"); + EXPECT_EQ(stringify(allowedIPv6), "2001:db8:85a3:0:0:8a2e:370:7334/128"); + EXPECT_EQ(stringify(allowedIPv6Range), "2001:db8:85a3:0:0:8a2e:370:7330/124"); + EXPECT_EQ(stringify(blacklistedIPv6), "0:0:0:0:0:0:0:1/128"); + EXPECT_EQ(allowedIPv4Range, blacklistedIPv4); + EXPECT_EQ(allowedIPv6Range, allowedIPv6); +} + +TEST_F(IPPolicyTest, asNetworkIPv4Range) { + auto actualIPv4Range = rgw::IAM::Condition::as_network("192.168.1.0/24"); + ASSERT_TRUE(actualIPv4Range.is_initialized()); + EXPECT_EQ(*actualIPv4Range, allowedIPv4Range); +} + +TEST_F(IPPolicyTest, asNetworkIPv4) { + auto actualIPv4 = rgw::IAM::Condition::as_network("192.168.1.1"); + ASSERT_TRUE(actualIPv4.is_initialized()); + EXPECT_EQ(*actualIPv4, blacklistedIPv4); +} + +TEST_F(IPPolicyTest, asNetworkIPv6Range) { + auto actualIPv6Range = rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7330/124"); + ASSERT_TRUE(actualIPv6Range.is_initialized()); + EXPECT_EQ(*actualIPv6Range, allowedIPv6Range); +} + +TEST_F(IPPolicyTest, asNetworkIPv6) { + auto actualIPv6 = rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7334"); + ASSERT_TRUE(actualIPv6.is_initialized()); + EXPECT_EQ(*actualIPv6, allowedIPv6); +} + +TEST_F(IPPolicyTest, asNetworkInvalid) { + EXPECT_FALSE(rgw::IAM::Condition::as_network("")); + EXPECT_FALSE(rgw::IAM::Condition::as_network("192.168.1.1/33")); + EXPECT_FALSE(rgw::IAM::Condition::as_network("2001:db8:85a3:0:0:8a2e:370:7334/129")); + EXPECT_FALSE(rgw::IAM::Condition::as_network("192.168.1.1:")); + EXPECT_FALSE(rgw::IAM::Condition::as_network("1.2.3.10000")); +} + +TEST_F(IPPolicyTest, ParseIPAddress) { + optional p; + + ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant, + bufferlist::static_from_string(ip_address_full_example))); + ASSERT_TRUE(p); + + EXPECT_EQ(p->text, ip_address_full_example); + EXPECT_EQ(p->version, Version::v2012_10_17); + EXPECT_EQ(*p->id, "S3IPPolicyTest"); + EXPECT_FALSE(p->statements.empty()); + EXPECT_EQ(p->statements.size(), 1U); + EXPECT_EQ(*p->statements[0].sid, "IPAllow"); + EXPECT_FALSE(p->statements[0].princ.empty()); + EXPECT_EQ(p->statements[0].princ.size(), 1U); + EXPECT_EQ(*p->statements[0].princ.begin(), + Principal::wildcard()); + EXPECT_TRUE(p->statements[0].noprinc.empty()); + EXPECT_EQ(p->statements[0].effect, Effect::Allow); + EXPECT_EQ(p->statements[0].action, s3ListBucket); + EXPECT_EQ(p->statements[0].notaction, s3None); + ASSERT_FALSE(p->statements[0].resource.empty()); + ASSERT_EQ(p->statements[0].resource.size(), 2U); + EXPECT_EQ(p->statements[0].resource.begin()->partition, Partition::aws); + EXPECT_EQ(p->statements[0].resource.begin()->service, Service::s3); + EXPECT_TRUE(p->statements[0].resource.begin()->region.empty()); + EXPECT_EQ(p->statements[0].resource.begin()->account, arbitrary_tenant); + EXPECT_EQ(p->statements[0].resource.begin()->resource, "example_bucket"); + EXPECT_EQ((p->statements[0].resource.begin() + 1)->resource, "example_bucket/*"); + EXPECT_TRUE(p->statements[0].notresource.empty()); + ASSERT_FALSE(p->statements[0].conditions.empty()); + ASSERT_EQ(p->statements[0].conditions.size(), 2U); + EXPECT_EQ(p->statements[0].conditions[0].op, TokenID::IpAddress); + EXPECT_EQ(p->statements[0].conditions[0].key, "aws:SourceIp"); + ASSERT_FALSE(p->statements[0].conditions[0].vals.empty()); + EXPECT_EQ(p->statements[0].conditions[0].vals.size(), 2U); + EXPECT_EQ(p->statements[0].conditions[0].vals[0], "192.168.1.0/24"); + EXPECT_EQ(p->statements[0].conditions[0].vals[1], "::1"); + optional convertedIPv4 = rgw::IAM::Condition::as_network(p->statements[0].conditions[0].vals[0]); + EXPECT_TRUE(convertedIPv4.is_initialized()); + if (convertedIPv4.is_initialized()) { + EXPECT_EQ(*convertedIPv4, allowedIPv4Range); + } + + EXPECT_EQ(p->statements[0].conditions[1].op, TokenID::NotIpAddress); + EXPECT_EQ(p->statements[0].conditions[1].key, "aws:SourceIp"); + ASSERT_FALSE(p->statements[0].conditions[1].vals.empty()); + EXPECT_EQ(p->statements[0].conditions[1].vals.size(), 2U); + EXPECT_EQ(p->statements[0].conditions[1].vals[0], "192.168.1.1/32"); + EXPECT_EQ(p->statements[0].conditions[1].vals[1], "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + optional convertedIPv6 = rgw::IAM::Condition::as_network(p->statements[0].conditions[1].vals[1]); + EXPECT_TRUE(convertedIPv6.is_initialized()); + if (convertedIPv6.is_initialized()) { + EXPECT_EQ(*convertedIPv6, allowedIPv6); + } +} + +TEST_F(IPPolicyTest, EvalIPAddress) { + auto allowp = Policy(cct.get(), arbitrary_tenant, + bufferlist::static_from_string(ip_address_allow_example)); + auto denyp = Policy(cct.get(), arbitrary_tenant, + bufferlist::static_from_string(ip_address_deny_example)); + auto fullp = Policy(cct.get(), arbitrary_tenant, + bufferlist::static_from_string(ip_address_full_example)); + Environment e; + Environment allowedIP, blacklistedIP, allowedIPv6, blacklistedIPv6; + allowedIP["aws:SourceIp"] = "192.168.1.2"; + allowedIPv6["aws:SourceIp"] = "::1"; + blacklistedIP["aws:SourceIp"] = "192.168.1.1"; + blacklistedIPv6["aws:SourceIp"] = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; + + auto trueacct = FakeIdentity( + Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS")); + // Without an IP address in the environment then evaluation will always pass + EXPECT_EQ(allowp.eval(e, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + EXPECT_EQ(fullp.eval(e, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Pass); + + EXPECT_EQ(allowp.eval(allowedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Allow); + EXPECT_EQ(allowp.eval(blacklistedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + + + EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Deny); + EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Deny); + + EXPECT_EQ(denyp.eval(blacklistedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + EXPECT_EQ(denyp.eval(blacklistedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Pass); + + EXPECT_EQ(denyp.eval(blacklistedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + EXPECT_EQ(denyp.eval(blacklistedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Pass); + EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Deny); + EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Deny); + + EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Allow); + EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Allow); + + EXPECT_EQ(fullp.eval(blacklistedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + EXPECT_EQ(fullp.eval(blacklistedIP, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Pass); + + EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Allow); + EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Allow); + + EXPECT_EQ(fullp.eval(blacklistedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket")), + Effect::Pass); + EXPECT_EQ(fullp.eval(blacklistedIPv6, trueacct, s3ListBucket, + ARN(Partition::aws, Service::s3, + "", arbitrary_tenant, "example_bucket/myobject")), + Effect::Pass); +} + +string IPPolicyTest::ip_address_allow_example = R"( +{ + "Version": "2012-10-17", + "Id": "S3SimpleIPPolicyTest", + "Statement": [{ + "Sid": "1", + "Effect": "Allow", + "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]}, + "Action": "s3:ListBucket", + "Resource": [ + "arn:aws:s3:::example_bucket" + ], + "Condition": { + "IpAddress": {"aws:SourceIp": "192.168.1.0/24"} + } + }] +} +)"; + +string IPPolicyTest::ip_address_deny_example = R"( +{ + "Version": "2012-10-17", + "Id": "S3IPPolicyTest", + "Statement": { + "Effect": "Deny", + "Sid": "IPDeny", + "Action": "s3:ListBucket", + "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]}, + "Resource": [ + "arn:aws:s3:::example_bucket", + "arn:aws:s3:::example_bucket/*" + ], + "Condition": { + "NotIpAddress": {"aws:SourceIp": ["192.168.1.1/32", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]} + } + } +} +)"; + +string IPPolicyTest::ip_address_full_example = R"( +{ + "Version": "2012-10-17", + "Id": "S3IPPolicyTest", + "Statement": { + "Effect": "Allow", + "Sid": "IPAllow", + "Action": "s3:ListBucket", + "Principal": "*", + "Resource": [ + "arn:aws:s3:::example_bucket", + "arn:aws:s3:::example_bucket/*" + ], + "Condition": { + "IpAddress": {"aws:SourceIp": ["192.168.1.0/24", "::1"]}, + "NotIpAddress": {"aws:SourceIp": ["192.168.1.1/32", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]} + } + } +} +)"; + TEST(MatchWildcards, Simple) { EXPECT_TRUE(match_wildcards("", "")); -- 2.39.5