S3 Selectを使う際は”s3:ListBucket”権限も付与したほうが良いですよ

2020.06.30

実装内容

S3 Selectを使った、以下のような構成を実装していました。

  1. ParquetファイルをS3バケットにアップロード
  2. S3バケットにはParquetファイル(suffixが.parquet)アップロードでトリガされるS3イベント通知を設定。Lambda関数を実行する。
  3. Lambda関数上でS3イベント通知のイベントペイロードをパースし、アップロードされたファイルに対してS3 Select
  4. S3 Selectの結果、レコードヒットしたら所定のSlackチャンネルにメッセージポスト

S3 Selectを実行するのに必要な権限は?

S3 Selectを使う際にPrincipal側に必要な権限は、対象バケット内オブジェクトに対する s3:GetObject のみです。 今回の場合はS3 Selectを実行するのはLambda関数なので、Lambda関数にアタッチするロールに以下のようにs3:GetObject権限をアタッチしました。

IAMポリシー例

{
    "Action": [
        "s3:GetObject"
    ],
    "Resource": "arn:aws:s3:::(S3バケット名)/*",
    "Effect": "Allow"
}

情報元

以下SelectObjectContentがいわゆるS3 Selectに対応するAPIです。 - SelectObjectContent | S3 API Reference

こちらに以下Permissionについての記載があります。

Permissions

You must have s3:GetObject permission for this operation. Amazon S3 Select does not support anonymous access. For more information about permissions, see Specifying Permissions in a Policy in the Amazon Simple Storage Service Developer Guide.

出くわしたエラー

試しにS3バケットにParquetファイルをアップしてLambda関数を実行したところ、以下のようなエラーが発生します。。

[ERROR] ClientError: An error occurred (AccessDenied) when calling the SelectObjectContent operation: Access Denied
(Trace情報が続く)

Access Deniedですので、以下のようなことを確認しましたが解決できません。

  • S3 Selectってs3:GetObject以外にも権限必要?→前述のAPIドキュメント等を再確認
  • S3バケットポリシーで弾かれている?
  • OrganizationsのSCPで弾かれている?(お客様環境だったのでどういう設定になっているか詳しくなかったため)
  • ちょうどS3のAccess Deniedエラートラブルシュートのナレッジセンター記事があったのでこれを元に色々調査

解決法

以下のブログエントリに書かれているとおりです。

selectの実行にListBucketは必要ありませんが、指定したオブジェクトが見つからない場合に「An error occurred (NoSuchKey) when calling the SelectObjectContent operation: The specified key does not exist.」を出力するためです。 ListBucketがないと、「An error occurred (AccessDenied) when calling the SelectObjectContent operation: Access Denied」となってしまい切り分けが難しくなります。

IAMポリシー追加

{
    "Action": [
        "s3:ListBucket"
    ],
    "Resource": "arn:aws:s3:::(S3バケット名)",
    "Effect": "Allow"
}

再実行するとエラーメッセージが変わりました。

[ERROR] NoSuchKey: An error occurred (NoSuchKey) when calling the SelectObjectContent operation: The specified key does not exist.
(Trace情報が続く)

指定したキーのオブジェクトが無いよ、とのこと。Access Deniedからここにたどり着くのは厳しい。。

なぜThe specified key does not existになったのか

本題から逸れますがこの点についても述べておきます。

S3 Selectで使用するS3オブジェクトのキーは、S3イベントのペイロードに記載されているものを使っています。ですのでオブジェクトは存在しているはずです。

なのですが、公式開発者ガイドを見ていて気づきました。オブジェクトキーはURLデコードする必要がありました。

s3 キーは、イベントに関与したバケットとオブジェクトに関する情報を提供します。オブジェクトのキー名の値は URL エンコードされます。たとえば、「red flower.jpg」は「red+flower.jpg」となります

今回の場合、テストしたオブジェクトのキーに「=」が含まれていたため、S3イベント通知のペイロードには「%3D」とURLエンコードされた値が入っていました。それをデコードせずにS3 Selectに突っ込んだためThe specified key does not existになっていました。

Lambda関数の修正

Pythonです。 urllib.parse.unquoteを使います。

+ import urllib.parse
def lambda_handler(event, context):
      logger.debug(event)

      for r in event['Records']:
          s3 = r['s3']
          bucket = s3['bucket']['name']
+         key = urllib.parse.unquote(s3['object']['key'])
-         key = s3['object']['key']
          logger.info("Object path: %s/%s", bucket, key)

まとめ

  • S3 Selectを使う際はPrincipalにs3:GetObjectに加えてs3:ListBucketも与えておくとデバッグがはかどります。
  • S3イベント通知でオブジェクトキーを使う際はURLデコードをお忘れなく。