AWS CLI の aws s3 コマンドで Access Denied になった話

記事タイトルとURLをコピーする

はじめに

こんにちは、アプリケーションサービス本部 ディベロップメントサービス1課の北出です。
今回は、お客様先の環境で、Codebuildを使ってS3バケット内のファイルを移動させる際にアクセス権限不足のエラーになってしまったので、戒めも込めて紹介します。
今回紹介する内容は結構多くの方に当てはまりそうな気もするので、読んでいただき、不具合を回避できればと思います。

何があったのか

開発したシステムはシンプルで、定期的にCodeBuildを実行し、Codebuild内では、様々な処理をします。
その処理の中に、あるバケット内のファイルを同じバケット内ので別フォルダ(≒プレフィックス)に移動させる処理がありました。
buildspec では以下のシンプルなAWS CLI コマンドを実行させるものでした。

 aws s3 mv s3://$BUCKET_NAME/$FILE_NAME.txt s3://$BUCKET_NAME/backup/

CodeBuildに割り当てたポリシーの一部は以下です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::${BucketName}/*"
        }
    ]
}

5MB以下のテストファイルでテストしていた時は特に問題なく動作していたのですが、運用を始めて、10MB以上の大きなファイルを扱うと以下のようなエラーが出ていました。

move failed: s3://test-bucket/large.txt to s3://test-bucket/backup/large.txt An error occurred (AccessDenied) when calling the GetObjectTagging operation: User: arn:aws:iam::123456789012:user/test-user is not authorized to perform: s3:GetObjectTagging on resource: "arn:aws:s3:::test-bucket/large.txt" because no identity-based policy allows the s3:GetObjectTagging action

エラーの内容としては単純で、 s3:GetObjectTagging の権限がないという内容なので、その権限を付与してあげればいい(この時はバケットポリシーも使っていたのでバケットポリシーにも権限付与)のですが、なぜオブジェクトにタグを一切つけておらず、大きいサイズの時だけこのようなエラーになるのかがわからず、今回調査しました。

調査結果

調べたのですが、重要なことは以下の2点です。

  • 大きなファイルサイズでは自動的にマルチパートコピーになる。
  • マルチパートコピーでは、GetObjectTagging が必要になる。

大きなファイルサイズでは自動的にマルチパートコピーになる。

こちらのサイトmultipart_threshold にあるように、AWS CLI のS3 コマンドで、アップロードやダウンロード、コピーの際、設定したファイルサイズを超えると自動でマルチパート処理になるようです。
デフォルト設定では8MBなので、今回のように10MB以上のファイルではマルチパート処理になります。
マルチパート処理になったから何の関係があるの?となるかもしれませんが、次が特に重要な原因になります。

マルチパートコピーでは、GetObjectTagging が必要になる。

何をいっているんだ?となるかもしれませんが、こちらのサイトマルチパートコピーのファイルプロパティとタグ を見ると以下のような記述があります。

デフォルトで、マルチパートコピーを実行する s3 名前空間の AWS CLI バージョン 2 のコマンドは、ソースのすべてのタグと、プロパティのセット (content-type、content-language、content-encoding、content-disposition、cache-control、expires) をコピー先に転送します。

これにより、AWS CLI バージョン 1 を使用する場合には行われなかった追加の AWS API コールが Amazon S3 エンドポイントに対して実行されることになる場合があります。これには、HeadObject、GetObjectTagging、および PutObjectTagging が含まれます。

APIコールにGetObjectTagging が含まれますと書いてありますね。
これ以上の記述は見つけられなかったのですが、オブジェクトのタグの有無にかかわらず、マルチパートの場合だけ GetObjectTagging のAPIが実行されているのだと思います。
AWS CLI V1からV2への移行 のドキュメントにも マルチパートコピーのファイルプロパティとタグの Amazon S3 処理が改善されました とあるので、AWS CLI バージョン2になったことによる仕様変更のようです。
マルチパート処理特有に仕様のようで、小さいファイルサイズの場合だと別に GetObjectTagging が必要ないというのが厄介です。

タグありオブジェクトで調べてみた。

  • 0KBの空のテキストファイル
  • 15MBのテキストファイル

を作成し、両方にタグを追加しました。ポリシーは以下の通りです。

"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"

この時に aws s3 mv をするとどうなるのか検証してみます。

0KBのファイルの場合

以下のエラーになりました。

move failed: s3://test-bucket/mini.txt to s3://test-bucket/backup/mini.txt An error occurred (AccessDenied) when calling the CopyObject operation: Access Denied

このように、 CopyObject の権限がないというエラーになりました。
ちなみに、タグなしの0KBのオブジェクトはエラーなく移動ができました。

15MBのファイルの場合

以下のエラーになりました。

move failed: s3://test-bucket/large.txt to s3://test-bucket/backup/large.txt An error occurred (AccessDenied) when calling the GetObjectTagging operation: User: arn:aws:iam::123456789012:user/test-user is not authorized to perform: s3:GetObjectTagging on resource: "arn:aws:s3:::test-bucket/large.txt" because no identity-based policy allows the s3:GetObjectTagging action

タグなしのオブジェクトでエラーが出ていたので、これは予想通りの挙動です。

続いて、ポリシーに以下の二つを追加します。

"s3:GetObjectTagging",
"s3:PutObjectTagging"

0KBのファイルの場合

エラーなく移動ができました。移動先にもタグは保持されていました。

15MBのファイルの場合

エラーなく移動ができました。移動先にもタグは保持されていました。

まとめ

今回の調査で、AWS CLI v2 の aws s3 mv コマンドの挙動について以下の点が明らかになりました。

  • aws s3 mv コマンドは、実質的にオブジェクトのコピー (s3:PutObject, s3:GetObject) と削除 (s3:DeleteObject) を行います。
  • コピー対象のファイルサイズが multipart_threshold (デフォルト 8MB) を超えると、自動的にマルチパートコピーが実行されます。
  • AWS CLI v2 のマルチパートコピーでは、デフォルトでオブジェクトのタグや一部のメタデータをコピーしようとします。この際、タグの有無にかかわらず s3:GetObjectTagging アクションが呼び出されます。
  • オブジェクトに実際にタグが付いている場合、それをコピー先にも保持するためには、s3:GetObjectTagging (コピー元のタグ取得) と s3:PutObjectTagging (コピー先へのタグ設定) の両方の権限が必要になります。これはファイルサイズ(マルチパートかどうか)に関わらず適用されます。

これらの挙動を踏まえ、aws s3 mv コマンドを実行する際に必要な IAM ポリシーのアクションを、ファイルサイズ(マルチパート処理の有無)とオブジェクトのタグの有無で整理すると、以下のようになります。

条件 必要なS3アクション 備考
小さいファイル (< 8MB)
タグなし
s3:PutObject
s3:GetObject
s3:DeleteObject
基本的な権限のみで移動可能です。
小さいファイル (< 8MB)
タグあり
s3:PutObject
s3:GetObject
s3:DeleteObject
s3:GetObjectTagging
s3:PutObjectTagging
タグ情報をコピー先に引き継ぐために、タグ関連の権限が追加で必要です。
大きいファイル (≧ 8MB)
タグなし
s3:PutObject
s3:GetObject
s3:DeleteObject
s3:GetObjectTagging
マルチパートコピーの仕様により、タグが存在しなくても s3:GetObjectTagging が呼び出されるため、この権限が必要です。今回遭遇したエラーはこのケースでした。
大きいファイル (≧ 8MB)
タグあり
s3:PutObject
s3:GetObject
s3:DeleteObject
s3:GetObjectTagging
s3:PutObjectTagging
マルチパートコピーの仕様と、タグ情報を引き継ぐために、両方のタグ関連権限が必要です。

結論として

どのようなファイルサイズやタグの有無であっても aws s3 mv コマンドを確実に成功させるためには、IAM ポリシーに以下の S3 アクションを含めることが推奨されます。

"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:GetObjectTagging",
"s3:PutObjectTagging"

さいごに

自分はS3オブジェクトを扱う際は "s3:PutObject", "s3:GetObject", "s3:DeleteObject", の3つで大丈夫と思っていたので、今回のケースでのエラーは予想していませんでした。
今後は s3:GetObjectTagging", "s3:PutObjectTagging も基本的には必要な権限という認識を持とうと思います。
あとは、テスト時に、お客様に扱いうるファイルの最大サイズも念入りに確認しておくとよかったと思います。この時はファイルサイズで処理が変わるという認識を持っていなかったので難しいですが。

今回は AWS CLI でのケースで、公式ドキュメントもそれを参照しました。 AWS SDK の場合は仕様が違う可能性もありますのでご注意ください。

北出 宏紀(執筆記事の一覧)

アプリケーションサービス本部ディベロップメントサービス3課

2024年9月中途入社です。