はじめに
好物はインフラとフロントエンドのかじわらゆたかです。
特定のIAM Roleからのみアクセスを受け付けるS3バケットを書こうとする場合、 以下のブログを参考にIAM Roleに紐づいているaws:userIdを用いる方法を行っていましたが、aws:PrincipalArnを用いることで同様のことができました。
aws:PrincipalArn ってなに?
AWS グローバル条件コンテキストキーの一つです。 ドキュメントに以下のが記載されております。
このキーを使用して、リクエストを行ったプリンシパルの Amazon リソースネーム (ARN) をポリシーで指定したARN と比較します。IAM ロールの場合、リクエストコンテキストは、ロールを引き受けたユーザーの ARN ではなく、ロールの ARN を返します。
AWS グローバル条件コンテキストキー - AWS Identity and Access Management
確かにロールのARNを指定することが可能に見えるので、確認してみました。
検証用Cloudformation テンプレート
s3-bucket-acces-to-a-specific-role-use-principal-arn.yaml
上記のテンプレートを動かして検証しました。 上記のテンプレートを作成すると以下のリソースが作成されます。
- アクセスが許可されたIAM Role
- アクセスが許可されていないIAM Role
- アクセスが許可されたIAM Roleからアクセス可能な検証用のS3バケット
- SFTPプロトコルで待ち受けるTransferFamilyのサーバー
- アクセスが許可されたIAM Roleと紐づいているSFTPサーバーのユーザー
- アクセスが許可されていないIAM Roleと紐づいているSFTPサーバーのユーザー
- アクセスが許可されたIAM Roleと紐づいているS3にアクセスるするためのLambda
- アクセスが許可されていないIAM Roleと紐づいているS3にアクセスるするためのLambda
S3のBucketPolicyは以下のようになっています
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MultiRestrictPolicy",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::{検証用のS3バケット}",
"arn:aws:s3:::{検証用のS3バケット}/*"
],
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "{アクセスが許可されたIAM RoleのARN}",
"aws:CalledVia": "cloudformation.amazonaws.com"
}
}
}
]
}
なお、SFTPユーザー用の公開鍵は作成してパラメータストアに登録しておく必要があります。 公開鍵の作成と登録は以下のようなシェルを作りました。
ssh-keygen -t rsa -b 4096 -C "s3-access-check-key" -f ./s3-access-check-key
aws ssm put-parameter \
--name s3-access-check-key \
--value \"$(cat ./s3-access-check-key.pub)\" \
--type String
検証してみた
IAM Roleでアクセスしてみた
以下のようなaws/configを作成し、Assume元をIAM User/IAM Roleで確認しました。
[profile check-access-allow-from-role]
output = json
role_arn = {アクセスが許可されたIAM Role}
source_profile = {Assume元のIAM Role}
region = ap-northeast-1
[profile check-access-allow-from-user]
output = json
role_arn = {アクセスが許可されたIAM Role}
source_profile = {Assume元のIAM User}
region = ap-northeast-1
[profile check-access-deny-from-role]
output = json
role_arn = {アクセスが許可されてないIAM Role}
source_profile = {Assume元のIAM Role}
region = ap-northeast-1
[profile check-access-deny-from-user]
output = json
role_arn = {アクセスが許可されていないIAM Role}
source_profile = {Assume元のIAM User}
region = ap-northeast-1
想定通りアクセス用のIAM Roleの権限でアクセス制御ができています。
$ aws s3 cp ./Happy20thanniversarytoClassMethod.txt s3://cm-kajiwara-access-check --profile check-access-allow-from-role
upload: ./Happy20thanniversarytoClassMethod.txt to s3://cm-kajiwara-access-check/Happy20thanniversarytoClassMethod.txt
$ aws s3 ls cm-kajiwara-access-check --profile check-access-allow-from-role
2023-07-07 14:20:51 0 Happy20thanniversarytoClassMethod.txt
$ aws s3 ls cm-kajiwara-access-check --profile check-access-allow-from-user
2023-07-07 14:20:51 0 Happy20thanniversarytoClassMethod.txt
$ aws s3 ls cm-kajiwara-access-check --profile check-access-deny-from-user
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
$ aws s3 ls cm-kajiwara-access-check --profile check-access-deny-from-role
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
アクセスが許可されたIAM RoleにAssume Roleをした後にアクセスができていることと、 アクセスが許可されてないIAM RoleにAssume Roleをしたあとにアクセスができていないことが確認できました。
このような形でSession名を変更しても問題ありません
[profile check-access-allow-from-role-chenge-session-name]
role_arn = {アクセスが許可されたIAM Role}
role_session_name = anniversary_to_classmethod
source_profile = {Assume元のIAM Role}
$ aws s3 ls cm-kajiwara-access-check --profile check-access-allow-from-role-chenge-session-name
2023-07-07 14:20:51 0 Happy20thanniversarytoClassMethod.txt
Lambdaでアクセスしてみた
実装したLambdaを実行してみました こちらも割り当てた権限通りの動きをしています。
$ aws lambda invoke --function-name {アクセスが許可されたIAM Roleと紐づいているS3にアクセスるするためのLambda} out --log-type Tail --query 'LogResult' --output text | base64 -d
START RequestId: dd5bfc27-f6d1-49e1-9c0e-8d009bcbfbd4 Version: $LATEST
END RequestId: dd5bfc27-f6d1-49e1-9c0e-8d009bcbfbd4
REPORT RequestId: dd5bfc27-f6d1-49e1-9c0e-8d009bcbfbd4 Duration: 2426.35 ms Billed Duration: 2427 ms Memory Size: 128 MB Max Memory Used: 77 MB Init Duration: 272.55 ms
$ aws lambda invoke --function-name {アクセスが許可されてないIAM Roleと紐づいているS3にアクセスるするためのLambda} out --log-type Tail --query 'LogResult' --output text | base64 -d
START RequestId: 94804667-3dd5-4988-8bff-3e54867e9ad4 Version: $LATEST
[ERROR] ClientError: An error occurred (403) when calling the HeadObject operation: Forbidden
Traceback (most recent call last):
File "/var/task/index.py", line 6, in lambda_handler
s3.download_file("cm-kajiwara-access-check", 'Happy20thanniversarytoClassMethod.txt', '/tmp/Happy20thanniversarytoClassMethod.txt')
(中略)
REPORT RequestId: 94804667-3dd5-4988-8bff-3e54867e9ad4 Duration: 338.34 ms Billed Duration: 339 ms Memory Size: 128 MB Max Memory Used: 79 MB
TransferFamilyでアクセスしてみた
こちらも権限の想定通りの動きをしています
$ sftp -i ./s3-access-check-key {アクセスが許可されたIAM Roleと紐づいているSFTPサーバーのユーザー}@{SFTPプロトコルで待ち受けるTransferFamilyのサーバー}
Connected to {SFTPプロトコルで待ち受けるTransferFamilyのサーバー}.
sftp> cd cm-kajiwara-access-check
sftp> ls
Happy20thanniversarytoClassMethod.txt
$ sftp -i ./s3-access-check-key {アクセスが許可されてないIAM Roleと紐づいているSFTPサーバーのユーザー}@{SFTPプロトコルで待ち受けるTransferFamilyのサーバー}
Connected to {SFTPプロトコルで待ち受けるTransferFamilyのサーバー}.
sftp> cd cm-kajiwara-access-check
sftp> ls
Couldn't read directory: Permission denied
まとめ
特定のIAM RoleからのみアクセスできるS3バケットとそのバケットポリシーをaws:PrincipalArnを用いる形で記載することができました。
今回様々なケースで検証しましたが、どのケースでも想定通りの動きをしてくれました。 この方法だとバケットポリシーを見直したときも、 aws:userIdを用いていたときに比べIAM RoleのARNが確認できることになります。
特定のIAM Roleからのみアクセスしたいと言ったとき、この方法を採用していただければと思います。
補足
BucketPolicyは非常に強い制約となり、間違えて記載するとAdministrator Policyが割当たっているIAMであっても書き換えが行えなくなります。 ですので、直接書き換えるのではなくCloudformationから記載をすることをおすすめいたします。 こちらの詳細については以下のブログが参考になります。