mcpField
S
s3-bucket-policy

s3-bucket-policy

github.com/dengineproblemセキュリティ65 回インストール
リポジトリ

s3-bucket-policy について

s3-bucket-policy は、AWS S3 バケットポリシーの作成・確認・改善を支援するスキルです。アクセス制御の例やセキュリティ条件、ポリシー検証、トラブルシューティングを扱います。

主な機能

  1. 01最小権限ポリシーの指針
  2. 02パブリックおよびクロスアカウントの例
  3. 03CloudFront と CloudTrail のポリシー例
  4. 04暗号化・IP・VPC・MFA の制限
  5. 05条件キーのリファレンス
  6. 06ポリシー検証とトラブルシューティング

活用シーン

  1. 01静的サイト向けバケットポリシーの作成
  2. 02クロスアカウントのバケットアクセス設定
  3. 03IP または VPC エンドポイントによる制限
  4. 04S3 AccessDenied エラーの調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dengineproblem/agents-monorepo --skill s3-bucket-policy

インストール後、Claude Code で/s3-bucket-policyを入力して読み込まれたか確認します。

詳細

AWS S3 バケットポリシーのエキスパート

セキュリティ、アクセス制御、コンプライアンスに重点を置いた、AWS S3 バケットポリシーの作成、分析、最適化に関する専門的なガイダンスです。

ポリシーの構造

{
  "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"
        }
      }
    }
  ]
}

基本原則

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"

一般的なポリシーパターン

静的ウェブサイト向けのパブリック読み取り
{
  "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"
        }
      }
    }
  ]
}
クロスアカウントアクセス
{
  "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 オリジンアクセスコントロール
{
  "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"
        }
      }
    }
  ]
}
暗号化の強制
{
  "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 ベースの制限
{
  "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 エンドポイント経由のアクセスのみ許可
{
  "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 による削除保護
{
  "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"
        }
      }
    }
  ]
}
時間ベースのアクセス
{
  "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 ロギング
{
  "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:
  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_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"

トラブルシューティング

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"

ポリシーの検証

# 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 の例

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"
          }
        }
      }
    ]
  })
}

ベストプラクティス

  1. 最小権限 — 必要最小限の権限を設定します
  2. パブリックアクセスをブロック — デフォルトでパブリックアクセスをブロックします
  3. 条件を使用 — 保護を強化する条件を追加します
  4. ロギングを有効化 — バケットへのすべてのアクセスを記録します
  5. バージョン管理 — ポリシーを git に保存します
  6. 定期的な監査 — ポリシーを定期的に確認します

コメント