Skip to content

The statement for var.source_ip_allow_list includes s3:PutBucketPolicy which locks user out of the bucket #269

@nitrocode

Description

@nitrocode

Describe the Bug

One of the use cases to use var.source_ip_allow_list is to expose an s3 bucket ONLY to cloudflare IPs. An engineer used this input and then attempted to change the bucket policy which then failed because the cicd was not coming from the allowlisted IP addresses.

dynamic "statement" {
for_each = length(var.source_ip_allow_list) > 0 ? [1] : []
content {
sid = "AllowIPPrincipals"
effect = "Deny"
actions = ["s3:*"]
resources = [local.bucket_arn, "${local.bucket_arn}/*"]
principals {
identifiers = ["*"]
type = "*"
}
condition {
test = "NotIpAddress"
variable = "aws:SourceIp"
values = var.source_ip_allow_list
}
}
}

Expected Behavior

Option 1. Deny all actions other than s3:PutBucketPolicy using not_actions

      effect      = "Deny"
      resources   = [local.bucket_arn, "${local.bucket_arn}/*"]
-     actions     = ["s3:*"]
+     not_actions = ["s3:PutBucketPolicy"]

Option 2. Leave off the local.bucket_arn so it only denies on objects instead of on buckets too

      effect    = "Deny"
-     resources = [local.bucket_arn, "${local.bucket_arn}/*"]
+     resources = ["${local.bucket_arn}/*"]
      actions   = ["s3:*"]

Option 3. New input such as var.source_ip_allow_list_s3_objects to toggle a new statement to deny to only impact objects (similar to option 2)

  dynamic "statement" { 
    for_each = length(var.source_ip_allow_list_s3_objects) > 0 ? [1] : [] 

    content { 
      sid       = "AllowIPPrincipalsOnObjects" 
      effect    = "Deny" 
      actions   = ["s3:*"] 
      resources = ["${local.bucket_arn}/*"] 
      principals { 
        identifiers = ["*"] 
        type        = "*" 
      } 
      condition { 
        test     = "NotIpAddress" 
        variable = "aws:SourceIp" 
        values   = var.source_ip_allow_list_s3_objects 
      } 
    } 
  }

Steps to Reproduce

  1. Supply set of IPs that CICD is not coming from
  2. terraform apply
  3. Try to change the policy
  4. terraform apply

The 2nd apply should fail and will require root account access to modify the s3 bucket policy

Screenshots

N/A

Environment

N/A

Additional Context

One workaround is to use var.source_policy_documents to add a custom policy

locals {
  bucket_name = "abcorg-ue1-prod-test"
  bucket_arn  = format("arn:aws:::%s", local.bucket_name)
}

data "aws_iam_policy_document" "deny_get_object" {
  statement {
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    effect = "Deny"

    actions = [
      "s3:GetObject",
    ]

    resources = [
      "${local.bucket_arn}/*",
    ]

    condition {
      test     = "NotIpAddress"
      variable = "aws:SourceIp"
      values = [
        "1.1.1.1",
        # etc
      ]
    }
  }
}

module "s3_bucket" {
  source = "cloudposse/s3-bucket/aws"
  # Cloud Posse recommends pinning every module to a specific version
  # version = "x.x.x"
  bucket_name = local.bucket_name

  source_policy_documents = [
    data.aws_iam_policy_documents.deny_get_object.json,
  ]
}
Reference all S3 actions
✗ policy_sentry query action-table --service s3
ALL s3 actions:
s3:AbortMultipartUpload
s3:AssociateAccessGrantsIdentityCenter
s3:BypassGovernanceRetention
s3:CreateAccessGrant
s3:CreateAccessGrantsInstance
s3:CreateAccessGrantsLocation
s3:CreateAccessPoint
s3:CreateAccessPointForObjectLambda
s3:CreateBucket
s3:CreateJob
s3:CreateMultiRegionAccessPoint
s3:CreateStorageLensGroup
s3:DeleteAccessGrant
s3:DeleteAccessGrantsInstance
s3:DeleteAccessGrantsInstanceResourcePolicy
s3:DeleteAccessGrantsLocation
s3:DeleteAccessPoint
s3:DeleteAccessPointForObjectLambda
s3:DeleteAccessPointPolicy
s3:DeleteAccessPointPolicyForObjectLambda
s3:DeleteBucket
s3:DeleteBucketPolicy
s3:DeleteBucketWebsite
s3:DeleteJobTagging
s3:DeleteMultiRegionAccessPoint
s3:DeleteObject
s3:DeleteObjectTagging
s3:DeleteObjectVersion
s3:DeleteObjectVersionTagging
s3:DeleteStorageLensConfiguration
s3:DeleteStorageLensConfigurationTagging
s3:DeleteStorageLensGroup
s3:DescribeJob
s3:DescribeMultiRegionAccessPointOperation
s3:DissociateAccessGrantsIdentityCenter
s3:GetAccelerateConfiguration
s3:GetAccessGrant
s3:GetAccessGrantsInstance
s3:GetAccessGrantsInstanceForPrefix
s3:GetAccessGrantsInstanceResourcePolicy
s3:GetAccessGrantsLocation
s3:GetAccessPoint
s3:GetAccessPointConfigurationForObjectLambda
s3:GetAccessPointForObjectLambda
s3:GetAccessPointPolicy
s3:GetAccessPointPolicyForObjectLambda
s3:GetAccessPointPolicyStatus
s3:GetAccessPointPolicyStatusForObjectLambda
s3:GetAccountPublicAccessBlock
s3:GetAnalyticsConfiguration
s3:GetBucketAcl
s3:GetBucketCORS
s3:GetBucketLocation
s3:GetBucketLogging
s3:GetBucketNotification
s3:GetBucketObjectLockConfiguration
s3:GetBucketOwnershipControls
s3:GetBucketPolicy
s3:GetBucketPolicyStatus
s3:GetBucketPublicAccessBlock
s3:GetBucketRequestPayment
s3:GetBucketTagging
s3:GetBucketVersioning
s3:GetBucketWebsite
s3:GetDataAccess
s3:GetEncryptionConfiguration
s3:GetIntelligentTieringConfiguration
s3:GetInventoryConfiguration
s3:GetJobTagging
s3:GetLifecycleConfiguration
s3:GetMetricsConfiguration
s3:GetMultiRegionAccessPoint
s3:GetMultiRegionAccessPointPolicy
s3:GetMultiRegionAccessPointPolicyStatus
s3:GetMultiRegionAccessPointRoutes
s3:GetObject
s3:GetObjectAcl
s3:GetObjectAttributes
s3:GetObjectLegalHold
s3:GetObjectRetention
s3:GetObjectTagging
s3:GetObjectTorrent
s3:GetObjectVersion
s3:GetObjectVersionAcl
s3:GetObjectVersionAttributes
s3:GetObjectVersionForReplication
s3:GetObjectVersionTagging
s3:GetObjectVersionTorrent
s3:GetReplicationConfiguration
s3:GetStorageLensConfiguration
s3:GetStorageLensConfigurationTagging
s3:GetStorageLensDashboard
s3:GetStorageLensGroup
s3:InitiateReplication
s3:ListAccessGrants
s3:ListAccessGrantsInstances
s3:ListAccessGrantsLocations
s3:ListAccessPoints
s3:ListAccessPointsForObjectLambda
s3:ListAllMyBuckets
s3:ListBucket
s3:ListBucketMultipartUploads
s3:ListBucketVersions
s3:ListJobs
s3:ListMultiRegionAccessPoints
s3:ListMultipartUploadParts
s3:ListStorageLensConfigurations
s3:ListStorageLensGroups
s3:ListTagsForResource
s3:ObjectOwnerOverrideToBucketOwner
s3:PauseReplication
s3:PutAccelerateConfiguration
s3:PutAccessGrantsInstanceResourcePolicy
s3:PutAccessPointConfigurationForObjectLambda
s3:PutAccessPointPolicy
s3:PutAccessPointPolicyForObjectLambda
s3:PutAccessPointPublicAccessBlock
s3:PutAccountPublicAccessBlock
s3:PutAnalyticsConfiguration
s3:PutBucketAcl
s3:PutBucketCORS
s3:PutBucketLogging
s3:PutBucketNotification
s3:PutBucketObjectLockConfiguration
s3:PutBucketOwnershipControls
s3:PutBucketPolicy
s3:PutBucketPublicAccessBlock
s3:PutBucketRequestPayment
s3:PutBucketTagging
s3:PutBucketVersioning
s3:PutBucketWebsite
s3:PutEncryptionConfiguration
s3:PutIntelligentTieringConfiguration
s3:PutInventoryConfiguration
s3:PutJobTagging
s3:PutLifecycleConfiguration
s3:PutMetricsConfiguration
s3:PutMultiRegionAccessPointPolicy
s3:PutObject
s3:PutObjectAcl
s3:PutObjectLegalHold
s3:PutObjectRetention
s3:PutObjectTagging
s3:PutObjectVersionAcl
s3:PutObjectVersionTagging
s3:PutReplicationConfiguration
s3:PutStorageLensConfiguration
s3:PutStorageLensConfigurationTagging
s3:ReplicateDelete
s3:ReplicateObject
s3:ReplicateTags
s3:RestoreObject
s3:SubmitMultiRegionAccessPointRoutes
s3:TagResource
s3:UntagResource
s3:UpdateAccessGrantsLocation
s3:UpdateJobPriority
s3:UpdateJobStatus
s3:UpdateStorageLensGroup

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug🐛 An issue with the system

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions