Repository: cloud-custodian/cloud-custodian

Issue:
ebs sharing cross account check



Discussion:
Related to #238


Resolving patch:
diff --git a/c7n/resources/ebs.py b/c7n/resources/ebs.py
--- a/c7n/resources/ebs.py
+++ b/c7n/resources/ebs.py
@@ -18,8 +18,8 @@
 
 from c7n.actions import ActionRegistry, BaseAction
 from c7n.filters import (
-    Filter, FilterRegistry, AgeFilter, ValueFilter, ANNOTATION_KEY,
-    FilterValidationError, OPERATORS)
+    CrossAccountAccessFilter, Filter, FilterRegistry, AgeFilter, ValueFilter,
+    ANNOTATION_KEY, FilterValidationError, OPERATORS)
 
 from c7n.manager import resources
 from c7n.resources.kms import ResourceKmsKeyAlias
@@ -108,6 +108,43 @@ def _filter_ami_snapshots(self, snapshots):
     return matches
 
 
+@Snapshot.filter_registry.register('cross-account')
+class SnapshotCrossAccountAccess(CrossAccountAccessFilter):
+
+    def process(self, resources, event=None):
+        self.accounts = self.get_accounts()
+        results = []
+        with self.executor_factory(max_workers=3) as w:
+            futures = []
+            for resource_set in chunks(resources, 50):
+                futures.append(w.submit(
+                    self.process_resource_set, resource_set))
+            for f in as_completed(futures):
+                if f.exception():
+                    self.log.error(
+                        "Exception checking cross account access \n %s" % (
+                            f.exception()))
+                    continue
+                results.extend(f.result())
+        return results
+
+    def process_resource_set(self, resource_set):
+        client = local_session(self.manager.session_factory).client('ec2')
+        results = []
+        for r in resource_set:
+            attrs = self.manager.retry(
+                client.describe_snapshot_attribute,
+                SnapshotId=r['SnapshotId'],
+                Attribute='createVolumePermission')['CreateVolumePermissions']
+            shared_accounts = {
+                g.get('Group') or g.get('UserId') for g in attrs}
+            delta_accounts = shared_accounts.difference(self.accounts)
+            if delta_accounts:
+                r['c7n:CrossAccountViolations'] = list(delta_accounts)
+                results.append(r)
+        return results
+
+
 @Snapshot.filter_registry.register('skip-ami-snapshots')
 class SnapshotSkipAmiSnapshots(Filter):
     """Filter to remove snapshots of AMIs from results
diff --git a/c7n/resources/rds.py b/c7n/resources/rds.py
--- a/c7n/resources/rds.py
+++ b/c7n/resources/rds.py
@@ -55,7 +55,8 @@
 from c7n.actions import (
     ActionRegistry, BaseAction, AutoTagUser, ModifyVpcSecurityGroupsAction)
 from c7n.filters import (
-    FilterRegistry, Filter, AgeFilter, OPERATORS, FilterValidationError)
+    CrossAccountAccessFilter, FilterRegistry, Filter, AgeFilter, OPERATORS,
+    FilterValidationError)
 
 import c7n.filters.vpc as net_filters
 from c7n.manager import resources
@@ -952,6 +953,43 @@ def process_resource_set(self, snaps, tag_keys):
                 ResourceName=arn, TagKeys=tag_keys)
 
 
+@RDSSnapshot.filter_registry.register('cross-account')
+class CrossAccountAccess(CrossAccountAccessFilter):
+
+    def process(self, resources, event=None):
+        self.accounts = self.get_accounts()
+        results = []
+        with self.executor_factory(max_workers=2) as w:
+            futures = []
+            for resource_set in chunks(resources, 20):
+                futures.append(w.submit(
+                    self.process_resource_set, resource_set))
+            for f in as_completed(futures):
+                if f.exception():
+                    self.log.error(
+                        "Exception checking cross account access\n %s" % (
+                            f.exception()))
+                    continue
+                results.extend(f.result())
+        return results
+
+    def process_resource_set(self, resource_set):
+        client = local_session(self.manager.session_factory).client('rds')
+        results = []
+        for r in resource_set:
+            attrs = {t['AttributeName']: t['AttributeValues']
+             for t in client.describe_db_snapshot_attributes(
+                DBSnapshotIdentifier=r['DBSnapshotIdentifier'])[
+                    'DBSnapshotAttributesResult']['DBSnapshotAttributes']}
+            r['c7n:attributes'] = attrs
+            shared_accounts = set(attrs.get('restore', []))
+            delta_accounts = shared_accounts.difference(self.accounts)
+            if delta_accounts:
+                r['c7n:CrossAccountViolations'] = list(delta_accounts)
+                results.append(r)
+        return results
+
+
 @RDSSnapshot.action_registry.register('region-copy')
 class RegionCopySnapshot(BaseAction):
     """Copy a snapshot across regions.
@@ -1056,7 +1094,7 @@ def process_resource_set(self, resource_set):
                 # we need to augment the tag set with the original
                 # resource tags to preserve the common case.
                 rtags = tags and list(tags) or None
-                if tags:
+                if tags and self.data.get('copy_tags', True):
                     rtags.extend(r['Tags'])
                 self.process_resource(target_client, target_key, rtags, r)
 


Test patch:
diff --git a/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_1.json b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_1.json
@@ -0,0 +1,19 @@
+{
+    "status_code": 200, 
+    "data": {
+        "SnapshotId": "snap-07459fee", 
+        "CreateVolumePermissions": [], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "c281f9f9-682b-4ffa-8e0b-2f76dc2757d0", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_2.json b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_2.json
@@ -0,0 +1,26 @@
+{
+    "status_code": 200, 
+    "data": {
+        "SnapshotId": "snap-7f9496cf", 
+        "CreateVolumePermissions": [
+            {
+                "UserId": "619193117841"
+            }, 
+            {
+                "UserId": "644160558196"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "5c45b547-ba2c-46d1-b9fe-6063f4b14463", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_3.json b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_3.json
@@ -0,0 +1,23 @@
+{
+    "status_code": 200, 
+    "data": {
+        "SnapshotId": "snap-af0eb71b", 
+        "CreateVolumePermissions": [
+            {
+                "Group": "all"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "1058419e-f9a2-4eab-a9cf-f46b71795b6f", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_4.json b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_4.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshotAttribute_4.json
@@ -0,0 +1,23 @@
+{
+    "status_code": 200, 
+    "data": {
+        "SnapshotId": "snap-08d7898add8b041a9", 
+        "CreateVolumePermissions": [
+            {
+                "UserId": "644160558196"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "cd786d25-d366-4026-8e30-f6f66986b40b", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshots_1.json b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshots_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/ec2.DescribeSnapshots_1.json
@@ -0,0 +1,105 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "43a42943-3cb9-4fb6-aa48-eca34c34bb33", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT"
+            }
+        }, 
+        "Snapshots": [
+            {
+                "Description": "Created by CreateImage(i-0ea09b4cbf12b50f2) for ami-e0fba8f7 from vol-48e2119c", 
+                "Encrypted": false, 
+                "VolumeId": "vol-48e2119c", 
+                "State": "completed", 
+                "VolumeSize": 8, 
+                "Progress": "100%", 
+                "StartTime": {
+                    "hour": 15, 
+                    "__class__": "datetime", 
+                    "month": 10, 
+                    "second": 24, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 21, 
+                    "minute": 38
+                }, 
+                "SnapshotId": "snap-07459fee", 
+                "OwnerId": "644160558196"
+            }, 
+            {
+                "Description": "", 
+                "Tags": [
+                    {
+                        "Value": "c7n-ebs-test-snapshot-01", 
+                        "Key": "Name"
+                    }
+                ], 
+                "Encrypted": false, 
+                "VolumeId": "vol-c5eaa459", 
+                "State": "completed", 
+                "VolumeSize": 1, 
+                "Progress": "100%", 
+                "StartTime": {
+                    "hour": 20, 
+                    "__class__": "datetime", 
+                    "month": 11, 
+                    "second": 44, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 16, 
+                    "minute": 5
+                }, 
+                "SnapshotId": "snap-7f9496cf", 
+                "OwnerId": "644160558196"
+            }, 
+            {
+                "Description": "Created by CreateImage(i-0ea09b4cbf12b50f2) for ami-04e2b113 from vol-48e2119c", 
+                "Encrypted": false, 
+                "VolumeId": "vol-48e2119c", 
+                "State": "completed", 
+                "VolumeSize": 8, 
+                "Progress": "100%", 
+                "StartTime": {
+                    "hour": 17, 
+                    "__class__": "datetime", 
+                    "month": 10, 
+                    "second": 2, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 21, 
+                    "minute": 0
+                }, 
+                "SnapshotId": "snap-af0eb71b", 
+                "OwnerId": "644160558196"
+            }, 
+            {
+                "Description": "", 
+                "Encrypted": false, 
+                "VolumeId": "vol-06eb669249151eaf1", 
+                "State": "completed", 
+                "VolumeSize": 1, 
+                "Progress": "100%", 
+                "StartTime": {
+                    "hour": 17, 
+                    "__class__": "datetime", 
+                    "month": 1, 
+                    "second": 30, 
+                    "microsecond": 0, 
+                    "year": 2017, 
+                    "day": 1, 
+                    "minute": 46
+                }, 
+                "SnapshotId": "snap-08d7898add8b041a9", 
+                "OwnerId": "644160558196"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ebs_cross_account/iam.ListRoles_1.json b/tests/data/placebo/test_ebs_cross_account/iam.ListRoles_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ebs_cross_account/iam.ListRoles_1.json
@@ -0,0 +1,37 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Marker": "ADL4OC71wUA21skFxadjblEnamPw60IL+/GIGM6srA+zUQh5Q5UgBPA2ec9dP3PNcMXzn7ITSWE0PS++oq5YfSA5", 
+        "IsTruncated": true, 
+        "Roles": [
+            {
+                "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22quicksight.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D", 
+                "RoleId": "AROAJ7VIQKM2MU4RUFTLO", 
+                "CreateDate": {
+                    "hour": 13, 
+                    "__class__": "datetime", 
+                    "month": 11, 
+                    "second": 3, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 27, 
+                    "minute": 42
+                }, 
+                "RoleName": "aws-quicksight-service-role-v0", 
+                "Path": "/service-role/", 
+                "Arn": "arn:aws:iam::644160558196:role/service-role/aws-quicksight-service-role-v0"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "535dd8f4-d04c-11e6-822d-810deb780f00", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "535dd8f4-d04c-11e6-822d-810deb780f00", 
+                "date": "Sun, 01 Jan 2017 18:01:31 GMT", 
+                "content-length": "1004", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_1.json b/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_1.json
@@ -0,0 +1,37 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Marker": "AC0vQRb4QpH4GnM1bvXL+X3UJgd6mdgDJEaP4fQLWc+RNFItvILRfiJYRiKUQcHnnG5lz9QybRf8Vf7vpzk+9IYu", 
+        "IsTruncated": true, 
+        "Roles": [
+            {
+                "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22quicksight.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D", 
+                "RoleId": "AROAJ7VIQKM2MU4RUFTLO", 
+                "CreateDate": {
+                    "hour": 13, 
+                    "__class__": "datetime", 
+                    "month": 11, 
+                    "second": 3, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 27, 
+                    "minute": 42
+                }, 
+                "RoleName": "aws-quicksight-service-role-v0", 
+                "Path": "/service-role/", 
+                "Arn": "arn:aws:iam::644160558196:role/service-role/aws-quicksight-service-role-v0"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "3390a56b-d04f-11e6-90de-9712e9ed93cb", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "3390a56b-d04f-11e6-90de-9712e9ed93cb", 
+                "date": "Sun, 01 Jan 2017 18:22:05 GMT", 
+                "content-length": "1004", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_2.json b/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/iam.ListRoles_2.json
@@ -0,0 +1,37 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Marker": "ADE6JNk8Ntq+ZBOEsGByH1KCxiaRrK0/MNm2a3B2NnnIrWVGsVpUrU5TK85L0OveqvtXl5mhFEvy5XQBy6Eeao4s", 
+        "IsTruncated": true, 
+        "Roles": [
+            {
+                "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22quicksight.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D", 
+                "RoleId": "AROAJ7VIQKM2MU4RUFTLO", 
+                "CreateDate": {
+                    "hour": 13, 
+                    "__class__": "datetime", 
+                    "month": 11, 
+                    "second": 3, 
+                    "microsecond": 0, 
+                    "year": 2016, 
+                    "day": 27, 
+                    "minute": 42
+                }, 
+                "RoleName": "aws-quicksight-service-role-v0", 
+                "Path": "/service-role/", 
+                "Arn": "arn:aws:iam::644160558196:role/service-role/aws-quicksight-service-role-v0"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "340b05d4-d04f-11e6-8b97-19e449c5bc70", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "340b05d4-d04f-11e6-8b97-19e449c5bc70", 
+                "date": "Sun, 01 Jan 2017 18:22:06 GMT", 
+                "content-length": "1004", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_1.json b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_1.json
@@ -0,0 +1,25 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBSnapshotAttributesResult": {
+            "DBSnapshotIdentifier": "rds:tidx-2016-12-31-23-16", 
+            "DBSnapshotAttributes": [
+                {
+                    "AttributeName": "restore", 
+                    "AttributeValues": []
+                }
+            ]
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "341b31d6-d04f-11e6-b659-ed6e259ccfdb", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "341b31d6-d04f-11e6-b659-ed6e259ccfdb", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "656", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_2.json b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_2.json
@@ -0,0 +1,25 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBSnapshotAttributesResult": {
+            "DBSnapshotIdentifier": "rds:tidx-2017-01-01-07-59", 
+            "DBSnapshotAttributes": [
+                {
+                    "AttributeName": "restore", 
+                    "AttributeValues": []
+                }
+            ]
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "3425e038-d04f-11e6-b659-ed6e259ccfdb", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "3425e038-d04f-11e6-b659-ed6e259ccfdb", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "656", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_3.json b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_3.json
@@ -0,0 +1,27 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBSnapshotAttributesResult": {
+            "DBSnapshotIdentifier": "tidx-pub", 
+            "DBSnapshotAttributes": [
+                {
+                    "AttributeName": "restore", 
+                    "AttributeValues": [
+                        "all"
+                    ]
+                }
+            ]
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "342fcb50-d04f-11e6-b659-ed6e259ccfdb", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "342fcb50-d04f-11e6-b659-ed6e259ccfdb", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "716", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_4.json b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_4.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshotAttributes_4.json
@@ -0,0 +1,27 @@
+{
+    "status_code": 200, 
+    "data": {
+        "DBSnapshotAttributesResult": {
+            "DBSnapshotIdentifier": "tidx-rdx", 
+            "DBSnapshotAttributes": [
+                {
+                    "AttributeName": "restore", 
+                    "AttributeValues": [
+                        "619193117841"
+                    ]
+                }
+            ]
+        }, 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "343b8b28-d04f-11e6-b659-ed6e259ccfdb", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "343b8b28-d04f-11e6-b659-ed6e259ccfdb", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "725", 
+                "content-type": "text/xml"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshots_1.json b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshots_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.DescribeDBSnapshots_1.json
@@ -0,0 +1,177 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "3360951d-d04f-11e6-a7d6-33920eb31e01", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "3360951d-d04f-11e6-a7d6-33920eb31e01", 
+                "vary": "Accept-Encoding", 
+                "content-length": "4975", 
+                "content-type": "text/xml", 
+                "date": "Sun, 01 Jan 2017 18:22:06 GMT"
+            }
+        }, 
+        "DBSnapshots": [
+            {
+                "Engine": "postgres", 
+                "SnapshotCreateTime": {
+                    "hour": 23, 
+                    "__class__": "datetime", 
+                    "month": 12, 
+                    "second": 55, 
+                    "microsecond": 938000, 
+                    "year": 2016, 
+                    "day": 31, 
+                    "minute": 17
+                }, 
+                "AvailabilityZone": "us-east-1a", 
+                "DBSnapshotArn": "arn:aws:rds:us-east-1:644160558196:snapshot:rds:tidx-2016-12-31-23-16", 
+                "PercentProgress": 100, 
+                "MasterUsername": "sinterclaus", 
+                "Encrypted": false, 
+                "LicenseModel": "postgresql-license", 
+                "StorageType": "gp2", 
+                "Status": "available", 
+                "VpcId": "vpc-d2d616b5", 
+                "DBSnapshotIdentifier": "rds:tidx-2016-12-31-23-16", 
+                "InstanceCreateTime": {
+                    "hour": 23, 
+                    "__class__": "datetime", 
+                    "month": 12, 
+                    "second": 53, 
+                    "microsecond": 306000, 
+                    "year": 2016, 
+                    "day": 31, 
+                    "minute": 16
+                }, 
+                "OptionGroupName": "default:postgres-9-5", 
+                "AllocatedStorage": 5, 
+                "EngineVersion": "9.5.4", 
+                "SnapshotType": "automated", 
+                "Port": 5432, 
+                "DBInstanceIdentifier": "tidx"
+            }, 
+            {
+                "Engine": "postgres", 
+                "SnapshotCreateTime": {
+                    "hour": 7, 
+                    "__class__": "datetime", 
+                    "month": 1, 
+                    "second": 37, 
+                    "microsecond": 779000, 
+                    "year": 2017, 
+                    "day": 1, 
+                    "minute": 59
+                }, 
+                "AvailabilityZone": "us-east-1a", 
+                "DBSnapshotArn": "arn:aws:rds:us-east-1:644160558196:snapshot:rds:tidx-2017-01-01-07-59", 
+                "PercentProgress": 100, 
+                "MasterUsername": "sinterclaus", 
+                "Encrypted": false, 
+                "LicenseModel": "postgresql-license", 
+                "StorageType": "gp2", 
+                "Status": "available", 
+                "VpcId": "vpc-d2d616b5", 
+                "DBSnapshotIdentifier": "rds:tidx-2017-01-01-07-59", 
+                "InstanceCreateTime": {
+                    "hour": 23, 
+                    "__class__": "datetime", 
+                    "month": 12, 
+                    "second": 53, 
+                    "microsecond": 306000, 
+                    "year": 2016, 
+                    "day": 31, 
+                    "minute": 16
+                }, 
+                "OptionGroupName": "default:postgres-9-5", 
+                "AllocatedStorage": 5, 
+                "EngineVersion": "9.5.4", 
+                "SnapshotType": "automated", 
+                "Port": 5432, 
+                "DBInstanceIdentifier": "tidx"
+            }, 
+            {
+                "Engine": "postgres", 
+                "SnapshotCreateTime": {
+                    "hour": 8, 
+                    "__class__": "datetime", 
+                    "month": 1, 
+                    "second": 18, 
+                    "microsecond": 607000, 
+                    "year": 2017, 
+                    "day": 1, 
+                    "minute": 41
+                }, 
+                "AvailabilityZone": "us-east-1a", 
+                "DBSnapshotArn": "arn:aws:rds:us-east-1:644160558196:snapshot:tidx-pub", 
+                "PercentProgress": 100, 
+                "MasterUsername": "sinterclaus", 
+                "Encrypted": false, 
+                "LicenseModel": "postgresql-license", 
+                "StorageType": "gp2", 
+                "Status": "available", 
+                "VpcId": "vpc-d2d616b5", 
+                "DBSnapshotIdentifier": "tidx-pub", 
+                "InstanceCreateTime": {
+                    "hour": 23, 
+                    "__class__": "datetime", 
+                    "month": 12, 
+                    "second": 53, 
+                    "microsecond": 306000, 
+                    "year": 2016, 
+                    "day": 31, 
+                    "minute": 16
+                }, 
+                "OptionGroupName": "default:postgres-9-5", 
+                "AllocatedStorage": 5, 
+                "EngineVersion": "9.5.4", 
+                "SnapshotType": "manual", 
+                "Port": 5432, 
+                "DBInstanceIdentifier": "tidx"
+            }, 
+            {
+                "Engine": "postgres", 
+                "SnapshotCreateTime": {
+                    "hour": 8, 
+                    "__class__": "datetime", 
+                    "month": 1, 
+                    "second": 9, 
+                    "microsecond": 839000, 
+                    "year": 2017, 
+                    "day": 1, 
+                    "minute": 43
+                }, 
+                "AvailabilityZone": "us-east-1a", 
+                "SourceRegion": "us-east-1", 
+                "PercentProgress": 100, 
+                "MasterUsername": "sinterclaus", 
+                "Encrypted": false, 
+                "LicenseModel": "postgresql-license", 
+                "StorageType": "gp2", 
+                "Status": "available", 
+                "VpcId": "vpc-d2d616b5", 
+                "SourceDBSnapshotIdentifier": "arn:aws:rds:us-east-1:644160558196:snapshot:rds:tidx-2017-01-01-07-59", 
+                "DBSnapshotIdentifier": "tidx-rdx", 
+                "InstanceCreateTime": {
+                    "hour": 23, 
+                    "__class__": "datetime", 
+                    "month": 12, 
+                    "second": 53, 
+                    "microsecond": 306000, 
+                    "year": 2016, 
+                    "day": 31, 
+                    "minute": 16
+                }, 
+                "OptionGroupName": "default:postgres-9-5", 
+                "AllocatedStorage": 5, 
+                "EngineVersion": "9.5.4", 
+                "SnapshotType": "manual", 
+                "DBSnapshotArn": "arn:aws:rds:us-east-1:644160558196:snapshot:tidx-rdx", 
+                "Port": 5432, 
+                "DBInstanceIdentifier": "tidx"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_1.json b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_1.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "33a00e61-d04f-11e6-8cc4-13b41fcd3561", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "33a00e61-d04f-11e6-8cc4-13b41fcd3561", 
+                "date": "Sun, 01 Jan 2017 18:22:06 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_2.json b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_2.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "33c2ff77-d04f-11e6-a7d6-33920eb31e01", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "33c2ff77-d04f-11e6-a7d6-33920eb31e01", 
+                "date": "Sun, 01 Jan 2017 18:22:06 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_3.json b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_3.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "33d91f8a-d04f-11e6-a7d6-33920eb31e01", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "33d91f8a-d04f-11e6-a7d6-33920eb31e01", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_4.json b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_4.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_rds_snapshot_access/rds.ListTagsForResource_4.json
@@ -0,0 +1,17 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "33eea447-d04f-11e6-9c6c-d1e739b07df7", 
+            "HTTPHeaders": {
+                "x-amzn-requestid": "33eea447-d04f-11e6-9c6c-d1e739b07df7", 
+                "date": "Sun, 01 Jan 2017 18:22:07 GMT", 
+                "content-length": "293", 
+                "content-type": "text/xml"
+            }
+        }, 
+        "TagList": []
+    }
+}
\ No newline at end of file
diff --git a/tests/test_ebs.py b/tests/test_ebs.py
--- a/tests/test_ebs.py
+++ b/tests/test_ebs.py
@@ -24,11 +24,34 @@
 logging.basicConfig(level=logging.DEBUG)
 
 
+class SnapshotAccessTest(BaseTest):
+
+    def test_snapshot_access(self):
+        # pre conditions, 2 snapshots one shared to a separate account, and one
+        # shared publicly. 2 non matching volumes, one not shared, one shared
+        # explicitly to its own account.
+        self.patch(CopySnapshot, 'executor_factory', MainThreadExecutor)
+        factory = self.replay_flight_data('test_ebs_cross_account')
+        p = self.load_policy({
+            'name': 'snap-copy',
+            'resource': 'ebs-snapshot',
+            'filters': ['cross-account'],
+            }, session_factory=factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 2)
+        self.assertEqual(
+            {r['SnapshotId']: r['c7n:CrossAccountViolations']
+             for r in resources},
+            {'snap-7f9496cf': ['619193117841'],
+             'snap-af0eb71b': ['all']})
+
+
 class SnapshotCopyTest(BaseTest):
 
     def test_snapshot_copy(self):
         self.patch(CopySnapshot, 'executor_factory', MainThreadExecutor)
-        # DEFAULT_REGION needs to be set to west for recording
+        self.change_environment(AWS_DEFAULT_REGION='us-west-2')
+
         factory = self.replay_flight_data('test_ebs_snapshot_copy')
         p = self.load_policy({
             'name': 'snap-copy',
@@ -41,16 +64,13 @@ def test_snapshot_copy(self):
                  'target_key': '82645407-2faa-4d93-be71-7d6a8d59a5fc'}]
             }, session_factory=factory)
         resources = p.run()
-        # If test region is target region aka us-east-1, then the action
-        # skips, and so does the test
-        if factory().region_name == 'us-east-1':
-            return
 
         self.assertEqual(len(resources), 1)
         client = factory(region="us-east-1").client('ec2')
         tags = client.describe_tags(
             Filters=[{'Name': 'resource-id',
-                       'Values': [resources[0]['CopiedSnapshot']]}])['Tags']
+                      'Values': [resources[0][
+                          'c7n:CopiedSnapshot']]}])['Tags']
         tags = {t['Key']: t['Value'] for t in tags}
         self.assertEqual(tags['ASV'], 'RoadKill')
 
@@ -61,7 +81,7 @@ def test_snapshot_ami_snapshot_filter(self):
         self.patch(CopySnapshot, 'executor_factory', MainThreadExecutor)
         # DEFAULT_REGION needs to be set to west for recording
         factory = self.replay_flight_data('test_ebs_ami_snapshot_filter')
-        
+
         #first case should return only resources that are ami snapshots
         p = self.load_policy({
             'name': 'ami-snap-filter',
@@ -72,7 +92,7 @@ def test_snapshot_ami_snapshot_filter(self):
             }, session_factory=factory)
         resources = p.run()
         self.assertEqual(len(resources), 3)
-        
+
         #second case should return resources that are NOT ami snapshots
         policy = self.load_policy({
             'name': 'non-ami-snap-filter',
@@ -84,7 +104,7 @@ def test_snapshot_ami_snapshot_filter(self):
         resources = policy.run()
         self.assertEqual(len(resources), 2)
 
-        
+
 class SnapshotTrimTest(BaseTest):
 
     def test_snapshot_trim(self):
diff --git a/tests/test_rds.py b/tests/test_rds.py
--- a/tests/test_rds.py
+++ b/tests/test_rds.py
@@ -411,6 +411,20 @@ def test_rds_db_instance_eligible_for_final_snapshot(self):
 
 class RDSSnapshotTest(BaseTest):
 
+    def test_rds_snapshot_access(self):
+        factory = self.replay_flight_data('test_rds_snapshot_access')
+        p = self.load_policy({
+            'name': 'rds-snap-access',
+            'resource': 'rds-snapshot',
+            'filters': ['cross-account'],
+            }, session_factory=factory)
+        resources = p.run()
+        self.assertEqual(len(resources), 2)
+        self.assertEqual(
+            {r['DBSnapshotIdentifier']: r['c7n:CrossAccountViolations']
+             for r in resources},
+            {'tidx-pub': ['all'], 'tidx-rdx': ['619193117841']})
+
     def test_rds_latest_manual(self):
         # preconditions
         # one db with manual and automatic snapshots