同一の IAM ロール内で AssumeRole してきた IAM ユーザーごとに異なる権限を付与する方法について

2023.05.02

こんにちは、アノテーション テクニカルサポートの mochizuki です。

突然ですが、皆さんが IAM ロールの運用を考える中で、以下のケースを考えたことはないでしょうか。

複数の IAM ユーザーを同一の IAM ロールへ AssumeRole させたいけど、IAM ユーザーごとに許可する権限は変更したい

イメージとしては以下の通りです。

結論としては実現可能なのですが、注意点も存在することを踏まえつつ、IAM ロールへアタッチするポリシーの設定内容などを紹介したいと思います。

前提

基本的には IAM のベストプラクティス[1] を参考にして、使用用途や役割ごとに IAM ロールを分けることが好ましいです。
同一の IAM ロール内で使用用途や役割ごとの権限付与を制御する場合、意図しないアクセス許可・拒否へとつながる確率が IAM ロールを分けている場合より高くなります。
そのため、基本的には使用用途や役割ごとに IAM ロールを分けた方が無難です。

本記事で紹介する内容は、あくまでも前述のケースを実現しようとした場合こうなるんだ、といった具合で参考程度に読んでもらえればと思います。

使用するキー

本ケースを実現するために、以下 2 つのキーを使用します。

  • sts:RoleSessionName[2]
  • aws:userid[3]

sts:RoleSessionNameは IAM ロールの信頼ポリシーの Condition 句、aws:useridは IAM ロールの IAM ポリシーの Condition 句で使用することにより、ロールセッション名を制御する役割を果たします。
これらのキーについては弊社ブログ[4][5] でもそれぞれ解説されているため、ドキュメントと併せて一読すると概要がつかめると思います。

信頼ポリシー例

まずは信頼ポリシーです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:RoleSessionName": "${aws:username}"
                }
            }
        }
    ]
}

上記の信頼ポリシーは、AWS アカウント 111111111111 からの IAM ユーザー名と同じロールセッション名を使用した AssumeRole を許可する、といった内容です。

sts:RoleSessionNameの値に${aws:username}を指定することで、ロールセッション名を AssumeRole する IAM ユーザー名に限定します。
これにより、aws sts assume-roleコマンド[6] 実行時、コマンドを実行した IAM ユーザー名以外のロールセッション名で AssumeRole させることを防ぐことができます。

なお、AWS マネジメントコンソールでのスイッチロール時については、自動的にロールセッション名が IAM ユーザー名になるため特に影響はありません。

IAM ポリシー例

次に IAM ポリシーです。

ホワイトリストパターン

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:Describe*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:userid": [
                        "AROAxxxxxxxxxxxxxxxxx:test-user-A"
                    ]
                }
            }
        }
    ]
}

上記の IAM ポリシーは、IAM ユーザーtest-user-Aが AssumeRole している場合は EC2 の Describe 権限を付与する、といった内容です。

aws:useridの値では、AssumeRole 先の IAM ロールの RoleId と IAM ユーザー名を指定しています。
IAM ロールの RoleId についてはaws iam get-roleコマンド[7] を実行することで確認できます。

ブラックリストパターン

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "NotAction": [
                "ec2:Describe*",
                "elasticloadbalancing:Describe*",
                "cloudwatch:ListMetrics",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:Describe*",
                "autoscaling:Describe*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:userid": [
                        "AROAxxxxxxxxxxxxxxxxx:test-user-A"
                    ]
                }
            }
        }
    ]
}

上記の IAM ポリシーは、IAM ユーザーtest-user-A以外の IAM ユーザーが AssumeRole してきた場合は、AWS 管理ポリシーAmazonEC2ReadOnlyAccess[8] 相当の権限以外を拒否する、といった内容です。

活用法としては、例えば IAM ロールへ AWS 管理ポリシーAmazonEC2FullAccess[9] と上記 IAM ポリシーを付与した場合、IAM ポリシーの組み合わせで付与される権限は以下の通りになります。

  • IAM ユーザーtest-user-A: AWS 管理ポリシーAmazonEC2FullAccessの権限
  • IAM ユーザーtest-user-A以外: AWS 管理ポリシーAmazonEC2ReadOnlyAccess相当の権限

確認してみた

使用する IAM リソース

使用する IAM リソースは、以下の通りです。

IAM ユーザー: test-user-A, test-user-B
(IAM ポリシー: IAM ロールtest-roleへ AssumeRole する権限を付与)

IAM ロール: test-role
(信頼ポリシー: 前述の通り、IAM ポリシー: AmazonEC2FullAccessと前述した Deny の IAM ポリシー)

期待される動作

期待される動作としては、以下の通り IAM ユーザーごとに異なる権限が付与されることです。

  • IAM ユーザーtest-user-A: AWS 管理ポリシーAmazonEC2FullAccessの権限
  • IAM ユーザーtest-user-B: AWS 管理ポリシーAmazonEC2ReadOnlyAccess相当の権限

信頼ポリシーの動作確認

まずは信頼ポリシーにより、ロールセッション名を AssumeRole する IAM ユーザー名に限定できているかを確認します。
以下は、IAM ユーザーtest-user-Aaws sts assume-roleコマンドを実行した場合の結果です。

  • IAM ユーザー名のロールセッション名test-user-A使用時(成功)
$ aws sts assume-role --role-arn arn:aws:iam::111111111111:role/test-role --role-session-name test-user-A --duration-second 900
{
    "Credentials": {
        "AccessKeyId": "EXAMPLEEEEEEEEEEEEE",
        "SecretAccessKey": "EXAMPLEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
        "SessionToken": "EXAMPLEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
        "Expiration": "2023-03-29T07:10:24+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAxxxxxxxxxxxxxxxxx:test-user-A",
        "Arn": "arn:aws:sts::111111111111:assumed-role/test-role/test-user-A"
    }
}
  • IAM ユーザー名以外のロールセッション名test-user-B使用時(失敗)
$ aws sts assume-role --role-arn arn:aws:iam::111111111111:role/test-role --role-session-name test-user-B --duration-second 900
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::111111111111:user/test-user-B is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::111111111111:role/test-role

想定通り、ロールセッション名を AssumeRole する IAM ユーザー名に限定できていることを確認できました!

IAM ポリシーの動作確認

続いて IAM ポリシーにより、AssumeRole している IAM ユーザーごとに許可する権限を変更できているかを確認します。
以下は、IAM ユーザーtest-user-Atest-user-Bそれぞれが IAM ロールtest-roleへ AssumeRole した状態で、aws ec2 describe-instancesコマンドおよびaws ec2 start-instancesコマンドを実行した場合の結果です。

  • IAM ユーザー名test-user-Aおよびtest-user-Baws ec2 describe-instancesコマンドを実行(成功)
    ※以下コマンドでは、EC2 インスタンス ID と Name タグを取得しています。
$ aws ec2 describe-instances --query 'Reservations[].Instances[].{instanceid:InstanceId,Tags:Tags[?Key==`Name`].Value|[0]}'
[
    {
        "instanceid": "i-xxxxxxxxxxxxxxxxx",
        "Tags": "hoge1"
    },
    {
        "instanceid": "i-yyyyyyyyyyyyyyyyy",
        "Tags": "hoge2"
    }
]
  • IAM ユーザーtest-user-Aaws ec2 start-instancesコマンドを実行(成功)
$ aws ec2 start-instances --instance-ids i-xxxxxxxxxxxxxxxxx
{
    "StartingInstances": [
        {
            "CurrentState": {
                "Code": 0,
                "Name": "pending"
            },
            "InstanceId": "i-xxxxxxxxxxxxxxxxx",
            "PreviousState": {
                "Code": 80,
                "Name": "stopped"
            }
        }
    ]
}
  • IAM ユーザーtest-user-Baws ec2 start-instancesコマンドを実行(失敗)
$ aws ec2 start-instances --instance-ids i-xxxxxxxxxxxxxxxxx

An error occurred (UnauthorizedOperation) when calling the StartInstances operation: You are not authorized to perform this operation. Encoded authorization failure message:

こちらも想定通り、AssumeRole している IAM ユーザーごとに許可する権限を変更できていることを確認できました!

注意点その 1

これまで紹介したように、IAM ロールの信頼ポリシーに記載するのが単一の AWS アカウントであれば問題ないのですが、複数の AWS アカウントを記載する場合には、ある問題が発生します。
その問題とは、各 AWS アカウントで同名の IAM ユーザーを作成した場合、その IAM ユーザーを指定した IAM ポリシーの権限を各 AWS アカウントで使用できてしまうことです。

イメージが湧きづらいかもしれないので、例を出します。
例えば、「確認してみた」で使用した信頼ポリシーを、AWS アカウント111111111111と AWS アカウント222222222222を対象とした以下の内容へ修正します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:RoleSessionName": "${aws:username}"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::222222222222:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:RoleSessionName": "${aws:username}"
                }
            }
        }
    ]
}

次に、AWS アカウント222222222222にも IAM ユーザーtest-user-Aを作成します。

そして、各 AWS アカウントに存在する IAM ユーザーtest-user-Aが IAM ロールtest-roleへ AssumeRole した状態で、aws ec2 start-instancesコマンドを実行します。
すると...

  • AWS アカウント111111111111および AWS アカウント222222222222に存在する IAM ユーザーtest-user-Aaws ec2 start-instancesコマンドを実行(成功)
$ aws ec2 start-instances --instance-ids i-xxxxxxxxxxxxxxxxx
{
    "StartingInstances": [
        {
            "CurrentState": {
                "Code": 0,
                "Name": "pending"
            },
            "InstanceId": "i-xxxxxxxxxxxxxxxxx",
            "PreviousState": {
                "Code": 80,
                "Name": "stopped"
            }
        }
    ]
}

どちらの AWS アカウントに存在する IAM ユーザーtest-user-Aからでも、aws ec2 start-instancesコマンドが成功しました。

これは、IAM ユーザー名が同名であればロールセッション名も同名になることで発生します。
単一の AWS アカウント内では同名の IAM ユーザーを作成できませんが、複数の AWS アカウントを使用するのであれば、各 AWS アカウントに同名の IAM ユーザーを作成できます。

これの何が問題かを上記の例で考えると、AWS アカウント 111111111111 に存在する IAM ユーザーにのみ権限を付与することを想定していたのに、AWS アカウント 222222222222 からでも同名の IAM ユーザーを作成することで、その権限を利用することができてしまう点です。

複数の AWS アカウント間で本ケースを実現したい場合の暫定対応としては、以下の信頼ポリシーのように Principal 句にて、各 AWS アカウントで異なる IAM ユーザー名を指定することです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:test-user-A"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:RoleSessionName": "${aws:username}"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::222222222222:test-user-C"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:RoleSessionName": "${aws:username}"
                }
            }
        }
    ]
}

これにより、意図しない AWS アカウントから同名の IAM ユーザーを使用したアクセスを防ぐことができます。

注意点その 2

これまで紹介した信頼ポリシーは Condition 句にてsts:RoleSessionNameの値に${aws:username}を指定することで、ロールセッション名を AssumeRole する IAM ユーザー名に限定していました。
もし Condition 句がない以下のような信頼ポリシーを設定した場合、aws sts assume-roleコマンド実行時に好きなロールセッション名を付けることが可能な状態となります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:root"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::222222222222:root"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

上記の信頼ポリシーでは、「注意点その 1」で言及した問題が単一 AWS アカウント内でも発生してしまう可能性があるためご注意ください。

参考資料

[1] IAM でのセキュリティのベストプラクティス - AWS Identity and Access Management
[2] IAM および AWS STS の条件コンテキストキー - AWS Identity and Access Management
[3] IAM ポリシーの要素: 変数とタグ - AWS Identity and Access Management
[4] [アップデート] IAMロールセッション名にユーザー名を強制できる条件 sts:RoleSessionName が使えるようになりました | DevelopersIO
[5] 特定の IAM ロールのみアクセスできる S3 バケットを実装する際に検討したあれこれ | DevelopersIO
[6] assume-role — AWS CLI 2.11.6 Command Reference
[7] get-role — AWS CLI 2.11.6 Command Reference
[8] AWS 管理ポリシー: AmazonEC2ReadOnlyAccess
[9] AWS 管理ポリシー: AmazonEC2FullAccess

さいごに

いかがでしたでしょうか。
ふと気になってまとめてみた内容とはなりますが、この記事がどなたかの一助となれば幸いです!