Amazon S3 Access Grants を試してみた

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

みなさん、こんにちは。
AWS CLI が好きなテクニカルサポート課の市野です。

本日は、昨年の re:Invent 2023 直前あたりに発表された Amazon S3 Access Grants を試してみたブログとなります。

Amszon S3 Access Grants とは?

Active Directory やサードパーティの IdP など、IAM Identity Center と統合できる認証基盤のアイデンティティ経由での S3 バケットへのアクセスの際に、読み取り or 書き込みなどのアクセスの権限付与の一元管理や、CloudTrail による証跡の記録を行えるようにする仕組みです。

公式発表

aws.amazon.com

AWS 公式ブログでの紹介

aws.amazon.com

今までも IAM ポリシーやバケットポリシーで権限の管理はできていましたが、公式文書によると以下のようなユースケースで特にメリットがあると記載がありました。

Getting started with S3 Access Grants

docs.aws.amazon.com

  • You are running into the bucket policy size limit of 20 KB.
  • You grant human identities, for example, Microsoft Entra ID (formerly Azure Active Directory), Okta, or Ping users and groups, access to S3 data for analytics and big data.
  • You must provide cross-account access without making frequent updates to IAM policies.
  • Your data is unstructured and object-level rather than structured, in row and column format.

(以下、機械翻訳による和訳)

  • バケット ポリシーのサイズ制限である 20 KB に達している。
  • Microsoft Entra ID (旧 Azure Active Directory)、Okta、または Ping のユーザーとグループなどの人間の ID に、分析やビッグ データのための S3 データへのアクセスを許可するケース。
  • IAM ポリシーを頻繁に更新せずに、クロスアカウント アクセスを提供する必要があり場合。
  • データは構造化されておらず、行と列の形式であり、構造化されているのではなくオブジェクト レベルであるような場合。

やってみた

手順概要

おおまかな流れは以下となります。

  1. IAM ロールの作成
  2. S3 ロケーションへのロールの付与
  3. IAM Identity Center インスタンスとの紐付け

実施手順

事前準備

S3 バケットとテスト用のフォルダオブジェクトの作成
バケット名の定義
BUCKET_NAME="swx-ichino-s3-grants"
バケットの作成
aws s3 mb s3://${BUCKET_NAME}
テスト用のフォルダオブジェクトの作成
aws s3api put-object --bucket ${BUCKET_NAME} --key folderOK/

Access Grants インスタンスの作成

AWS アカウント ID の取得

後続工程の Access Grants インスタンス作成時の --account-id オプションで使用するためのアカウント ID を取得しておきます。

AWS_ACCOUNT_ID=$(\
  aws sts get-caller-identity \
    --query "Account" \
    --output text)
Access Grants インスタンスの作成

s3control create-access-grants-instance サブコマンドで Access Grants インスタンスを作成します。(ついでに作成完了後に ARN を変数に格納します。)

なお、s3control create-access-grants-instance サブコマンドは、AWS CLI v1 ではバージョン 1.30.7 以上、AWS CLI v2 ではバージョン 2.13.39 以上でないと利用ができないため注意が必要です。

ACCESS_GRANTS_INSTANCE_ARN=$(aws s3control create-access-grants-instance \
  --account-id ${AWS_ACCOUNT_ID} \
  --query 'AccessGrantsInstanceArn'\
  --output text)

S3 Access Grants IAM ロールの作成

信頼ポリシードキュメントの作成

Principalaccess-grants.s3.amazonaws.com 、許可するアクションとして sts:AssumeRolests:SetSourceIdentitysts:SetContext を定義した信頼ポリシードキュメントを作成します。

cat << EOF > ./trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "access-grants.s3.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:SetContext"
            ]
        }
    ]
}
EOF
IAM ロールの作成

前工程で作成した信頼ポリシードキュメントを利用し s3ag-location-role というロール名の IAM ロールを作成します。

aws iam create-role \
  --role-name s3ag-location-role \
  --assume-role-policy-document file://trust-policy.json
ポリシードキュメントの作成

以下の内容となるよう IAM ポリシードキュメントを作成します。

cat << EOF > iam-policy.json
{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Sid": "ObjectLevelReadPermissions",
           "Effect": "Allow",
           "Action": [
               "s3:GetObject",
               "s3:GetObjectVersion",
               "s3:GetObjectAcl",
               "s3:GetObjectVersionAcl",
               "s3:ListMultipartUploadParts"
           ],
           "Resource": [
               "arn:aws:s3:::*"
           ],
           "Condition": {
               "StringEquals": {
                   "aws:ResourceAccount": "${AWS_ACCOUNT_ID}"
               },
               "ArnEquals": {
                   "s3:AccessGrantsInstanceArn": [
                       "${ACCESS_GRANTS_INSTANCE_ARN}"
                   ]
               }
           }
       },
       {
           "Sid": "ObjectLevelWritePermissions",
           "Effect": "Allow",
           "Action": [
               "s3:PutObject",
               "s3:PutObjectAcl",
               "s3:PutObjectVersionAcl",
               "s3:DeleteObject",
               "s3:DeleteObjectVersion",
               "s3:AbortMultipartUpload"
           ],
           "Resource": [
               "arn:aws:s3:::*"
           ],
           "Condition": {
               "StringEquals": {
                   "aws:ResourceAccount": "${AWS_ACCOUNT_ID}"
               },
               "ArnEquals": {
                   "s3:AccessGrantsInstanceArn": [
                       "${ACCESS_GRANTS_INSTANCE_ARN}"
                   ]
               }
           }
       },
       {
           "Sid": "BucketLevelReadPermissions",
           "Effect": "Allow",
           "Action": [
               "s3:ListBucket"
           ],
           "Resource": [
               "arn:aws:s3:::*"
           ],
           "Condition": {
               "StringEquals": {
                   "aws:ResourceAccount": "${AWS_ACCOUNT_ID}"
               },
               "ArnEquals": {
                   "s3:AccessGrantsInstanceArn": [
                       "${ACCESS_GRANTS_INSTANCE_ARN}"
                   ]
               }
           }
       },
       {
           "Sid": "KMSPermissions",
           "Effect": "Allow",
           "Action": [
               "kms:Decrypt",
               "kms:GenerateDataKey"
           ],
           "Resource": [
               "*"
           ]
       }
   ]
}
EOF
ポリシーのアタッチ

前工程で作成したポリシードキュメントを使用し、先ほど作成した IAM ロール s3ag-location-role にカスタマーインラインポリシー s3ag-location-role としてアタッチします。

aws iam put-role-policy \
  --role-name s3ag-location-role \
  --policy-name s3ag-location-role \
  --policy-document file://iam-policy.json

ロケーションの作成

作成した IAM ロールの ARN の取得

事前準備として、前工程までで作成した IAM ロール s3ag-location-role の ARN を取得します。

ROLE_ARN=$(aws iam list-roles \
  --query 'Roles[?RoleName==`s3ag-location-role`].Arn' \
  --output text)
S3 ロケーション作成の実施

s3control create-access-grants-location サブコマンドで、S3 ロケーションを管理する IAM ポリシーとして前工程までで作成した IAM ポリシー s3ag-location-role を指定して S3 ロケーションを作成します。

aws s3control create-access-grants-location \
  --account-id ${AWS_ACCOUNT_ID} \
  --location-scope s3:// \
  --iam-role-arn ${ROLE_ARN}

作成に成功すると以下のような値の返却があります。

{
    "CreatedAt": "2024-01-26T08:18:13.238000+00:00",
    "AccessGrantsLocationId": "default",
    "AccessGrantsLocationArn": "arn:aws:s3:ap-northeast-1:XXXXXXXXXXXX:access-grants/default/location/default",
    "LocationScope": "s3://",
    "IAMRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/s3ag-location-role"
}

アクセス許可の作成

Access Grants インスタンス ID の取得

後続の工程のために作成した Access Grants インスタンス ID を取得します。

執筆時点では default という文字列が利用されるようですが、ここでは前工程までで取得している Access Grants インスタンス の ARN から切り出して取得することとしています。

ACCESS_GRANTS_INSTANCE_ID=$(\
  awk -F '/' '{print $2}' <<< $ACCESS_GRANTS_INSTANCE_ARN)
アクセス権限付与対象となる被付与者の ARN の取得

今回は、IAM Identity Center の権限セットを対象としたいので、パスプレフィックスに /aws-reserved/sso.amazonaws.com を含む IAM ロールから対象としたい権限セットを指定し ARN を取得します。

今回の検証用アカウントでは、IAM Identity Center の 許可セットととして EC2FullAccess という名称の許可セットを用意しています。
そしてその許可セットには、マネージドポリシー AmazonEC2FullAccess の付与とs3:GetDataAccess アクションのみ許可を付与しています。

TARGET_GRANTEE_ARN=$(aws iam list-roles \
  --path-prefix /aws-reserved/sso.amazonaws.com \
  --query "Roles[?contains(to_string(RoleName),\`AWSReservedSSO_EC2FullAccess_\`)].Arn" \
  --output text)
アクセス権限付与の実施

前工程までで取得した Access Grants インスタンス ID、被付与者の ARN とともに、対象とする S3 バケットおよびプリフィックスを指定し、権限を付与します。

権限(--permission オプション)の設定には、READ(読み取りのみ)、WRITE(書き込みのみ)、READWRITE(読み書き)のいずれかが指定可能です。

また、前述の通り今回は IAM リソースを対象とするので、GranteeType には IAM、GranteeIdentifier には 前工程で取得した IAM ロールの ARN を指定します。

aws s3control create-access-grant \
  --account-id ${AWS_ACCOUNT_ID} \
  --access-grants-location-id ${ACCESS_GRANTS_INSTANCE_ID} \
  --access-grants-location-configuration "S3SubPrefix=\"${BUCKET_NAME}/folderOK/*\"" \
  --permission READ \
  --grantee "GranteeType=IAM,GranteeIdentifier=\"${TARGET_GRANTEE_ARN}\""

詳細な API 仕様は、API リファレンス CreateAccessGrant をご参照ください。

上記コマンドが正しく処理されると、以下のようなレスポンスがあります。

{
    "CreatedAt": "2024-01-29T02:46:02.270000+00:00",
    "AccessGrantId": "d25ebd05-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "AccessGrantArn": "arn:aws:s3:ap-northeast-1:XXXXXXXXXXXX:access-grants/default/grant/d25ebd05-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "Grantee": {
        "GranteeType": "IAM",
        "GranteeIdentifier": "arn:aws:iam::XXXXXXXXXXXX:role/aws-reserved/sso.amazonaws.com/ap-northeast-1/AWSReservedSSO_EC2FullAccess_1xxxxxxxxxxxxxxb"
    },
    "AccessGrantsLocationId": "default",
    "AccessGrantsLocationConfiguration": {
        "S3SubPrefix": "swx-ichino-s3-grants/folderOK/*"
    },
    "Permission": "READ",
    "GrantScope": "s3://swx-ichino-s3-grants/folderOK/*"
}

S3 Access Grants インスタンスの IAM Identity Center インスタンスへの関連付け

IAM Identity Center インスタンス ARN の取得
IIC_INSTANCE_ARN=$(\
  aws sso-admin list-instances \
    --query 'Instances[].InstanceArn' \
    --output text)
S3 Access Grants インスタンスの IAM Identity Center インスタンスへの関連付けの実施
aws s3control associate-access-grants-identity-center \
  --account-id ${AWS_ACCOUNT_ID} \
  --identity-center-arn ${IIC_INSTANCE_ARN}

これで一連の操作が完了です。

利用方法について

前述までの作業が完了していれば、S3 Access Grants インスタンスに作成された権限の被付与者となる IAM エンティティである状態で s3control get-data コマンドを実行することで一時認証情報を取得できるようになります。

その認証情報を export や AWS CLI クレデンシャルファイルに設定することで、S3 Access Grants を通じて得られた権限で S3 バケットへアクセスできることとなります。

S3 Access Grants からの一時認証の取得
aws s3control get-data-access \
  --account-id ${AWS_ACCOUNT_ID} \
  --target "s3://${BUCKET_NAME}/folderOK/*" \
  --permission READ

上記コマンドを実行すると、以下のような一時認証情報が返却されます。
この一時認証情報を利用することで、 Access Grants を通じて得られた権限で振る舞うこととなります。

{
    "Credentials": {
        "AccessKeyId": "ASIXXXXXXXXXXXXXXXXX",
        "SecretAccessKey": "Zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "SessionToken": "{TOKEN_STRINGS...}",
        "Expiration": "2024-01-29T04:01:13+00:00"
    },
    "MatchedGrantTarget": "s3://swx-ichino-s3-grants/folderOK/*"
}

アクセスのテスト

現在の認証情報の確認

多少伏せ字にしていますが、EC2FullAccess という許可セットに基づく IAM Identity Center ユーザーとして認識されていることが確認できました。

aws sts get-caller-identity
{
    "UserId": "ARXXXXXXXXXXXXXXXXXXX:ec2-operator@hogehoge.com",
    "Account": "XXXXXXXXXXXX",
    "Arn": "arn:aws:sts::XXXXXXXXXXXX:assumed-role/AWSReservedSSO_EC2FullAccess_1xxxxxxxxxxxxxxb/ec2-operator@hogehoge.com"
}

S3 バケット内の一覧表示(事前確認)

S3 バケットを指定しない場合
aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
S3 バケットを指定した場合
aws s3 ls s3://${BUCKET_NAME}/folderOK/
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

s3control get-data-access による認証情報の取得と適用

前述の通り、s3control get-data-access で得た認証情報を用いてアクセスすることで、S3 Access Grants で許可された権限によるアクセスができることとなります。

ここでは、s3control get-data-access の結果から AccessKeyId、SecretAccessKey、SessionToken を取り出して export する想定で処理を組み立ててみます。

一時認証情報の取得
GET_DATA_ACCESS_TOKENS=$(aws s3control get-data-access \
  --account-id ${AWS_ACCOUNT_ID} \
  --target "s3://${BUCKET_NAME}/folderOK/*" \
  --permission READ \
  --query 'Credentials.[AccessKeyId, SecretAccessKey, SessionToken]' \
  --output text)
取得した一時認証情報の適用
eval $(awk -F ' ' '{ printf("ACCESSKEY_ID=\"%s\" ; SECRET_ACCESSKEY=\"%s\" ; SESSION_TOKEN=\"%s\"",$1,$2,$3) }' <<< ${GET_DATA_ACCESS_TOKENS} )
export AWS_ACCESS_KEY_ID=$ACCESSKEY_ID
export AWS_SECRET_ACCESS_KEY=$SECRET_ACCESSKEY
export AWS_SESSION_TOKEN=$SESSION_TOKEN
現在の認証情報の再確認

ここでも多少伏せ字にしましたが、Arn が IAM Identity Center の許可セットに紐づいていることを示す「AWSReservedSSO_〜」ではなく、IAM ロール s3ag-location-role に AssumeRole していることが見てとれます。

aws sts get-caller-identity
{
    "UserId": "AROXXXXXXXXXXXXXXXXXX:access-grants-ab74552e-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "Account": "XXXXXXXXXXXX",
    "Arn": "arn:aws:sts::XXXXXXXXXXXX:assumed-role/s3ag-location-role/access-grants-ab74552e-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

S3 バケット内の一覧表示(再確認)

S3 バケットを指定しない場合

S3 Access Grant で許可されていないロケーションとなるため、引き続き権限エラーが発生します

aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
S3 バケットを指定した場合

S3 Access Grants に許可されたバケット、プレフィックスである場合、正しくアクセスができ、バケット内の情報が得られていることを確認しました。

aws s3 ls s3://${BUCKET_NAME}/folderOK/
2024-01-29 11:43:59          0
S3 バケットを指定するものの、付与されていない WRITE 相当を実行した場合

Access Denied となり、アップロードに失敗することが確認できました。

touch test.txt
aws s3 cp test.txt s3://${BUCKET_NAME}/folderOK/
upload failed: ./test.txt to s3://swx-ichino-s3-grants/folderOK/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

まとめ

今回、クロスアカウントでのアクセスは試せていませんが、AWS IAM Identity Center と統合して権限管理が行える S3 Access Grants の登場により、企業ディレクトリのユーザーやグループに基づいた管理がしやすくなるかと思われます。

この記事がどなたかの参考になれば幸いです。

ではまた。

市野 和明 (記事一覧)

マネージドサービス部・テクニカルサポート課

お客様から寄せられたご質問や技術検証を通じて得られた気づきを投稿していきます。

情シスだった前職までの経験で、UI がコロコロ変わる AWS においては GUI で手順を残していると画面構成が変わってしまって後々まごつくことが多かった経験から、極力変わりにくい AWS CLI での記事が多めです。