【S3】Curlでファイルをアップロード時に所有者やバケットポリシーの問題

# 初めに
アドベントカレンダー記事の２発目です

最近、S3のObject OwnerShipがリリースしたので、所有者問題が解決されましたので、
簡単に使い方や、その前にアップロードしたオブジェクトの対応について書きます

# 概要
外部からS3にアップロードしたデータの所有者はアップロードの人でしたが、
curlなどでアップロードすると、ゲストユーザー（？）になるので、ディフォルトでオブジェクトを公開することになります
バケットの所有者はオブジェクトの権限も持ってないので、IAMの権限があってもアクセスや削除などができません

この記事は、S3のObject OwnerShipでアップロードされたものの所有者を強制的変更すると、
Object OwnerShipを設定する前に、アップロードされたオブジェクトの所有者を変更する方法を紹介します

# S3のObject OwnerShipの設定手順
## 1. バケットの作成
コンソールで適当に作成します
方法は割愛します

## 2. バケットポリシーの設定
- 特定のIPだけアクセス許可します
- PutObject時にACLを `bucket-owner-full-control` で設定してるリクエストだけ許可します
    - オーナーはバケット所有者になるので、 `Deny` ポリシーなくても公開はしないので不要です
        - 需要次第に設定しても問題ありません

```json
{
    "Version": "2012-10-17",
    "Id": "test_policy",
    "Statement": [
        {
            "Sid": "get",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{バケット名}/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "1.1.1.1/32"
                    ]
                }
            }
        },
        {
            "Sid": "put",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::{バケット名}/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                },
                "IpAddress": {
                    "aws:SourceIp": [
                        "1.1.1.1/32"
                    ]
                }
            }
        }
    ]
}
```

## 3. S3 Object Ownershipの設定
- S3 Object Ownershipの選択肢をオンにします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309401/fc97e6b7-83e8-ecab-5b6d-a41a1a8a157d.png)



## Curlでオブジェクトをアップロードする方法

```console
$ curl -XPUT -H "x-amz-acl:bucket-owner-full-control" -T {ファイル名} https://{バケット名}.s3-ap-northeast-1.amazonaws.com/{ファイル名}
```

### 検証
```console
# curl -XPUT -T test.txt https://${バケット名}.s3-ap-northeast-1.amazonaws.com/test.txt
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>829150681872CD3C</RequestId><HostId>W1sEi85Oa03yMlm8SSwFKvSv2RY7B1FhnhEQhspvsBX0VCPND0esPhg7hunsUpJoWiIHuik5r+k=</HostId></Error>
'bucket-owner-full-control' のヘッダーがないので、失敗

# curl -XPUT -H "x-amz-acl:bucket-owner-full-control" -T test.txt https://${バケット名}.s3-ap-northeast-1.amazonaws.com/test.txt
エラーが出ないので、成功
```

### 所有者
- `bucket-owner-full-control` がついていないままアップロードしたもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309401/74d82508-758e-f724-ab34-11488ea855a2.png)



- `bucket-owner-full-control` がついているアップロードしたもの
    - ちゃんと所有者を変更してくれました
    
![スクリーンショット 2020-10-20 13.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309401/f3766de6-f737-eeba-6dab-a31962328b25.png)


# オブジェクトの所有者を変更する方法
## 1. バケットの全オブジェクトを取得
- 必要であれば、 `--prefix` をつけて、特定のプレフィックスを指定できます
- 件数が多ければ、一回で全件表示出来ない場合があるので、 `NextToken` を取得し、 `--starting-token` をつけて続きを取得しましょう

https://docs.aws.amazon.com/cli/latest/reference/s3api/list-objects-v2.html

```console
# aws s3api list-objects-v2 --bucket ${バケット名} --query Contents[*].[Key] --output text >> /tmp/key.txt
```

## 2. オブジェクトのACLを変更
- `1` で取ったリストをもとに、オブジェクトのACLを変更します

```console
# cat key.txt | xargs -I KEY aws s3api put-object-acl --bucket ${バケット名} --key KEY --acl bucket-owner-full-control --no-sign-request
```

## 3. オブジェクトの所有者を変更
- `1` で取ったリストをもとに、オブジェクトの所有者を変更します

```console
# cat key.txt |xargs -I KEY aws s3 cp s3://${バケット名}/KEY s3://${バケット名}/KEY --metadata-directive REPLACE
```

# 最後に
S3のObject OwnerShipがリリースしたことに、長年に悩まされたことを解消できました。
ありがとうございます!

この記事もみんなさんの悩みを解決できたら嬉しいですね