AWS KMS Key Policies: The Access Model That Trips Up Everyone
Day 13/60 -- AWS KMS key policies. The single most important thing to understand: KMS key policies override IAM policies. If the key policy does not grant access, IAM cannot grant it. This is different from S3, Lambda, and most other AWS services. Every customer managed key needs this in the policy: # Create a KMS key with a policy that separates admin from usage KEY_ID=$(aws kms create-key \ --description "app-encryption-key" \ --query 'KeyMetadata.KeyId' \ --output text) # Grant encrypt/decrypt to the app role only aws kms create-grant \ --key-id $KEY_ID \ --grantee-principal arn:aws:iam::123456789012:role/app-service \ --operations Encrypt Decrypt GenerateDataKey # Add an alias for human-readable reference aws kms create-alias \ --alias-name alias/app-encryption \ --target-key-id $KEY_ID # Verify key rotation is enabled aws kms enable-key-rotation --key-id $KEY_ID aws kms get-key-rotation-status --key-id $KEY_ID Without it, IAM policies in your account
Day 13/60 -- AWS KMS key policies.
The single most important thing to understand: KMS key policies override IAM policies. If the key policy does not grant access, IAM cannot grant it. This is different from S3, Lambda, and most other AWS services.
Every customer managed key needs this in the policy:
# Create a KMS key with a policy that separates admin from usage
KEY_ID=$(aws kms create-key \
--description "app-encryption-key" \
--query 'KeyMetadata.KeyId' \
--output text)
# Grant encrypt/decrypt to the app role only
aws kms create-grant \
--key-id $KEY_ID \
--grantee-principal arn:aws:iam::123456789012:role/app-service \
--operations Encrypt Decrypt GenerateDataKey
# Add an alias for human-readable reference
aws kms create-alias \
--alias-name alias/app-encryption \
--target-key-id $KEY_ID
# Verify key rotation is enabled
aws kms enable-key-rotation --key-id $KEY_ID
aws kms get-key-rotation-status --key-id $KEY_ID
Without it, IAM policies in your account have zero effect on that key. Miss it and you call AWS Support.
Practical tip: Use kms:ViaService conditions to restrict which AWS services can use a key:
{
"Condition": {
"StringEquals": {
"kms:ViaService": "rds.us-east-1.amazonaws.com"
}
}
}
This ensures your database encryption key can only be used through RDS, not directly by someone with Decrypt permissions.
Audit your existing keys with:
for key in $(aws kms list-keys --query 'Keys[].KeyId' --output text); do
echo "=== $key ==="
aws kms get-key-policy --key-id $key --policy-name default
done
Check that every key has the root statement and that admin/user roles are separated. This is Day 13 of the 60 Day Cloud Security Challenge.