Repository: cloud-custodian/cloud-custodian

Issue:
aws - glue catalog issues
### Describe the feature

1. The resources.json for a `glue-catalog` policy contains KeyId if a user uses the KeyId to apply encryption. We're trying to avoid overwriting the encryption setting using a key alias because it was found to break cross account glue catalog pattern. Apparently, the alias could resolve to a different key that could potentially cause usage of multiple keys re: encryption at rest
```
[
  {
    "DataCatalogEncryptionSettings": {
      "EncryptionAtRest": {
        "CatalogEncryptionMode": "SSE-KMS",
        "SseAwsKmsKeyId": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2"
      },
      "ConnectionPasswordEncryption": {
        "ReturnConnectionPasswordEncrypted": true,
        "AwsKmsKeyId": "arn:aws:kms:us-east-1:644160558196:key/358f7699-4ea5-455a-9c78-1c868301e5a8"
      }
    },
    "CatalogId": "644160558196"
  }
]
```
2. When the [put_catalog_encryption](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/glue/client/put_data_catalog_encryption_settings.html) api is used to modify one encryption config (Rest or Connection) it nullifies the other existing setting

### Extra information or context

_No response_


Discussion:

Resolving patch:
diff --git a/c7n/resources/glue.py b/c7n/resources/glue.py
--- a/c7n/resources/glue.py
+++ b/c7n/resources/glue.py
@@ -607,6 +607,32 @@ def get_resources(self, resource_ids):
         return [{'CatalogId': self.config.account_id}]
 
 
+@GlueDataCatalog.filter_registry.register('kms-key')
+class GlueCatalogKmsFilter(KmsRelatedFilter):
+
+    schema = type_schema(
+        'kms-key',
+        rinherit=ValueFilter.schema,
+        **{'key-type': {'type': 'string', 'enum': [
+            'EncryptionAtRest', 'ConnectionPasswordEncryption']},
+            'required': ['key-type'],
+            'match-resource': {'type': 'boolean'},
+            'operator': {'enum': ['and', 'or']}})
+
+    permissions = ('glue:GetDataCatalogEncryptionSettings',)
+
+    RelatedIdsExpression = ''
+
+    def __init__(self, data, manager=None):
+        super().__init__(data, manager)
+        key_type_to_related_ids = {
+            'EncryptionAtRest': 'DataCatalogEncryptionSettings.EncryptionAtRest.SseAwsKmsKeyId',
+            'ConnectionPasswordEncryption':
+              'DataCatalogEncryptionSettings.ConnectionPasswordEncryption.AwsKmsKeyId'
+        }
+        self.RelatedIdsExpression = key_type_to_related_ids.get(self.data.get('key-type'))
+
+
 @GlueDataCatalog.action_registry.register('set-encryption')
 class GlueDataCatalogEncryption(BaseAction):
     """Modifies glue data catalog encryption based on specified parameter
@@ -666,9 +692,18 @@ class GlueDataCatalogEncryption(BaseAction):
 
     def process(self, resources):
         client = local_session(self.manager.session_factory).client('glue')
+        self.process_catalog_encryption(client, resources)
+
+    def process_catalog_encryption(self, client, resources):
         # there is one glue data catalog per account
+        if 'DataCatalogEncryptionSettings' not in resources[0]:
+            resources = self.manager.resources()
+        enc_config = resources[0]['DataCatalogEncryptionSettings']
+        updated_config = {**enc_config, **self.data['attributes']}
+        if enc_config == updated_config:
+            return
         client.put_data_catalog_encryption_settings(
-            DataCatalogEncryptionSettings=self.data['attributes'])
+            DataCatalogEncryptionSettings=updated_config)
 
 
 @GlueDataCatalog.filter_registry.register('glue-security-config')


Test patch:
diff --git a/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json
--- a/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json
+++ b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_2.json
@@ -3,8 +3,7 @@
     "data": {
         "DataCatalogEncryptionSettings": {
             "EncryptionAtRest": {
-                "CatalogEncryptionMode": "SSE-KMS",
-                "SseAwsKmsKeyId": "alias/aws/glue"
+                "CatalogEncryptionMode": "DISABLED"
             },
             "ConnectionPasswordEncryption": {
                 "ReturnConnectionPasswordEncrypted": false
diff --git a/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_3.json b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_catalog_change_encryption_event/glue.GetDataCatalogEncryptionSettings_3.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "SSE-KMS",
+                "SseAwsKmsKeyId": "alias/aws/glue"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": false
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_1.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_1.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "SSE-KMS",
+                "SseAwsKmsKeyId": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": false
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_2.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_2.json
@@ -0,0 +1,15 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "SSE-KMS",
+                "SseAwsKmsKeyId": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": false
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_3.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_3.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.GetDataCatalogEncryptionSettings_3.json
@@ -0,0 +1,16 @@
+{
+    "status_code": 200,
+    "data": {
+        "DataCatalogEncryptionSettings": {
+            "EncryptionAtRest": {
+                "CatalogEncryptionMode": "SSE-KMS",
+                "SseAwsKmsKeyId": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2"
+            },
+            "ConnectionPasswordEncryption": {
+                "ReturnConnectionPasswordEncrypted": true,
+                "AwsKmsKeyId": "alias/skunk/glue/encrypted"
+            }
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.PutDataCatalogEncryptionSettings_1.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.PutDataCatalogEncryptionSettings_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/glue.PutDataCatalogEncryptionSettings_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.DescribeKey_1.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.DescribeKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "7d9388c0-8c78-4acb-ad3c-b9d1764522b2",
+            "Arn": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2021,
+                "month": 4,
+                "day": 8,
+                "hour": 16,
+                "minute": 3,
+                "second": 41,
+                "microsecond": 245000
+            },
+            "Enabled": true,
+            "Description": "",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_1.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_1.json
@@ -0,0 +1,34 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/data-sync-kms-key",
+                "AliasArn": "arn:aws:kms:us-east-1:644160558196:alias/data-sync-kms-key",
+                "TargetKeyId": "7d9388c0-8c78-4acb-ad3c-b9d1764522b2",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2021,
+                    "month": 4,
+                    "day": 8,
+                    "hour": 16,
+                    "minute": 3,
+                    "second": 41,
+                    "microsecond": 753000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2021,
+                    "month": 4,
+                    "day": 8,
+                    "hour": 16,
+                    "minute": 3,
+                    "second": 41,
+                    "microsecond": 753000
+                }
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_2.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/kms.ListAliases_2.json
@@ -0,0 +1,59 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/alias/skunk/glue/encrypted",
+                "AliasArn": "arn:aws:kms:us-east-1:644160558196:alias/alias/skunk/glue/encrypted",
+                "TargetKeyId": "358f7699-4ea5-455a-9c78-1c868301e5a8",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2019,
+                    "month": 4,
+                    "day": 9,
+                    "hour": 16,
+                    "minute": 14,
+                    "second": 58,
+                    "microsecond": 29000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2019,
+                    "month": 4,
+                    "day": 9,
+                    "hour": 16,
+                    "minute": 14,
+                    "second": 58,
+                    "microsecond": 29000
+                }
+            },
+            {
+                "AliasName": "alias/data-sync-kms-key",
+                "AliasArn": "arn:aws:kms:us-east-1:644160558196:alias/data-sync-kms-key",
+                "TargetKeyId": "7d9388c0-8c78-4acb-ad3c-b9d1764522b2",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2021,
+                    "month": 4,
+                    "day": 8,
+                    "hour": 16,
+                    "minute": 3,
+                    "second": 41,
+                    "microsecond": 753000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2021,
+                    "month": 4,
+                    "day": 8,
+                    "hour": 16,
+                    "minute": 3,
+                    "second": 41,
+                    "microsecond": 753000
+                }
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/tagging.GetResources_1.json b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_glue_datacat_key_alias_apply_enc/tagging.GetResources_1.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2",
+                "Tags": [
+                    {
+                        "Key": "c7n",
+                        "Value": "test"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_glue.py b/tests/test_glue.py
--- a/tests/test_glue.py
+++ b/tests/test_glue.py
@@ -619,6 +619,53 @@ def test_glue_datacat_put_encryption(self):
             'EncryptionAtRest'),
             {'CatalogEncryptionMode': 'SSE-KMS', 'SseAwsKmsKeyId': 'alias/skunk/glue/encrypted'})
 
+    def test_glue_datacat_key_alias_apply_enc(self):
+        session_factory = self.replay_flight_data("test_glue_datacat_key_alias_apply_enc")
+        c = session_factory().client('kms')
+        key_id='arn:aws:kms:us-east-1:644160558196:key/7d9388c0-8c78-4acb-ad3c-b9d1764522b2'
+        key_alias = c.list_aliases(KeyId=key_id)
+        self.assertEqual(key_alias.get('Aliases')[0].get('AliasName'), 'alias/data-sync-kms-key')
+        client = session_factory().client('glue')
+        enc_setting = client.get_data_catalog_encryption_settings()
+        self.assertEqual(enc_setting.get('DataCatalogEncryptionSettings').get(
+            'EncryptionAtRest').get('SseAwsKmsKeyId'), key_id)
+        self.assertEqual(enc_setting.get('DataCatalogEncryptionSettings').get(
+            'ConnectionPasswordEncryption').get(
+              'ReturnConnectionPasswordEncrypted'), False)
+        p = self.load_policy(
+            {
+                'name': 'glue-catalog-encryption',
+                "resource": 'glue-catalog',
+                'filters': [{
+                    'type': 'kms-key',
+                    'key-type': 'EncryptionAtRest',
+                    'key': 'c7n:AliasName',
+                    'value': 'alias/data-sync-kms-key',
+                    'op': 'eq'},
+                ],
+                'actions': [{
+                    'type': 'set-encryption',
+                    'attributes': {
+                        'ConnectionPasswordEncryption': {
+                            'ReturnConnectionPasswordEncrypted': True,
+                            'AwsKmsKeyId': 'alias/skunk/glue/encrypted'},
+                    },
+                }]
+            },
+            session_factory=session_factory,)
+
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        client = session_factory().client("glue")
+        datacatlog = client.get_data_catalog_encryption_settings()
+        self.assertEqual(datacatlog.get('DataCatalogEncryptionSettings').get(
+            'EncryptionAtRest'),
+            {'CatalogEncryptionMode': 'SSE-KMS', 'SseAwsKmsKeyId': key_id})
+        self.assertEqual(datacatlog.get('DataCatalogEncryptionSettings').get(
+            'ConnectionPasswordEncryption'),
+            {'ReturnConnectionPasswordEncrypted': True,
+             'AwsKmsKeyId': 'alias/skunk/glue/encrypted'})
+
     def test_glue_catalog_cross_account(self):
         session_factory = self.replay_flight_data("test_glue_catalog_cross_account")
         p = self.load_policy(