IAM Policy No Full Access to KMS
Overview
This check identifies custom IAM policies that grant full access to AWS Key Management Service (KMS) using the kms:* action. Policies should follow the principle of least privilege by granting only the specific KMS actions needed, scoped to specific keys.
Risk
Granting kms:* permissions creates serious security risks:
- Data exposure: Anyone with the policy can decrypt sensitive data protected by any KMS key
- Key tampering: Attackers can modify key policies or disable keys
- Data loss: Keys can be scheduled for deletion, making encrypted data permanently unreadable
- Privilege escalation: Broad KMS access can help attackers move laterally through your environment
Remediation Steps
Prerequisites
You need permission to view and edit IAM policies in your AWS account. This typically requires IAM administrator access or a role with iam:GetPolicy, iam:GetPolicyVersion, and iam:CreatePolicyVersion permissions.
AWS Console Method
- Open the IAM console in us-east-1
- Click Policies in the left navigation
- Find the policy flagged by Prowler (you can search by name)
- Click the policy name to open it
- Select the Permissions tab, then click Edit
- Find any statement with
"Action": "kms:*"and replace it with specific actions like:kms:Encryptkms:Decryptkms:GenerateDataKeykms:DescribeKey
- Update the
Resourcefield from"*"to specific KMS key ARNs:arn:aws:kms:us-east-1:123456789012:key/your-key-id - Click Next, review your changes, then click Save changes
AWS CLI (optional)
Step 1: Identify the problematic policy
List your customer-managed policies:
aws iam list-policies \
--scope Local \
--region us-east-1 \
--query 'Policies[*].[PolicyName,Arn]' \
--output table
Step 2: View the current policy document
Get the policy version and document:
# Get the default version ID
aws iam get-policy \
--policy-arn arn:aws:iam::123456789012:policy/YourPolicyName \
--region us-east-1 \
--query 'Policy.DefaultVersionId' \
--output text
# Get the policy document (replace v1 with your version)
aws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/YourPolicyName \
--version-id v1 \
--region us-east-1 \
--query 'PolicyVersion.Document' \
--output json
Step 3: Create a new policy version with least privilege
Create a JSON file with scoped permissions:
cat > /tmp/kms-least-privilege-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificKMSActions",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/your-key-id",
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.us-east-1.amazonaws.com"
}
}
}
]
}
EOF
Apply the updated policy:
aws iam create-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/YourPolicyName \
--policy-document file:///tmp/kms-least-privilege-policy.json \
--set-as-default \
--region us-east-1
Common KMS actions by use case
| Use Case | Required Actions |
|---|---|
| Encrypt data | kms:Encrypt, kms:GenerateDataKey |
| Decrypt data | kms:Decrypt |
| S3 server-side encryption | kms:Encrypt, kms:Decrypt, kms:GenerateDataKey |
| Secrets Manager | kms:Decrypt, kms:GenerateDataKey |
| View key info only | kms:DescribeKey, kms:GetKeyPolicy |
CloudFormation (optional)
Replace any existing policy with this least-privilege template:
AWSTemplateFormatVersion: '2010-09-09'
Description: IAM policy with least privilege KMS permissions
Parameters:
KMSKeyArn:
Type: String
Description: ARN of the KMS key to grant access to
AllowedPattern: "arn:aws:kms:[a-z0-9-]+:[0-9]+:key/[a-f0-9-]+"
Resources:
LeastPrivilegeKMSPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: LeastPrivilegeKMSAccess
Description: Policy with scoped KMS permissions following least privilege
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowSpecificKMSActions
Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
- kms:DescribeKey
Resource: !Ref KMSKeyArn
Condition:
StringEquals:
kms:ViaService: !Sub 's3.${AWS::Region}.amazonaws.com'
Outputs:
PolicyArn:
Description: ARN of the created IAM policy
Value: !Ref LeastPrivilegeKMSPolicy
Deploy with:
aws cloudformation deploy \
--template-file template.yaml \
--stack-name least-privilege-kms-policy \
--parameter-overrides KMSKeyArn=arn:aws:kms:us-east-1:123456789012:key/your-key-id \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
variable "kms_key_arn" {
description = "ARN of the KMS key to grant access to"
type = string
}
variable "policy_name" {
description = "Name for the IAM policy"
type = string
default = "LeastPrivilegeKMSAccess"
}
variable "via_service" {
description = "AWS service that must be used to access the key (e.g., s3.us-east-1.amazonaws.com)"
type = string
default = "s3.us-east-1.amazonaws.com"
}
resource "aws_iam_policy" "least_privilege_kms" {
name = var.policy_name
description = "Policy with scoped KMS permissions following least privilege"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowSpecificKMSActions"
Effect = "Allow"
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext",
"kms:DescribeKey"
]
Resource = var.kms_key_arn
Condition = {
StringEquals = {
"kms:ViaService" = var.via_service
}
}
}
]
})
}
output "policy_arn" {
description = "ARN of the created IAM policy"
value = aws_iam_policy.least_privilege_kms.arn
}
Apply with:
terraform apply -var="kms_key_arn=arn:aws:kms:us-east-1:123456789012:key/your-key-id"
Verification
After making changes, verify the fix:
- Return to the IAM console and open your updated policy
- Review the JSON to confirm no
kms:*actions remain - Re-run the Prowler check to confirm it passes:
prowler aws --check iam_policy_no_full_access_to_kms --region us-east-1
Advanced verification with AWS CLI
Check for any remaining kms:* statements:
# Get all customer-managed policies
for arn in $(aws iam list-policies --scope Local --query 'Policies[*].Arn' --output text --region us-east-1); do
version=$(aws iam get-policy --policy-arn "$arn" --query 'Policy.DefaultVersionId' --output text --region us-east-1)
doc=$(aws iam get-policy-version --policy-arn "$arn" --version-id "$version" --query 'PolicyVersion.Document' --output json --region us-east-1)
if echo "$doc" | grep -q '"kms:\*"'; then
echo "FOUND kms:* in policy: $arn"
fi
done
Additional Resources
Notes
- Test before applying: Changes to IAM policies take effect immediately. Test in a non-production environment first.
- Policy version limits: IAM policies can have at most 5 versions. Delete old versions if you hit this limit.
- Identify dependent resources: Before modifying a policy, use
aws iam list-entities-for-policyto see which users, groups, and roles depend on it. - Consider conditions: The
kms:ViaServicecondition shown in examples restricts key usage to specific AWS services. Adjust or remove based on your needs. - Audit existing access: Use AWS CloudTrail to see what KMS actions are actually being used before narrowing permissions.