turbot/steampipe-mod-aws-compliance

Control: 2 IAM principals should not have IAM inline policies that allow decryption actions on all KMS keys

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.

With AWS KMS, you control who can use your customer master keys (CMKs) and gain access to your encrypted data. IAM policies define which actions an identity (user, group, or role) can perform on which resources. Following security best practices, AWS recommends that you allow least privilege. In other words, you should grant to identities only the permissions they need and only for keys that are required to perform a task. Otherwise, the user might use keys that are not appropriate for your data.

Instead of granting permission for all keys, determine the minimum set of keys that users need to access encrypted data. Then design policies that allow the users to use only those keys. For example, do not allow kms:Decrypt permission on all KMS keys. Instead, allow them only on keys in a particular Region for your account. By adopting the principle of least privilege, you can reduce the risk of unintended disclosure of your data.

Remediation

To remediate this issue, you modify the inline policy to restrict access to the keys.

To modify an IAM inline policy

  1. Open the IAM console.
  2. In the IAM navigation pane, choose Users, Groups, or Roles.
  3. Choose the name of the user, group or role for which to modify IAM inline policies.
  4. Choose the arrow next to the policy to modify.
  5. Choose Edit policy.
  6. Choose the JSON tab.
  7. Change the “Resource” value to the specific keys that you want to allow.
  8. After you modify the policy, choose Review policy.
  9. Choose Save changes.

Usage

Run the control in your terminal:

powerpipe control run aws_compliance.control.foundational_security_kms_2

Snapshot and share results via Turbot Pipes:

powerpipe login
powerpipe control run aws_compliance.control.foundational_security_kms_2 --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_id
from
aws_iam_user i
left join user_with_decrypt_grant d on i.arn = d.arn
union
select
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_id
from
aws_iam_role r
left join role_with_decrypt_grant d on r.arn = d.arn
where
r.arn not like '%service-role/%'
union
select
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_id
from
aws_iam_group g
left join group_with_decrypt_grant d on g.arn = d.arn;

Tags