AWS Security Blog
How to use AWS Config to determine compliance of AWS KMS key policies to your specifications
August 31, 2021: AWS KMS is replacing the term customer master key (CMK) with AWS KMS key and KMS key. The concept has not changed. To prevent breaking changes, AWS KMS is keeping some variations of this term. More info.
One of the top security methodologies is the principle of least privilege, which is the practice of limiting user, application, and service permissions to only those necessary to perform a function or task. In this post, I will describe how you can use AWS Config to create compliance rules that will scan AWS Key Management Service (AWS KMS) key policies to determine whether they follow your company’s guidelines for least privilege. You can use the AWS Config Rules Development Kit (RDK) on GitHub (aws-config-rdk) to quickly create and save custom AWS Config rule sets as AWS Lambda functions in your account(s), and test the rules against sample configuration items.
In this solution, you use a Gherkin syntax file, which is good method for determining the requirements of your rule, as well as how it should react to input. A Gherkin syntax file gives you the ability to create a parameter file and a feature file. The parameter file lists information like RuleName, SourceRuntime, CodeKey, InputParameters, Trigger, SourcePeriodic, and so on. The feature file includes a description of the rule, detailed information about the parameters, what the feature is meant to accomplish, and the scenarios you want to evaluate your rule against.
In this post, I will show you how to take a parameter file and feature file, and use the requirements in them to create your AWS Config rule and testing scenarios using the AWS Config RDK. I include an example key policy file for you to use for your test scenarios. You can download all the code snippets used in this post from the aws-config-aws-kms-policy-rule GitHub repository. I will explain how to use the code examples to create the AWS Config rule as a Lambda function, and how to use the AWS Config RDK to test locally and deploy the rule to your account(s).
Overview
The solution described in this post uses custom AWS Config rules to scan AWS KMS key policies every 24 hours. It checks these rules against a set of parameters that you determine beforehand to meet your organization’s security standards. Based on the rule checks, AWS Config determines the key policy to be either compliant or noncompliant when compared against your company’s specifications. Noncompliant resources are noted as such, so that an administrator can review and determine if the policy should be modified, or if it will be allowed as an exception. The following figure shows the overview of the process.
The way the process works is as follows:
- The AWS Config rule is triggered by a configuration change and scans your key policy.
- The key policy is evaluated against your custom AWS Config rule scenarios.
- The compliance results of the key policies are presented in the AWS Config console.
The files in the GitHub repository you will use for this post are:
- AWSConfigRuleKMS.feature – This is the Gherkin file used to determine the scenarios you will test against.
- AWSConfigRuleKMSPolicy.py – This file is used to create the policy formatting that the AWSConfigRuleKMS.py script uses to parse AWS KMS key policies. Because the AWS KMS key policies are JSON objects, you have to parse them before inputting them as data so you can retrieve comparison results.
- AWSConfigRuleKMS.py – This is the actual script that does the comparisons of the policies retrieved and the scenarios laid out in the Gherkin file.
- AWSConfigRuleKMS_test.py – This is the testing file that takes an example policy, runs it against the multiple test scenarios, and outputs the test results. This lets you know if your script is running as expected and producing the proper outcomes.
- parameters.json – This is the parameters file that tells AWS Config which parameters to check for in the rules.
Prerequisites
This solution has the following prerequisites:
- An AWS account.
- The AWS Config (RDK) from GitHub installed. Must have at least version 0.7.8.
- An IAM resource with permissions to create AWS Config rules and to select an AWS Config role that allows trust policy modification.
- Have an AWS KMS customer master key (CMK) alias and role ID for whitelisting and testing configurations.
- Have Python version 3.5, 3.6, or 3.7 installed.
In addition, this solution uses the following services:
Deploying the solution
From the Gherkin file in my repo, you can see you will be testing for eight different scenarios regarding least privilege access to your AWS KMS customer master keys (CMKs). I decided to whitelist any CMKs that have an alias beginning with the word “Otter*”, and any UserID that begins with “AROAOTTER*”. The parameters are set in the parameters.json file. This is how AWS Config knows what to check for in the rules. You will use these same parameters in the AWSConfigRuleKMS_test.py file when performing your tests. Be sure to modify these parameters when creating your own rules.
Scenario 1: Checks to determine if a CMK is marked DISABLED.
Scenario 2: Checks to determine if a CMK is marked ENABLED and is in the list of whitelisted CMKs.
Scenario 3: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, and has an action equal to kms:*.
Scenario 4: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, does not have kms:*, and has a policy allowing the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.
Scenario 5: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, does not have kms:*, and does not have a policy allowing the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.
Scenario 6: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Create*, kms:Delete*, and kms:Put*.
Scenario 7: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is not listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Create*, kms:Delete*, and kms:Put*.
Scenario 8: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.
You will be using the AWS Config RDK to create and test your rules, and also to deploy them to your account.
To create your AWS Config rule (RDK CLI)
- Open a terminal window.
- Use the cd command to move to the directory in which you want to create your rule.
- Use the create command to create your rule, using the following example. Replace all variables in italics with your inputs.
- You should see output similar to the following:
In your directory, you will now see the following files.
- AWSConfigRuleKMS.py: This is a skeleton file for you to create your Lambda function. It has some base code and is commented to assist you with building your functions.
- AWSConfigRuleKMS_test.py: This is a skeleton file for you to create your testing scenario script. It has some base code and helpers in place to make this easier for you.
- parameters.json: This is a parameter file, based on the inputs from the create command.
For this solution, I have already created the code snippets you will need. Download the files from my GitHub repository. Make sure to include the AWSConfigRuleKMSPolicy.py from my repository in the directory as well.
To download the code snippets from the GitHub repository (CLI)
- Open a terminal.
- Use the cd command to change to the directory where you created your AWS Config rule.
- Run the following command:
Now that you have the code snippets, you need to place them into the skeleton files to complete the rule.
To complete the Python scripts
- In a text editor, open your local copies of both AWSConfigRuleKMS.py and AWSConfigRuleKMS_test.py.
- Copy the contents of the AWSConfigRuleKMS.py from my GitHub repository.
- In the AWSConfigRuleKMS.py skeleton file, delete the code from the line import json down to and including the line above the section starting with # Helper Functions #. Paste the copied code in its place and save the file.
- Copy the contents of the AWSConfigRuleKMS_test.py from my GitHub repository.
- In the AWSConfigRuleKMS_test.py skeleton file, delete the code from the line import sys down to and including the line above the section starting with # Helper Functions #. Paste the copied code in its place and save the file.
With all the files updated, you will now use the AWS Config RDK to test the scenarios. For testing, you use the AWSConfigRuleKMS_test.py file, which is the file housing the test scenarios to ensure that your rules work as expected.
To test your AWS Config rule (RDK CLI)
- Open a terminal window.
- Use the cd command to change to the directory one level above where you created your AWS Config rule. For example, if your AWS Config rule is in C://User/Documents/Config/AWSConfigRuleKMS, then change to the C://User/Documents/Config directory.
- Use the test-local command to test your rule, using the following example:
$ rdk test-local AWSConfigRuleKMS_test - You should see output similar to the following:
If you encounter errors during testing, they could be caused by:
- Incorrect code in the AWSConfigRuleKMS.py file
- Incorrect code in the AWSConfigRuleKMS_test.py file
- Invalid formatting in the parameters.json file
- Invalid names on the files – they must match the exact formatting I have.
You should go back through your code to make sure that you used proper syntax, and that the test cases match your requirements. If you run into syntax issues, you can find the full list of AWS supported SDKs on the Tools to Build on AWS page. You can match your code formatting against the code I supplied in my GitHub repository to ensure proper spacing/tabs.
When testing is complete, deploy your AWS Config rule into your account(s). The file to deploy the rule itself is the AWSConfigRuleKMS.py file.
To deploy your AWS Config rule (RDK CLI)
- Open a terminal window.
- Use the cd command to change to the directory one level above where you created your AWS Config rule. For example, if your rule is in C://User/Documents/Config/AWSConfigRuleKMS, then change to the C://User/Documents/Config directory.
- Use the deploy command to deploy your rule, using the following example:
$ rdk deploy AWSConfigRuleKMS - You should see output similar to the following:
There are two ways you can verify that your deployment was successful. You can use either the AWS CloudFormation console, or the AWS Config console. Let’s look at it in the AWS Config console.
To verify deployment in the AWS Config console
- Open the AWS Config console.
- In the navigation pane, choose Rules.
- Choose the AWSConfigRuleKMS rule (or what you named it).
- In the Rule details section, you will see the name, trigger type, resource type, rule ARN, parameters, and status, as shown in the following screenshot.
After the deployment is complete, you must modify the AWS Config role that the AWS Config recorder assumes. Although typically this role would be AWSServiceRoleForConfig, in this solution you need to have your own AWS Config role with the Managed Policy arn:aws:iam::aws:policy/service-role/AWSConfigRole attached. The reason for this is that you need to modify the Trust Policy of the AWS Config role to trust the newly created AWS Lambda role. The AWS Lambda role ARN should look similar to the following:
To create your AWS Config recorder role in the IAM console
- Open the AWS IAM console.
- In the navigation pane, select Roles.
- At the top, choose Create Role.
- Select Config from the list of services.
- For Select your use case, select Config – Customizable.
- Choose Next: Permissions.
- Choose Next: Tags.
- Choose Next: Review.
- Enter a descriptive name for the role. For my example, I used CustomConfigRole.
- Choose Create role.
To modify the AWS Config recorder role’s trust policy
- Open the AWS IAM console.
- In the navigation pane, select Roles.
- Choose the role created by the RDK for Lambda, which is named AWSConfigRuleKMS-rdkLambdaRole-RANDOMCHARACTERS, or whatever you named it.
- Next to the Role ARN, choose the copy icon.
- Go back to the Roles screen.
- Choose the role you just created.
- Choose the Trust relationships tab.
- Choose Edit trust relationship.
- Copy the following trust policy, and paste it over the existing trust policy. (You need to change the AWS Account ARN in italics to your own AWS Account number and role name):
- Choose Update Trust Policy.
To modify the AWS Config recorder role in the AWS Config console
- Open the AWS Config console.
- In the navigation pane, select Settings.
- Scroll down to AWS Config role*.
- Select the radio button next to Choose a role from your account.
- Choose the role you created for AWS Config.
- Choose Save.
After you have done this, you can use the AWS Config console to force an evaluation of your resources.
To force an AWS Config rule evaluation in the AWS Config console
- Open the AWS Config console.
- In the navigation pane, select Rules.
- Choose the AWSConfigRuleKMS rule (or what you named it).
- At the top right-hand side of the console, choose Re-evaluate. This can take a few minutes for results to populate.
To have the key policy correctly evaluated, you need to add permissions to the key policy for the root ARN or the AWS Lambda role ARN to perform kms:GetKeyPolicy.
To update key policies to include the root or AWS Lambda role ARN
- Open the AWS KMS console.
- In the navigation pane, select Customer managed keys.
- Choose the Alias or Key ID you want to modify.
- Under Key policy, choose Edit.
- Copy the following policy snippet and paste it at the bottom of your current policy. (Replace the values in italics with your actual values).
- Choose Save changes.
In the following example screenshot of my AWS Config console, you can see that I have quite a few CMKs that don’t meet my compliance policies. The key policy either does not have the required permissions defined in my scenarios, or does not have the permissions for the root ARN or my Lambda role to perform kms:GetKeyPolicy. Both can result in a noncompliant status.
For example, the key policy for alias/SecurityOtterCMK that follows is missing the condition for aws:userid, as well as mixed permission sets. So this CMK is marked as Noncompliant.
On the other hand, the CMK marked as alias/Otter-EBS is in my whitelisted keys based upon my Gherkin, so it shows as Compliant.
You can now monitor your KMS keys for least privilege based on your required parameters.
Conclusion
In this post, I showed you how to use the AWS Config RDK to create, test, and deploy a custom AWS Config rule that scans your KMS key policies to look for rules designed to implement a least privilege concept. I supplied all scripts necessary to create your rule and test locally. With this AWS Config rule, you can use the AWS Config console to see whether your AWS KMS key policies are compliant with your company standards, so that you can react accordingly.
If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS KMS forum or contact AWS Support.
Want more AWS Security how-to content, news, and feature announcements? Follow us on Twitter.