mcpField
A
aws-s3

aws-s3

github.com/bagelholeクラウド・DevOps1.2k347 回インストール23 週間前に更新
リポジトリ

aws-s3 について

AWS CLI v2でAmazon S3を管理するためのスキルです。バケットのセキュリティ、ライフサイクル、レプリケーション、アクセス制御、一般的な問題の解決を扱います。

主な機能

  1. 01バケットの作成と保護
  2. 02バケットポリシーとアクセス制御の設定
  3. 03オブジェクトのライフサイクル設定
  4. 04クロスリージョンレプリケーションの構成
  5. 05署名付きURLの生成
  6. 06アクセスやポリシーの問題解決

活用シーン

  1. 01本番データ用の安全なバケット作成
  2. 02オブジェクトの低コストストレージへの移行
  3. 03クロスリージョンの災害復旧設定
  4. 04プライベートオブジェクトへの一時アクセス

インストール

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

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill aws-s3

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

詳細

AWS S3

本番環境向けのセキュリティ、ライフサイクルポリシー、レプリケーション、アクセス制御により、Amazon S3 オブジェクトストレージを管理します。

このスキルを使用する場面

  • セキュリティ強化(暗号化、パブリックアクセスブロック、バージョニング)を適用して S3 バケットを作成する
  • HTTPS の強制、IP 範囲の制限、クロスアカウントアクセスの許可を行うバケットポリシーを作成する
  • オブジェクトをストレージクラス間で移行するライフサイクルルールを設定する
  • 災害復旧のためにクロスリージョンレプリケーションを設定する
  • プライベートオブジェクトへの一時的なアクセス用に、署名付き URL を生成する
  • 静的ウェブサイトホスティングまたは CloudFront オリジンを設定する
  • アクセス拒否エラーやポリシーの競合をトラブルシューティングする

前提条件

  • AWS CLI v2 がインストールされ、設定済みであること
  • IAM 権限:s3:*、s3-object-lambda:*、kms:*(SSE-KMS 用)
  • レプリケーションの場合:レプリケーション権限を持つ IAM ロールと、対象リージョンの宛先バケット
  • ログ記録の場合:適切な ACL を設定した、別のログ記録用バケット

バケットの作成と保護

# Create a bucket (us-east-1 does not need LocationConstraint)
aws s3api create-bucket \
  --bucket my-app-data-prod \
  --region us-east-1

# Create a bucket in another region
aws s3api create-bucket \
  --bucket my-app-data-dr \
  --region us-west-2 \
  --create-bucket-configuration LocationConstraint=us-west-2

# Block ALL public access (always do this first)
aws s3api put-public-access-block \
  --bucket my-app-data-prod \
  --public-access-block-configuration '{
    "BlockPublicAcls": true,
    "IgnorePublicAcls": true,
    "BlockPublicPolicy": true,
    "RestrictPublicBuckets": true
  }'

# Enable versioning
aws s3api put-bucket-versioning \
  --bucket my-app-data-prod \
  --versioning-configuration Status=Enabled

# Enable server-side encryption with SSE-KMS
aws s3api put-bucket-encryption \
  --bucket my-app-data-prod \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "alias/s3-key"
      },
      "BucketKeyEnabled": true
    }]
  }'

# Enable access logging
aws s3api put-bucket-logging \
  --bucket my-app-data-prod \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-access-logs-bucket",
      "TargetPrefix": "s3-logs/my-app-data-prod/"
    }
  }'

# Add tags
aws s3api put-bucket-tagging \
  --bucket my-app-data-prod \
  --tagging '{
    "TagSet": [
      {"Key": "Environment", "Value": "production"},
      {"Key": "Team", "Value": "platform"},
      {"Key": "DataClassification", "Value": "confidential"}
    ]
  }'

バケットポリシー

# Apply a bucket policy (enforce HTTPS and restrict to VPC endpoint)
aws s3api put-bucket-policy \
  --bucket my-app-data-prod \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "DenyInsecureTransport",
        "Effect": "Deny",
        "Principal": "*",
        "Action": "s3:*",
        "Resource": [
          "arn:aws:s3:::my-app-data-prod",
          "arn:aws:s3:::my-app-data-prod/*"
        ],
        "Condition": {
          "Bool": {"aws:SecureTransport": "false"}
        }
      },
      {
        "Sid": "RestrictToVPCEndpoint",
        "Effect": "Deny",
        "Principal": "*",
        "Action": "s3:*",
        "Resource": [
          "arn:aws:s3:::my-app-data-prod",
          "arn:aws:s3:::my-app-data-prod/*"
        ],
        "Condition": {
          "StringNotEquals": {
            "aws:sourceVpce": "vpce-abc123"
          }
        }
      }
    ]
  }'

クロスアカウントアクセスのポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrossAccountRead",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:role/DataAnalystRole"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-data-prod",
        "arn:aws:s3:::my-app-data-prod/shared/*"
      ]
    }
  ]
}

ライフサイクルルール

# Apply a comprehensive lifecycle configuration
aws s3api put-bucket-lifecycle-configuration \
  --bucket my-app-data-prod \
  --lifecycle-configuration '{
    "Rules": [
      {
        "ID": "TierDownOldData",
        "Status": "Enabled",
        "Filter": {"Prefix": "data/"},
        "Transitions": [
          {"Days": 30, "StorageClass": "STANDARD_IA"},
          {"Days": 90, "StorageClass": "GLACIER_IR"},
          {"Days": 180, "StorageClass": "GLACIER"},
          {"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
        ]
      },
      {
        "ID": "ExpireLogs",
        "Status": "Enabled",
        "Filter": {"Prefix": "logs/"},
        "Expiration": {"Days": 90},
        "Transitions": [
          {"Days": 7, "StorageClass": "STANDARD_IA"},
          {"Days": 30, "StorageClass": "GLACIER"}
        ]
      },
      {
        "ID": "CleanupOldVersions",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "NoncurrentVersionTransitions": [
          {"NoncurrentDays": 30, "StorageClass": "STANDARD_IA"},
          {"NoncurrentDays": 90, "StorageClass": "GLACIER"}
        ],
        "NoncurrentVersionExpiration": {"NoncurrentDays": 180}
      },
      {
        "ID": "AbortIncompleteUploads",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
      },
      {
        "ID": "ExpireDeleteMarkers",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "Expiration": {"ExpiredObjectDeleteMarker": true}
      }
    ]
  }'

クロスリージョンレプリケーション

# Enable replication (requires versioning on both buckets)
aws s3api put-bucket-replication \
  --bucket my-app-data-prod \
  --replication-configuration '{
    "Role": "arn:aws:iam::123456789012:role/S3ReplicationRole",
    "Rules": [
      {
        "ID": "ReplicateAll",
        "Status": "Enabled",
        "Priority": 1,
        "Filter": {"Prefix": ""},
        "Destination": {
          "Bucket": "arn:aws:s3:::my-app-data-dr",
          "StorageClass": "STANDARD_IA",
          "EncryptionConfiguration": {
            "ReplicaKmsKeyID": "arn:aws:kms:us-west-2:123456789012:key/dr-key-id"
          },
          "Metrics": {"Status": "Enabled", "EventThreshold": {"Minutes": 15}},
          "ReplicationTime": {"Status": "Enabled", "Time": {"Minutes": 15}}
        },
        "DeleteMarkerReplication": {"Status": "Enabled"},
        "SourceSelectionCriteria": {
          "SseKmsEncryptedObjects": {"Status": "Enabled"}
        }
      }
    ]
  }'

# Check replication status
aws s3api head-object \
  --bucket my-app-data-prod \
  --key data/important-file.json \
  --query "ReplicationStatus"

署名付き URL

# Generate a presigned URL for downloading (valid 1 hour)
aws s3 presign s3://my-app-data-prod/reports/quarterly.pdf \
  --expires-in 3600

# Generate a presigned URL for uploading
aws s3 presign s3://my-app-data-prod/uploads/user-file.zip \
  --expires-in 3600

# Presigned URL with specific content type (using the API directly)
aws s3api generate-presigned-url \
  --client-method put_object \
  --params '{"Bucket":"my-app-data-prod","Key":"uploads/photo.jpg","ContentType":"image/jpeg"}' \
  --expires-in 3600

S3 の一般的な操作

# Sync a local directory to S3
aws s3 sync ./build s3://my-app-data-prod/static/ \
  --delete \
  --exclude "*.tmp" \
  --cache-control "max-age=31536000" \
  --content-encoding "gzip"

# Copy with storage class
aws s3 cp large-archive.tar.gz s3://my-app-data-prod/archives/ \
  --storage-class GLACIER_IR

# List objects with size summary
aws s3 ls s3://my-app-data-prod/ --recursive --summarize --human-readable

# Remove all objects with a prefix
aws s3 rm s3://my-app-data-prod/temp/ --recursive

# Get bucket size via CloudWatch (most efficient for large buckets)
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name BucketSizeBytes \
  --dimensions Name=BucketName,Value=my-app-data-prod Name=StorageType,Value=StandardStorage \
  --start-time "$(date -u -d '2 days ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 86400 \
  --statistics Average \
  --output table

Terraform S3 バケット

resource "aws_s3_bucket" "main" {
  bucket = "my-app-data-prod"

  tags = {
    Environment        = "production"
    DataClassification = "confidential"
  }
}

resource "aws_s3_bucket_versioning" "main" {
  bucket = aws_s3_bucket.main.id
  versioning_configuration {
    status = "Enabled"
  }
}

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

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

resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.s3.arn
    }
    bucket_key_enabled = true
  }
}

resource "aws_s3_bucket_lifecycle_configuration" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    id     = "tier-down"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    noncurrent_version_transition {
      noncurrent_days = 30
      storage_class   = "GLACIER"
    }

    noncurrent_version_expiration {
      noncurrent_days = 180
    }

    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
}

resource "aws_s3_bucket_policy" "enforce_https" {
  bucket = aws_s3_bucket.main.id

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

トラブルシューティング

問題原因対処方法
GetObject でアクセス拒否バケットポリシーまたは IAM によってアクセスが拒否されていますバケットポリシー、IAM ポリシー、パブリックアクセスブロックを確認します
PutObject でアクセス拒否必須の暗号化ヘッダーがありませんSSE ヘッダーを追加し、バケットポリシーの暗号化条件を確認します
署名付き URL で 403URL の期限切れ、またはリージョンが間違っていますURL を再生成し、バケットのリージョンと一致していることを確認します
レプリケーションが機能しないコピー元またはコピー先でバージョニングが無効です両方のバケットでバージョニングを有効にします
ライフサイクルによる移行が行われないルールのフィルターがオブジェクトに一致しませんプレフィックスとタグのフィルターを確認し、ルールのステータスを確認します
バケットを削除できないバケットが空ではないか、バージョン付きオブジェクトが残っていますまずすべてのオブジェクトとバージョンを削除し、バージョニングを無効にします
大きなファイルのアップロードが遅い単一パートのアップロードを使用していますaws s3 cp(マルチパートが自動適用されます)を使用するか、マルチパートのしきい値を設定します
クロスアカウントアクセスが拒否されるバケットポリシーと IAM ポリシーの両方が必要ですバケットポリシーと呼び出し元の IAM ポリシーの両方で許可します
Object Lock によって削除できないガバナンスモードまたはコンプライアンスモードが有効です(権限がある場合は)ガバナンスのバイパスを使用するか、保持期間が終了するまで待ちます

関連スキル

  • aws-iam - バケットおよびオブジェクトのアクセスポリシー
  • aws-vpc - プライベートな S3 アクセス用の VPC エンドポイント
  • aws-cost-optimization - ストレージクラスの最適化
  • terraform-aws - S3 の IaC デプロイ
  • cloudformation - AWS ネイティブの S3 テンプレート

コメント