
特定の IAM ロールのみアクセスできる S3 バケットを実装する際に検討したあれこれ
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
今回は S3 バケットへのアクセスを特定 IAM ロールからのみに限定して利用する機会がありましたので、設定方法と検討したあれこれをご紹介します。
やりたいこと
構成図はこんな感じ
前提条件
- IAM ロールと S3 バケットは同一アカウントに存在する
 - IAM ロールには S3 を管理する権限がアタッチされている
 - 今回は AmazonS3FullAccess ポリシーをアタッチしています
 
NotPrincipal でやってみる
「特定 IAM ロール以外は制限する」という考え方でパッと思いつくのは、以下のような NotPrincipal で制限する方法かと思います。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "NotPrincipal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxxx:role/cm-marumo.atsushi"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::cm-marumo-test/*"
        }
    ]
}
cm-marumo.atsushiロール以外は Deny されるcm-marumo.atsushiには AmazonS3FullAccess の権限がある
一見、これで出来そうに思います。実際に DeleteObject を試してみましょう。
$ aws s3 rm s3://cm-marumo-test/test.txt delete failed: s3://cm-marumo-test/test.txt An error occurred (AccessDenied) when calling the DeleteObject operation: Access Denied
出来ません、、、。
なぜ NotPrincipal では出来ないのでしょう?
assumed-role とセッション
IAM ユーザーからのスイッチロールや、AWS サービスにロールをアタッチして利用している場合、内部的には sts:AssumeRole が発行されて一時的なクレデンシャルを取得してセッションを確立しています。
つまり、NotPrincipal で特定 IAM ロールを除外する場合、STS で発行された以下のような assumed-role の ARN も含めて除外してあげなければなりません。
arn:aws:sts::<アカウントID>:assumed-role/<ロール名>/<セッション名>
しかし、ここで厄介になるのがセッション名です。
セッション名の部分が固定されているのであれば話が早いのですが、ランダムに割り当てられて固定化することができないケースも多いです。たとえば、今回のようなスイッチロールではセッションの都度、変わってしまいます。
逆に固定されているケースとしては、以前にご紹介した Lambda にアタッチしているロールです。Lambda の場合、セッション名部分には関数名が入りますので固定化することが出来ます。(詳細は以前のブログを参照ください)
じゃぁワイルドカードで・・・
と思いますよね。私もそう考えました。
しかし、NotPrincipal の指定でセッション部分をワイルドカードにすることは出来ません。
NotPrincipal 要素で引き受けたロールセッションを指定する場合、ワイルドカード (*) を使用して「すべてのセッション」を意味することはできません。プリンシパルは常に特定のセッションに名前を付ける必要があります。
(AWS JSON ポリシーの要素: NotPrincipal)
どうやら、セッション名が固定出来ない場合に NotPrincipal で実現するのは難しそうです。
ということで次に、実装した代替案を紹介します。
(結論)aws:userId を使った代替案
前置きが長くなりましたが、NotPrincipal では実現できそうにないということが判ったので、代替案として利用したのが aws:userId の Condition で絞り込む方法です。
aws:userId
「ロールでやりたかったんじゃないの?」
という声が聞こえてきそうですが、assume-role しているセッションの aws:userId にはロール ID が含まれています。
以下の sts get-caller-identity コマンドで確認すると確認することが出来ます。
$ aws sts get-caller-identity Account: 'xxxxxxxxxxxx' Arn: arn:aws:sts::xxxxxxxxxxxx:assumed-role/cm-marumo.atsushi/1583xxxxxxxxxxxxxxx UserId: AROAxxxxxxxxxxxxxxVAI:1583xxxxxxxxxxxxxxx
UserId の AROA で始まり : の部分まではロール ID となっており、: より後ろがセッション名です。
iam get-role コマンドでロール ID を確認すると合致しているのが判りますね。
$ aws iam get-role --role-name cm-marumo.atsushi
Role:
Arn: arn:aws:iam::xxxxxxxxxxxx:role/cm-marumo.atsushi
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Condition: {}
Effect: Allow
Principal:
AWS: arn:aws:iam::xxxxxxxxxxxx:user/cm-marumo.atsushi
Version: '2012-10-17'
CreateDate: '2018-08-09T08:37:00+00:00'
MaxSessionDuration: 3600
Path: /
RoleId: AROAxxxxxxxxxxxxxxVAI
RoleLastUsed:
LastUsedDate: '2020-03-10T08:49:00+00:00'
Region: us-east-1
RoleName: cm-marumo.atsushi
バケットポリシーをこう書く
Condition の aws:userId ではセション名にワイルドカードを指定することが出来ますので、バケットポリシーを以下のように書くことができます。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::cm-marumo-test/*",
            "Condition": {
                "StringNotLike": {
                    "aws:userId": [
                        "AROAxxxxxxxxxxxxxxVAI:*"
                    ]
                }
            }
        }
    ]
}
注意いただきたいのは ロール ID の後ろの :* です。この指定がないと、条件にマッチしません。
動作確認
この状態で先程と同じように DeleteObject してみましょう。
$ aws s3 rm s3://cm-marumo-text/test.txt delete: s3://cm-marumo-test/test.txt
ということで紆余曲折ありましたが、Condition と aws:userID を使って特定の IAM ロールのみにアクセスを限定させることが出来ました!
さいごに
ちょっと凝ったバケットポリシーを書こうとしたとき、いつも思うのですが
「バケットポリシーは奥が深い」
毎度、毎度、「あぁ・・、俺は雰囲気でバケットポリシー使ってたのか・・・」と反省します。
特定の IAM ロールのみにバケットアクセスを許可させいたケースはよくある要望かと思いますので、同じような要件がありましたら、是非、参考にしてください。
以上!大阪オフィスの丸毛(@marumo1981)でした!







