みなさん、こんにちは。
AWS CLI が好きなテクニカルサポート課の市野です。
本日は、昨年の re:Invent 2023 直前あたりに発表された Amazon S3 Access Grants を試してみたブログとなります。
Amszon S3 Access Grants とは?
Active Directory やサードパーティの IdP など、IAM Identity Center と統合できる認証基盤のアイデンティティ経由での S3 バケットへのアクセスの際に、読み取り or 書き込みなどのアクセスの権限付与の一元管理や、CloudTrail による証跡の記録を行えるようにする仕組みです。
公式発表
AWS 公式ブログでの紹介
今までも IAM ポリシーやバケットポリシーで権限の管理はできていましたが、公式文書によると以下のようなユースケースで特にメリットがあると記載がありました。
Getting started with S3 Access Grants
- 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 ポリシーを頻繁に更新せずに、クロスアカウント アクセスを提供する必要があり場合。
- データは構造化されておらず、行と列の形式であり、構造化されているのではなくオブジェクト レベルであるような場合。
やってみた
手順概要
おおまかな流れは以下となります。
- IAM ロールの作成
- S3 ロケーションへのロールの付与
- 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 ロールの作成
信頼ポリシードキュメントの作成
Principal
を access-grants.s3.amazonaws.com
、許可するアクションとして sts:AssumeRole
、sts:SetSourceIdentity
、sts: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 での記事が多めです。
X(Twitter):@kazzpapa3(AWS Community Builder)