s3-bucket-policy

AWS S3 Bucket Policy Expert

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "s3-bucket-policy" with this command: npx skills add dengineproblem/agents-monorepo/dengineproblem-agents-monorepo-s3-bucket-policy

AWS S3 Bucket Policy Expert

Expert guidance on creating, analyzing, and optimizing AWS S3 bucket policies with focus on security, access control, and compliance.

Policy Structure

{ "Version": "2012-10-17", "Id": "PolicyIdentifier", "Statement": [ { "Sid": "StatementIdentifier", "Effect": "Allow | Deny", "Principal": { "AWS": "arn:aws:iam::account-id:root" }, "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::bucket-name", "arn:aws:s3:::bucket-name/*" ], "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] }

Core Principles

security_principles: least_privilege: description: "Grant only minimum necessary permissions" practice: "Start with deny all, add specific allows"

explicit_deny: description: "Deny always overrides Allow" practice: "Use Deny for security guardrails"

defense_in_depth: description: "Multiple layers of security" practice: "Combine bucket policy + IAM + ACL + encryption"

avoid_wildcards: bad: '"Principal": "*"' better: '"Principal": {"AWS": "arn:aws:iam::123456789012:root"}'

common_mistakes:

  • "Using Principal: * without conditions"
  • "Missing resource ARN for objects (/*)"
  • "Forgetting to block public access"
  • "Not enabling versioning before policies"

Common Policy Patterns

Public Read for Static Website

{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-website-bucket/", "Condition": { "StringEquals": { "s3:ExistingObjectTag/public": "true" } } } ] }

Cross-Account Access

{ "Version": "2012-10-17", "Statement": [ { "Sid": "CrossAccountAccess", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::987654321098:root" }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::shared-bucket", "arn:aws:s3:::shared-bucket/*" ], "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } } ] }

CloudFront Origin Access Control

{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-cdn-bucket/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE" } } } ] }

Enforce Encryption

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyUnencryptedUploads", "Effect": "Deny", "Principal": "", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::secure-bucket/", "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" } } }, { "Sid": "DenyIncorrectKMSKey", "Effect": "Deny", "Principal": "", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::secure-bucket/", "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012" } } } ] }

IP-Based Restrictions

{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowFromCorporateNetwork", "Effect": "Allow", "Principal": "", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::internal-bucket/", "Condition": { "IpAddress": { "aws:SourceIp": [ "192.0.2.0/24", "203.0.113.0/24" ] } } }, { "Sid": "DenyFromOtherIPs", "Effect": "Deny", "Principal": "", "Action": "s3:", "Resource": [ "arn:aws:s3:::internal-bucket", "arn:aws:s3:::internal-bucket/*" ], "Condition": { "NotIpAddress": { "aws:SourceIp": [ "192.0.2.0/24", "203.0.113.0/24" ] } } } ] }

VPC Endpoint Access Only

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyNonVPCAccess", "Effect": "Deny", "Principal": "", "Action": "s3:", "Resource": [ "arn:aws:s3:::private-bucket", "arn:aws:s3:::private-bucket/*" ], "Condition": { "StringNotEquals": { "aws:SourceVpce": "vpce-1234567890abcdef0" } } } ] }

MFA Delete Protection

{ "Version": "2012-10-17", "Statement": [ { "Sid": "RequireMFAForDelete", "Effect": "Deny", "Principal": "", "Action": [ "s3:DeleteObject", "s3:DeleteObjectVersion" ], "Resource": "arn:aws:s3:::critical-bucket/", "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "false" } } } ] }

Time-Based Access

{ "Version": "2012-10-17", "Statement": [ { "Sid": "BusinessHoursOnly", "Effect": "Deny", "Principal": "", "Action": "s3:", "Resource": [ "arn:aws:s3:::business-bucket", "arn:aws:s3:::business-bucket/*" ], "Condition": { "DateGreaterThan": { "aws:CurrentTime": "2024-01-01T18:00:00Z" }, "DateLessThan": { "aws:CurrentTime": "2024-01-02T09:00:00Z" } } } ] }

CloudTrail Logging

{ "Version": "2012-10-17", "Statement": [ { "Sid": "AWSCloudTrailAclCheck", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::cloudtrail-logs-bucket", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail" } } }, { "Sid": "AWSCloudTrailWrite", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::cloudtrail-logs-bucket/AWSLogs/123456789012/*", "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control", "AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail" } } } ] }

Condition Keys Reference

condition_keys: global: aws:SourceIp: "IP address or CIDR" aws:SourceVpc: "VPC ID" aws:SourceVpce: "VPC endpoint ID" aws:PrincipalOrgID: "AWS Organization ID" aws:CurrentTime: "ISO 8601 datetime" aws:MultiFactorAuthPresent: "true/false" aws:SecureTransport: "true/false"

s3_specific: s3:x-amz-acl: "ACL to apply" s3:x-amz-server-side-encryption: "AES256 or aws:kms" s3:x-amz-server-side-encryption-aws-kms-key-id: "KMS key ARN" s3:ExistingObjectTag/<key>: "Object tag value" s3:RequestObjectTagKeys: "Tags being set" s3:prefix: "Object key prefix" s3:max-keys: "Max keys in ListBucket" s3:object-lock-mode: "GOVERNANCE or COMPLIANCE"

operators: StringEquals: "Exact match" StringNotEquals: "Not equal" StringLike: "Wildcard match (*)" IpAddress: "IP in CIDR" NotIpAddress: "IP not in CIDR" DateGreaterThan: "After date" DateLessThan: "Before date" Bool: "Boolean check" Null: "Key exists/not exists"

Security Best Practices

security_checklist: block_public_access: setting: "Block all public access" how: | aws s3api put-public-access-block
--bucket my-bucket
--public-access-block-configuration
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

enable_versioning: purpose: "Protect against accidental deletion" how: | aws s3api put-bucket-versioning
--bucket my-bucket
--versioning-configuration Status=Enabled

enable_logging: purpose: "Audit access" how: | aws s3api put-bucket-logging
--bucket my-bucket
--bucket-logging-status '{"LoggingEnabled":{"TargetBucket":"log-bucket","TargetPrefix":"s3-access/"}}'

default_encryption: purpose: "Encrypt at rest" how: | aws s3api put-bucket-encryption
--bucket my-bucket
--server-side-encryption-configuration
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"alias/s3-key"}}]}'

lifecycle_policy: purpose: "Manage object lifecycle" example: "Transition to Glacier after 90 days, delete after 365"

Troubleshooting

common_issues: access_denied: symptoms: "403 AccessDenied error" checks: - "Verify IAM user/role permissions" - "Check bucket policy allows action" - "Verify resource ARN is correct" - "Check for explicit Deny statements" - "Verify bucket block public access settings" debug: | # Check effective policy aws s3api get-bucket-policy --bucket my-bucket

  # Test access
  aws s3api head-object --bucket my-bucket --key test.txt

policy_too_large: limit: "20 KB maximum" solutions: - "Use IAM policies instead" - "Consolidate statements" - "Use conditions instead of listing principals" - "Reference IAM roles instead of users"

invalid_principal: symptoms: "MalformedPolicy error" common_causes: - "Account ID doesn't exist" - "Role/user doesn't exist" - "Typo in ARN format" format: "arn:aws:iam::ACCOUNT-ID:root/role/user"

condition_not_working: checks: - "Verify condition key spelling" - "Check operator type matches value type" - "Ensure condition applies to correct action"

Policy Validation

Validate policy syntax

aws iam simulate-custom-policy
--policy-input-list file://policy.json
--action-names s3:GetObject
--resource-arns arn:aws:s3:::my-bucket/test.txt

Test policy with IAM Policy Simulator

Console: https://policysim.aws.amazon.com/

Check for public access

aws s3api get-bucket-policy-status --bucket my-bucket

List bucket policies

aws s3api get-bucket-policy --bucket my-bucket --output text

Terraform Example

resource "aws_s3_bucket" "example" { bucket = "my-secure-bucket" }

resource "aws_s3_bucket_public_access_block" "example" { bucket = aws_s3_bucket.example.id

block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }

resource "aws_s3_bucket_policy" "example" { bucket = aws_s3_bucket.example.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "EnforceSSL" Effect = "Deny" Principal = "" Action = "s3:" Resource = [ aws_s3_bucket.example.arn, "${aws_s3_bucket.example.arn}/*" ] Condition = { Bool = { "aws:SecureTransport" = "false" } } } ] }) }

Лучшие практики

  • Least privilege — минимальные необходимые права

  • Block public access — блокируй публичный доступ по умолчанию

  • Use conditions — добавляй условия для дополнительной защиты

  • Enable logging — логируй все обращения к bucket

  • Version control — храни политики в git

  • Regular audits — проверяй политики регулярно

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

social-media-marketing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

video-marketing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

k6-load-test

No summary provided by upstream source.

Repository SourceNeeds Review