Control: KMS key decryption should be restricted in IAM inline policy
Description
Checks whether the inline policies that are embedded in your IAM identities (role, user, or group) allow the AWS KMS decryption actions on all KMS keys. This control uses Zelkova, an automated reasoning engine, to validate and warn you about policies that may grant broad access to your secrets across AWS accounts. This control fails if kms:Decrypt or kms:ReEncryptFrom actions are allowed on all KMS keys in an inline policy.
Usage
Run the control in your terminal:
powerpipe control run aws_compliance.control.kms_key_decryption_restricted_in_iam_inline_policy
Snapshot and share results via Turbot Pipes:
powerpipe loginpowerpipe control run aws_compliance.control.kms_key_decryption_restricted_in_iam_inline_policy --share
SQL
This control uses a named query:
with user_with_decrypt_grant as ( select distinct arn from aws_iam_user, jsonb_array_elements(inline_policies_std) as inline_policy, jsonb_array_elements(inline_policy -> 'PolicyDocument' -> 'Statement') as statement where statement ->> 'Effect' = 'Allow' and statement -> 'Resource' ?| array['*', 'arn:aws:kms:*:' || account_id || ':key/*', 'arn:aws:kms:*:' || account_id || ':alias/*'] and statement -> 'Action' ?| array['*', 'kms:*', 'kms:decrypt', 'kms:deencrypt*', 'kms:reencryptfrom']),role_with_decrypt_grant as ( select distinct arn from aws_iam_role, jsonb_array_elements(inline_policies_std) as inline_policy, jsonb_array_elements(inline_policy -> 'PolicyDocument' -> 'Statement') as statement where statement ->> 'Effect' = 'Allow' and statement -> 'Resource' ?| array['*', 'arn:aws:kms:*:' || account_id || ':key/*', 'arn:aws:kms:*:' || account_id || ':alias/*'] and statement -> 'Action' ?| array['*', 'kms:*', 'kms:decrypt', 'kms:deencrypt*', 'kms:reencryptfrom']),group_with_decrypt_grant as ( select distinct arn from aws_iam_group, jsonb_array_elements(inline_policies_std) as inline_policy, jsonb_array_elements(inline_policy -> 'PolicyDocument' -> 'Statement') as statement where statement ->> 'Effect' = 'Allow' and statement -> 'Resource' ?| array['*', 'arn:aws:kms:*:' || account_id || ':key/*', 'arn:aws:kms:*:' || account_id || ':alias/*'] and statement -> 'Action' ?| array['*', 'kms:*', 'kms:decrypt', 'kms:deencrypt*', 'kms:reencryptfrom'])select i.arn as resource, case when d.arn is null then 'ok' else 'alarm' end as status, case when d.arn is null then 'User ' || i.title || ' not allowed to perform decryption actions on all keys.' else 'User ' || i.title || ' allowed to perform decryption actions on all keys.' end as reason
, i.account_idfrom aws_iam_user i left join user_with_decrypt_grant d on i.arn = d.arnunionselect r.arn as resource, case when d.arn is null then 'ok' else 'alarm' end as status, case when d.arn is null then 'Role ' || r.title || ' not allowed to perform decryption actions on all keys.' else 'Role ' || r.title || ' allowed to perform decryption actions on all keys.' end as reason
, r.account_idfrom aws_iam_role r left join role_with_decrypt_grant d on r.arn = d.arnwhere r.arn not like '%service-role/%'unionselect g.arn as resource, case when d.arn is null then 'ok' else 'alarm' end as status, case when d.arn is null then 'Role ' || g.title || ' not allowed to perform decryption actions on all keys.' else 'Group ' || g.title || ' allowed to perform decryption actions on all keys.' end as reason , g.account_idfrom aws_iam_group g left join group_with_decrypt_grant d on g.arn = d.arn;