Amazon S3 Access Grants とは何か。何が嬉しいのか。

この記事は [**AWS Community Builders Advent Calendar 2023**](https://qiita.com/advent-calendar/2023/aws-community-builders) 11 日目の記事です。

## S3 Access Grants とは
AWS re:Invent 2023 で発表された Amazon S3 の新たなアクセスコントロール機能です。S3 Access Grants を使用すると、特定のスコープ (バケット、プレフィックス、オブジェクト) に対し、Microsoft Entra ID や Okta などの外部ディレクトリのユーザー、グループに基づいてアクセスを許可できます。

https://aws.amazon.com/jp/about-aws/whats-new/2023/11/amazon-s3-access-grants-data-lake-permissions/

## S3 Access Grants の特徴
### 企業ディレクトリとの統合
S3 Access Grants は同じく AWS re:Invent 2023 期間中に発表された AWS IAM Identity Center の [Trusted identity propagation](https://docs.aws.amazon.com/singlesignon/latest/userguide/trustedidentitypropagation-overview.html) という機能がベースになっています。

https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-analytics-users-data-access-iam-identity-center/

Trusted identity propagation により、AWS IAM Identity Center と S3 Access Grants の認可エンジンの間でやり取りが行われて、ユーザー、グループの ID 情報が S3 まで受け渡されます。AWS IAM Identity Center は Microsoft Entra ID、Okta、Ping、OneLogin などの IdP と接続できるため、企業ディレクトリの情報をもとにアクセス許可を設定できます。

Trusted identity propagation は S3 Access Grants の他にも、Amazon Redshift のクエリエディタ、Amazon QuickSight、AWS Lake Formation、一部のサードパーティー製品で使用できます。

### アクセス許可のスケール
S3 Access Grants は既存のバケットポリシーや S3 アクセスポイントと比較して大規模で動的なアクセス制御が可能です。

Amazon S3 に対するアクセス制御の一般的な方法は IAM ポリシーとバケットポリシーの使用です。ただし IAM ポリシーは 5 KB、バケットポリシーは 20 KB の制限があるため、データセットや許可を行うプリンシパルの数が小規模な場合に適しています。

データセットやプリンシパルの数が拡大し、より多くのポリシーが必要になった場合のアプローチとして、[S3 アクセスポイント](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/access-points.html) を使用できます。リージョン毎にアカウントあたり最大 10,000 個のアクセスポイントを作成でき、各アクセスポイントが独自のポリシーを持つことができます。一方でクライアント見た場合、複数のアクセスポイントから適切なアクセスポイントを利用するためのメカニズムが必要です。

これらの既存機能は引き続き有用ですが、どちらかというと静的なアクセスパターンに適しています。企業内のユーザーやグループに対するアクセス制御は動的であることも多いでしょう。制御が必要なデータや人が増えたり減ったりするたびに管理者がポリシーを変更するのは骨が折れる作業です。

S3 Access Grants を使用すると、直感的な方法で IAM プリンシパルや企業ディレクトリのユーザー、グループにアクセス許可を割り当てることができます。またリージョン毎にアカウントあたり最大 100,000 件の許可を定義できるため、非常にスケーラブルです。

### Auditability (可監査性)
AWS IAM Identity Center の Trusted identity propagation と S3 Access Grants の統合により、リソースにアクセスしたエンドユーザーの ID が S3 に伝搬されます。誰がどのデータにアクセスしたかを CloudTrail のデータイベントで監査できます。

具体的には CloudTrail の S3 データイベントに `onBehalfOf` が追加され、アクセス元のアイデンティティストアおよびユーザー ID が直接記録されます。

```json
{
  …
  "userIdentity": {
    "type": "AssumeRole",
    "principalId": "AROAEXAMPLE:access-grants-01234567-89ab-cdef…",
    "accountId": "111111111111",
    …
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        …
        "arn": "arn:aws:iam::111111111111:role/s3-access-grants-role"
        …
      }
      …
    },
    "onBehalfOf": {
      "userId": "b3743f38-…",
      "identityStoreArn": "arn:aws:identitystore::222222222222:identitystore/d-123456780a"
    }
  }
…  
```
[How to develop a user-facing data application with IAM Identity Center and S3 Access Grants (Part 2)](https://aws.amazon.com/jp/blogs/storage/how-to-develop-a-user-facing-data-application-with-iam-identity-center-and-s3-access-grants-part-2/) より引用

## S3 Access Grants を介したアクセスの仕組み
### S3 Access Grants の概念
前提として以下の概念を理解する必要があります。

* **S3 Access Grants instance**
誰がどのデータに対してどのレベルのアクセス権を持つかを個々に定義するための論理コンテナです。リージョン毎にアカウントあたり 1 つのインスタンスを作成できます。

* **Locations (ロケーション)**
S3 Access Grants インスタンスがアクセスを許可できるデータロケーションを定義します。特定の S3 バケット、プレフィックス、またはオブジェクトをスコープとし、その Locaiton に対応する IAM Role を関連付けます。S3 Access Grants インスタンスごとに 1,000 の S3 Access Grants ロケーションを登録できます。

* **Grants (権限)**
個々のアクセス許可の定義です。どのエンティティ (IAM プリンシパル、企業ディレクトリ内のユーザーまたはグループ) がどの Location にどのアクセスレベル (READ/WRITE/READWRITE) を持つかを定義します。S3 Access Grants インスタンスごとに100,000 の許可を作成できます。

### どのように S3 にアクセスするのか
ユーザー (クライアントアプリケーション) は [GetDataAccess API](https://docs.aws.amazon.com/AmazonS3/latest/API/API_control_GetDataAccess.html) を介して S3 Access Grants insntace と通信し、データへのアクセスをリクエストします。リクエストが承認されると一時的な認証情報が返されるため、それを使用して対象の S3 リソースにアクセスできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/ab0873f6-267f-6242-208c-d06543c0639e.png)

## やってみる
### S3 Access Grants instance の作成
S3 コンソールの Access Grants から「S3 Acess Grants インスタンスを作成」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/225cb2e1-56bd-38d1-cd09-543cd2803a07.png)

ステップ 1 S3 Access Grants インスタンスを作成で「IAM アイデンティティセンターインスタンスを追加にチェックをいれ、構成済みの IAM Identity Center ARN を入力し、次へ進みます。

:::note warn
S3 Access Grants インスタンスの IAM Identity Center インスタンスは同じリージョンである必要があります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/9bb9b48a-d250-e14c-e01d-0f03eb78dda8.png)

ステップ 2 のロケーションの登録では何も入力せずに、戻るボタンをクリックします。ステップ 1 に戻ると S3 Grant Access インスタンスが作成されているため、キャンセルボタンをクリックしてウィザードを終了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/1ba2ac6e-e2c4-1bbc-96d9-ec9046b78ed5.png)

:::note info
Locations に紐づける IAM Role のポリシーで、S3 Access Grants Instance ARN を Condition 句に指定できます。ここではインスタンス作成のみを行い、後ほどロケーションを登録します。
:::

S3 Access Grants インスタンスが作成されると、IAM Identity Center 側にも AWS マネージドアプリケーション (S3AccessGrants-\<InstanceName>) が自動で登録されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/d73c7722-b6bb-0dbe-8a36-fd3b24ae7f3f.png)

### ロケーションの登録
最初に Location に紐づける IAM Role を作成します。ここではドキュメントに記載されているサンプルを参考に IAM ポリシーを作成してアタッチしました。`s3:AccessGrantsInstanceArn` を先ほど作成したインスタンスの ARN に変更してください。

https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-grants-location.html

:::note warn
KMS キーを使用したサーバー側暗号化を使用している場合、IAM ポリシーに KMS のアクセス許可を追加することを忘れずに！
:::

作成したロールの信頼ポリシーには以下を設定します。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "access-grants.s3.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:SetContext"
            ]
        }
    ]
}
```

ロールの作成が完了したら、ロケーションを登録します。S3 Access Grants インスタンスの詳細画面でロケーションタブを選択し、「ロケーションを登録」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/bdf33a95-4890-5471-24e1-21c3f5723266.png)

ロケーション範囲にアクセス許可のスコープ (バケット、プレフィックス、またはオブジェクト) の S3 URI を入力します。今回は動作確認が目的であるため、デフォルトの場所として `s3://` (リージョン内のすべてのバケット) を入力しました。

許可に先ほど作成した IRM Role を指定して、ロケーションを登録を完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/a915e3d0-7a4d-dafb-7640-006792d1a719.png)

### 権限の作成
最後に権限 (Grants) を作成します。S3 Access Grants インスタンスの詳細画面で権限タブを選択し、「権限を登録」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/b7c877bc-573a-e1e7-3510-1ddd584f5942.png)

権限範囲のロケーションに先ほど登録したデフォルトのローケーション `s3://` を設定します。オプションでサブプレフィックスを使用すると、ロケーション記載の URI からさらに特定のプレフィックスまたはオブジェクトにアクセスを制限することができます。

デフォルトのローケーション `s3://` を指定した場合は、必ずサブプレフィックスを入力する必要があります。リージョン内のすべてのバケットに対する Grants は作成できないためです。ここではサブプレフィックスで特定バケットのすべてのオブジェクトを指定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/3523eea3-cd40-60ba-c5f7-350b701984be.png)

画面下部に進み、許可とアクセスを設定します。ここでは以下のように設定し、権限を作成しました。

**許可**: 読み込みおよび書き込み (RAEDWRITE)
**被付与者タイプ**: IAM アイデンティティーセンターからのディレクトリ ID
**ディレクトリアイデンティティタイプ**: グループ
**IAM アイデンティティセンターグループ ID**: 実際のグループ ID 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/0a75c6f7-77bf-1a77-5abd-ccf2541da3d2.png)

以下のように権限が追加されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/eb619dec-bfdb-859b-2e34-d78bd1bdcb42.png)

### S3 Access Grants を介したアクセス
マネジメントコンソールからは S3 Access Grants を介したアクセスはできないため、AWS CLI を使用して動作を確認します。

使用した AWS CLI のバージョン

```shell-session
$ aws --version
aws-cli/2.14.6 Python/3.11.6 Linux/6.1.61-85.141.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023 prompt/off
```
`aws s3control get-data-access` を実行して、アクセスをリクエストします。ここではあえて参照権限のみをリクエストしています。事前に定義した権限 (Grants) の条件を満たしているため、一時的な認証情報が払い出されます。

```shell-session
$ aws s3control get-data-access --account-id 123456789012 --target "s3://bucket_name/*" --permission READ
{
    "Credentials": {
        "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
        "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY",
        "SessionToken": "AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==",
        "Expiration": "2023-12-10T20:14:33+00:00"
    },
    "MatchedGrantTarget": "s3://bucket-name/*"
}
```

一時クレデンシャルを使用して、S3 バケット上のオブジェクトを参照できることを確認します。

```shell-session
$ export AWS_ACCESS_KEY_ID="ASIA56VSOXDWK4BMJUH2"
$ export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY"
$ export AWS_SESSION_TOKEN="AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA=="
$ aws s3 ls s3://bucket-name/test.txt
2023-12-10 19:23:15          0 test.txt
```

参照権限のみをリクエストしていたため、ファイルをアップロードしようとすると権限エラーになります。

```shell-session
$ touch test2.txt
$ aws s3 cp test2.txt s3://bucket-name/
upload failed: ./test2.txt to s3://bucket-name/test2.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
```

## 参考
https://aws.amazon.com/jp/s3/features/access-grants/

https://aws.amazon.com/jp/blogs/storage/scaling-data-access-with-amazon-s3-access-grants/

https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-grants.html

https://www.youtube.com/watch?v=Ts-ZMBzGeh0

以上です。
参考になれば幸いです。