Control: 5 KMS keys should not be publicly accessible
Description
This control checks whether an AWS KMS key is publicly accessible. The control fails if the KMS key is publicly accessible.
Implementing least privilege access is fundamental to reducing security risk and the impact of errors or malicious intent. If the key policy for an AWS KMS key allows access from external accounts, third parties might be able to encrypt and decrypt data by using the key. This could result in an internal or external threat exfiltrating data from AWS services that use the key.
Remediation
For information about updating the key policy for an AWS KMS key, see Key policies in AWS KMS in the AWS Key Management Service Developer Guide.
Usage
Run the control in your terminal:
powerpipe control run aws_compliance.control.foundational_security_kms_5Snapshot and share results via Turbot Pipes:
powerpipe loginpowerpipe control run aws_compliance.control.foundational_security_kms_5 --shareSQL
This control uses a named query:
with wildcard_action_policies as (  select    arn,    count(*) as statements_num  from    aws_kms_key,    jsonb_array_elements(policy_std -> 'Statement') as s  where    s ->> 'Effect' = 'Allow'    -- kms:CallerAccount    and s -> 'Condition' -> 'StringEquals' -> 'kms:calleraccount' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'kms:calleraccount' is null    and (      s -> 'Condition' -> 'StringLike' -> 'kms:calleraccount' is null      or s -> 'Condition' -> 'StringLike' -> 'kms:calleraccount' ? '*'    )    -- aws:SourceOwner    and s -> 'Condition' -> 'StringEquals' -> 'aws:sourceowner' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourceowner' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:sourceowner' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:sourceowner' ? '*'    )    -- aws:SourceAccount    and s -> 'Condition' -> 'StringEquals' -> 'aws:sourceaccount' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourceaccount' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:sourceaccount' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:sourceaccount' ? '*'    )    -- aws:PrincipalOrgID    and s -> 'Condition' -> 'StringEquals' -> 'aws:principalorgid' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalorgid' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:principalorgid' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:principalorgid' ? '*'    )    -- aws:PrincipalAccount    and s -> 'Condition' -> 'StringEquals' -> 'aws:principalaccount' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalaccount' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:principalaccount' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:principalaccount' ? '*'    )    -- aws:PrincipalArn    and s -> 'Condition' -> 'StringEquals' -> 'aws:principalarn' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalarn' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:principalarn' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:principalarn' ? '*'    )    and (      s -> 'Condition' -> 'ArnEquals' -> 'aws:principalarn' is null      or s -> 'Condition' -> 'ArnEquals' -> 'aws:principalarn' ? '*'    )    and (      s -> 'Condition' -> 'ArnLike' -> 'aws:principalarn' is null      or s -> 'Condition' -> 'ArnLike' -> 'aws:principalarn' ? '*'    )    -- aws:SourceArn    and s -> 'Condition' -> 'StringEquals' -> 'aws:sourcearn' is null    and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourcearn' is null    and (      s -> 'Condition' -> 'StringLike' -> 'aws:sourcearn' is null      or s -> 'Condition' -> 'StringLike' -> 'aws:sourcearn' ? '*'    )    and (      s -> 'Condition' -> 'ArnEquals' -> 'aws:sourcearn' is null      or s -> 'Condition' -> 'ArnEquals' -> 'aws:sourcearn' ? '*'    )    and (      s -> 'Condition' -> 'ArnLike' -> 'aws:sourcearn' is null      or s -> 'Condition' -> 'ArnLike' -> 'aws:sourcearn' ? '*'    )    and (      s -> 'Principal' -> 'AWS' = '["*"]'      or s ->> 'Principal' = '*'    )  group by    arn)select  k.arn as resource,  case    when p.arn is null then 'ok'    else 'alarm'  end status,  case    when p.arn is null then title || ' does not allow public access.'    else title || ' contains ' || coalesce(p.statements_num, 0) ||    ' statements that allow public access.'  end as reason    , k.region, k.account_idfrom  aws_kms_key as k  left join wildcard_action_policies as p on p.arn = k.arnwhere  key_manager = 'CUSTOMER';