diff --git a/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED.py b/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED.py new file mode 100644 index 000000000..5e99eb212 --- /dev/null +++ b/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED.py @@ -0,0 +1,66 @@ +# Copyright 2017-2019 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file 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. +''' +##################################### +## Gherkin ## +##################################### +Rule Name: + AMI_EBS_ENCRYPTED + +Description: + Check EBS-backed AMI is encrypted. + +Rationale: + Encrypting on EBS ensure that no data is written on disk in clear text. + +Indicative Severity: + Medium + +Trigger: + Periodic + +Reports on: + AWS::::Account + +Scenarios: + Scenario: 1 + Given: AMI target EBS is encrypted + Then: Return COMPLIANT + Scenario: 2 + Given: AMI target EBS is not encrypted + Then: Return NON_COMPLIANT +''' + +from rdklib import Evaluator, Evaluation, ConfigRule, ComplianceType + +DEFAULT_RESOURCE_TYPE = 'AWS::::Account' + +class AMI_EBS_ENCRYPTED(ConfigRule): + + def evaluate_periodic(self, event, client_factory, valid_rule_parameters): + ec2_client = client_factory.build_client('ec2') + evaluations = [] + amis = ec2_client.describe_images(Owners=["self"]) + for ami in amis.get('Images'): + block_devices = ami.get('BlockDeviceMappings') + for block_device in block_devices: + ebs = block_device.get('Ebs') + if ebs and ebs['Encrypted']: + evaluations.append(Evaluation(ComplianceType.COMPLIANT, ami.get('ImageId'), DEFAULT_RESOURCE_TYPE)) + else: + evaluations.append(Evaluation(ComplianceType.NON_COMPLIANT, ami.get('ImageId'), DEFAULT_RESOURCE_TYPE, + annotation="AMI target EBS is not encrypted")) + return evaluations + +def lambda_handler(event, context): + my_rule = AMI_EBS_ENCRYPTED() + evaluator = Evaluator(my_rule) + return evaluator.handle(event, context) diff --git a/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED_test.py b/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED_test.py new file mode 100644 index 000000000..e9d254484 --- /dev/null +++ b/python/AMI_EBS_ENCRYPTED/AMI_EBS_ENCRYPTED_test.py @@ -0,0 +1,69 @@ +# Copyright 2017-2019 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file 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. + +import unittest +from mock import patch, MagicMock +from rdklib import Evaluation, ComplianceType +import rdklibtest + +RESOURCE_TYPE = 'AWS::::Account' + +MODULE = __import__('AMI_EBS_ENCRYPTED') +RULE = MODULE.AMI_EBS_ENCRYPTED() + +CLIENT_FACTORY = MagicMock() +EC2_CLIENT_MOCK = MagicMock() + +def mock_get_client(client_name, *args, **kwargs): + if client_name == 'ec2': + return EC2_CLIENT_MOCK + raise Exception("Attempting to create an unknown client") + +@patch.object(CLIENT_FACTORY, 'build_client', MagicMock(side_effect=mock_get_client)) +class ComplianceTest(unittest.TestCase): + + non_encrypted_ami_ebs = { + "Images": [{ + "ImageId": "ami-0112c52e7ca89ee2e", + "BlockDeviceMappings": [{ + "Ebs": { + "Encrypted": False + } + }] + }] + } + encrypted_ami_ebs = { + "Images": [{ + "ImageId": "ami-08d7261cad6c06507", + "BlockDeviceMappings": [{ + "Ebs": { + "Encrypted": True + } + }] + }] + } + + def test_scenario1_amitargetebsencrypted_returnscompliant(self): + EC2_CLIENT_MOCK.describe_images.return_value = self.encrypted_ami_ebs + response = RULE.evaluate_periodic("", CLIENT_FACTORY, "") + resp_expected = [ + Evaluation(ComplianceType.COMPLIANT, "ami-08d7261cad6c06507", RESOURCE_TYPE) + ] + rdklibtest.assert_successful_evaluation(self, response, resp_expected) + + def test_scenario2_amitargetebsnotencrypted_returnsnoncompliant(self): + EC2_CLIENT_MOCK.describe_images.return_value = self.non_encrypted_ami_ebs + response = RULE.evaluate_periodic("", CLIENT_FACTORY, "") + resp_expected = [ + Evaluation(ComplianceType.NON_COMPLIANT, "ami-0112c52e7ca89ee2e", RESOURCE_TYPE, + annotation="AMI target EBS is not encrypted") + ] + rdklibtest.assert_successful_evaluation(self, response, resp_expected, 1) diff --git a/python/AMI_EBS_ENCRYPTED/parameters.json b/python/AMI_EBS_ENCRYPTED/parameters.json new file mode 100644 index 000000000..0509f443f --- /dev/null +++ b/python/AMI_EBS_ENCRYPTED/parameters.json @@ -0,0 +1,13 @@ +{ + "Version": "1.0", + "Parameters": { + "RuleName": "AMI_EBS_ENCRYPTED", + "SourceRuntime": "python3.6-lib", + "SourcePeriodic": "One_Hour", + "CodeKey": "AMI_EBS_ENCRYPTED.zip", + "InputParameters": "{}", + "OptionalParameters": "{}", + "SourceEvents": "AWS::EC2::Volume" + }, + "Tags": "[]" +}