turbot / steampipe-mod-aws-perimeter

Is your AWS perimeter secure? Use Powerpipe and Steampipe to check your AWS accounts for public resources, resources shared with untrusted accounts, insecure network configurations and more.
https://hub.powerpipe.io/mods/turbot/aws_perimeter
Apache License 2.0
106 stars 6 forks source link

aws_perimeter.control.s3_bucket_policy_prohibit_public_access check reports OK for buckets it can't process #21

Closed rbracewell closed 1 year ago

rbracewell commented 1 year ago

Running the AWS perimeter mod control aws_perimeter.control.s3_bucket_policy_prohibit_public_access fails with the error

Error: operation error S3: GetBucketLocation, https response error StatusCode: 403, RequestID: <REDACTED>, HostID: <REDACTED>, api error AccessDenied: Access Denied (SQLSTATE HV000)

This looks to be down to several buckets with messed up permission policies. So I modified the steampipe configuration to include

ignore_error_codes = ["AccessDenied", "AccessDeniedException", "NotAuthorized", "UnauthorizedOperation", "UnrecognizedClientException", "AuthorizationError"]

This at least allows the control to run to a conclusion however, the buckets that previously caused the control to fail now report OK

OK : <BUCKET NAME REDACTED> policy does not allow public access. .......................................................... <nil> <ACCOUNT NAME REDACTED>

Steampipe version (steampipe -v) v0.17.4

Plugin version (steampipe plugin list)

+--------------------------------------------------+---------+-------------+
| Installed Plugin                                 | Version | Connections |
+--------------------------------------------------+---------+-------------+
| hub.steampipe.io/plugins/turbot/aws@latest       | 0.91.0  | aws         |
| hub.steampipe.io/plugins/turbot/steampipe@latest | 0.6.0   | steampipe   |
+--------------------------------------------------+---------+-------------+

To reproduce Create a bucket policy which only allows access to a specific role.

Expected behavior Should these particular resources be flagged as ERROR, as the credentials being used are insufficient for these buckets to be evaluated even though certain error codes are being ignored?

rajlearner17 commented 1 year ago

@rbracewell Appreciate trying out Steampipe!

Sad to see it's not yielding the expected result for you. Nicely detailed information to replicate the issue at our end; we will look into it and revert.

khushboo9024 commented 1 year ago

Hi @rbracewell thanks for sharing your observation. Appreciate it!

We did reproduce the issue in line with your error as one of the example cases, i.e. restricting the getLocation call by specifying the bucket resource policy. The current plugin design is handling this issue in the way mentioned here. Hence, when we ignore the AccessDenied in aws.spc > ignore_error_codes, it, sets the bucket location as nil and this results in making the affected bucket policy as, nil. This is not getting you the expected result; for now, pls refer to the below attached query, where we are moving the validation to Info , the reason being we may have cases where there may not be any policy set in the bucket and that returns nil. Let us know if this fits your current need. Pls keep us informed of any other edge cases, you are coming across. Meanwhile, we are discussing further how we can handle it better way. We will keep this thread posted.

with wildcard_action_policies as (
  select
    arn,
    count(*) as statements_num
  from
    aws_s3_bucket,
    jsonb_array_elements(policy_std -> 'Statement') as s
  where
    s ->> 'Effect' = 'Allow'
    -- 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 r.policy is null then 'info'
    when p.arn is null then 'ok'
    else 'alarm'
  end as status,
  case
    when r.policy is null then title || ' does not have defined policy or insufficient access to the policy.'
    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,
  r.region,
  r.account_id
from
  aws_s3_bucket as r
  left join wildcard_action_policies as p on p.arn = r.arn
rbracewell commented 1 year ago

Agreed Info would be better than Error given your description.

rajlearner17 commented 1 year ago

Thanks for the response, @rbracewell , we will adjust the query to info for such edge cases for the time being.

github-actions[bot] commented 1 year ago

'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.'

github-actions[bot] commented 1 year ago

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.