Comments (9)
Thanks for raising the question @rbracewell 👍 .
Adding the relevant slack thread for additional details - https://steampipe.slack.com/archives/C01UECB59A7/p1673974501135479
from steampipe-mod-aws-perimeter.
Going through the documentation provided by AWS, I think we should add a condition to check for kms:CallerAccount
since setting this parameter to *
can result in the key becoming public.
@khushboo9024 @rajlearner17 could you please provide some more insight on this issue?
from steampipe-mod-aws-perimeter.
'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
from steampipe-mod-aws-perimeter.
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.
from steampipe-mod-aws-perimeter.
Hey @rbracewell, sorry for the delayed response.
The kms:CallerAccount condition is often used to enforce access control and limit the usage of KMS keys to specific AWS accounts. By including this condition in a key policy, we can ensure that only authorized AWS accounts can perform operations on the KMS key, such as encrypting or decrypting data. I have created a KMS key in CloudTrail, and the below policies are created by default. I have updated the kms:CallerAccount value to '*' in one of the below policies.
-- alias/test-key
select jsonb_pretty(policy) from aws_kms_key where id = '01d1934c-af89-4cf9-8ed7-b6b49c0a85e3'
+---------------------------------------------------------------------------------------------------------------+
| jsonb_pretty |
+---------------------------------------------------------------------------------------------------------------+
| { |
| "Id": "Key policy created by CloudTrail", |
| "Version": "2012-10-17", |
| "Statement": [ |
| { |
| "Sid": "Enable IAM User Permissions", |
| "Action": "kms:*", |
| "Effect": "Allow", |
| "Resource": "*", |
| "Principal": { |
| "AWS": [ |
| "arn:aws:sts::123456789:assumed-role/superuser/[email protected]", |
| "arn:aws:iam::123456789:root" |
| ] |
| } |
| }, |
| { |
| "Sid": "Allow CloudTrail to encrypt logs", |
| "Action": "kms:GenerateDataKey*", |
| "Effect": "Allow", |
| "Resource": "*", |
| "Condition": { |
| "StringLike": { |
| "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:123456789:trail/*" |
| }, |
| "StringEquals": { |
| "AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789:trail/test-trail" |
| } |
| }, |
| "Principal": { |
| "Service": "cloudtrail.amazonaws.com" |
| } |
| }, |
| { |
| "Sid": "Allow CloudTrail to describe key", |
| "Action": "kms:DescribeKey", |
| "Effect": "Allow", |
| "Resource": "*", |
| "Principal": { |
| "Service": "cloudtrail.amazonaws.com" |
| } |
| }, |
| { |
| "Sid": "Allow principals in the account to decrypt log files", |
| "Action": [ |
| "kms:Decrypt", |
| "kms:ReEncryptFrom" |
| ], |
| "Effect": "Allow", |
| "Resource": "*", |
| "Condition": { |
| "StringLike": { |
| "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:123456789:trail/*"
"kms:CallerAccount": "*" // I have edited the policy here |
| }, | |
| }, |
| "Principal": { |
| "AWS": "*" |
| } |
| }, |
| { |
| "Sid": "Allow alias creation during setup", |
| "Action": "kms:CreateAlias", |
| "Effect": "Allow", |
| "Resource": "*", |
| "Condition": { |
| "StringEquals": { |
| "kms:ViaService": "ec2.us-east-1.amazonaws.com", |
| "kms:CallerAccount": "123456789" |
| } |
| }, |
| "Principal": { |
| "AWS": "*" |
| } |
| }, |
| { |
| "Sid": "Enable cross account log decryption", |
| "Action": [ |
| "kms:Decrypt", |
| "kms:ReEncryptFrom" |
| ], |
| "Effect": "Allow", |
| "Resource": "*", |
| "Condition": { |
| "StringLike": { |
| "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:123456789:trail/*" |
| }, |
| "StringEquals": { |
| "kms:CallerAccount": "123456789" |
| } |
| }, |
| "Principal": { |
| "AWS": "*" |
| } |
| } |
| ] |
| } |
+---------------------------------------------------------------------------------------------------------------+
Here is the updated the mod control query - (got the expected output)
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. // check for CallerAccount
and 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
r.arn as resource,
case
when p.arn is null then 'ok'
else 'alarm'
end as status,
case
when p.arn is null then title || ' policy does not allow public access.'
else title || ' policy contains ' || coalesce(p.statements_num, 0) ||
' statement(s) that allow public access.'
end as reason
from
aws_kms_key as r
left join wildcard_action_policies as p on p.arn = r.arn
where
key_manager = 'CUSTOMER';
+------------------------------------------------------------------------------+--------+---------------------------------------------------------------------------+
| resource | status | reason |
+------------------------------------------------------------------------------+--------+---------------------------------------------------------------------------+
| arn:aws:kms:ap-south-1:123456789:key/e6ce31c8-5f6f-45ce-bec4-ee7354a4757d | ok | alias/intg-test policy does not allow public access. |
| arn:aws:kms:us-east-1:123456789:key/01d1934c-af89-4cf9-8ed7-b6b49c0a85e3 | alarm | alias/test-key policy contains 1 statement(s) that allow public access. |
| arn:aws:kms:ap-south-1:123456789:key/a1dbc718-e60a-416e-8ab5-3a935043eeae | ok | alias/test2 policy does not allow public access. |
+------------------------------------------------------------------------------+--------+---------------------------------------------------------------------------+
Could you please check the above query and let us know if it solves the problem?
@misraved @rajlearner17 Please share your views regarding the updated query.
from steampipe-mod-aws-perimeter.
When running the existing query from the documentation I get the following results
ok: 192
alarm: 8
Running the revised query from above I get the following results
ok: 200
alarm: 0
This is not what I expected. Of those 8 in alarm only one of them uses CallerAccount so I would still have expected the alarm count to be 7 with the new query.
from steampipe-mod-aws-perimeter.
Hey @rbracewell, thanks for the quick check. Could you please try the below one? I hope this will work as it was expected. If it does not work, please share one policy structure(without sensitive info) where CallerAccount is not used.
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. // check for 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
r.arn as resource,
case
when p.arn is null then 'ok'
else 'alarm'
end as status,
case
when p.arn is null then title || ' policy does not allow public access.'
else title || ' policy contains ' || coalesce(p.statements_num, 0) ||
' statement(s) that allow public access.'
end as reason
from
aws_kms_key as r
left join wildcard_action_policies as p on p.arn = r.arn
where
key_manager = 'CUSTOMER';
from steampipe-mod-aws-perimeter.
The revised query returned the expected results this time.
The policy correctly detected kms:CallerAccount and marked as ok rather than alarm
from steampipe-mod-aws-perimeter.
Thanks @rbracewell for testing the updated query suggested by @bigdatasourav 👍.
The feedback has been extremely helpful in nailing down the query output 🎉 .
from steampipe-mod-aws-perimeter.
Related Issues (11)
- Add initial mod file and structure HOT 1
- Control elb_application_lb_waf_enabled providing incorrect result HOT 2
- Add trusted controls for AWS policies HOT 2
- Update Resource Policy Public Access controls to align with additional services supported by AWS HOT 2
- aws_perimeter.control.s3_bucket_policy_prohibit_public_access check reports OK for buckets it can't process HOT 6
- Add additional dimensions to queries
- Add initial public access controls
- Error when defined trusted accounts parameter
- Update mod.sp to address deprecation warning
- Update resource policy public access controls to evaluate common conditions
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from steampipe-mod-aws-perimeter.