AWS IAM の結果整合性を避けるためセッションポリシーを用いてポリシーの動作確認を行う

IAM ポリシーを変更するとそれが伝播するまで待つ必要があります。IAM ポリシーはそのままにして、セッションでのみ有効なセッションポリシーを変更しながら使えばその影響を回避できます。

IAM リソース変更の即時反映を期待してはだめ

コンバンハ、千葉(幸)です。

AWS IAM は結果整合性が採用されています。 *1

言い換えれば、変更が即時に反映されることは保証されていません。例えば以下の操作を行ったとします。

  1. IAM ユーザー A に唯一アタッチされた IAM ポリシー P に、EC2 操作を許可する権限を追加する
  2. IAM ユーザー A の認証情報を利用して EC2 の操作を行う

このとき無条件に 2 が成功することを期待したくなりますが、1 からの実行間隔が短いと失敗することもあります。最終的には結果整合性により「成功する」に収束しますが、数秒なり数分間なりは 1 の変更前の権限で評価が行われ失敗する期間が発生します。

IAM ポリシーのチューニングのために頻繁にポリシーを変更して動作確認をする、という機会はそこそこあると思います。このときに IAM の結果整合性がネックになります。都度十分な時間を置いてからテストをするのは手間ですし、短めな間隔でテストした場合には「今のはちゃんと変更後のポリシーで評価されたのだろうか」という疑念が付きまといます。

そんなケースの回避策としてセッションポリシーを用いる、という考え方を最近知ったのでご紹介します。

先にまとめ

  • IAM プリンシパルに変更を加えずセッションポリシーで動作確認することで IAM の結果整合性による影響を避けられる
  • とは言え万能ではなく以下のことに注意
    • 単純に AWS マネジメントコンソールにサインイン/スイッチロールした際にセッションポリシーは使えない
    • IAM プリンシパルのアイデンティティベースポリシーでは広めに権限をつけておく必要がある
    • IAM プリンシパルに複数の IAM ポリシーがアタッチされる場合の検証の仕方には考慮が必要
    • アクション実行先のリソースがリソースベースポリシーを持つ場合、そこも踏まえた評価論理の意識が必要

セッションポリシーを用いた動作確認がすべてのケースにマッチするわけではないので、選択肢の一つとして覚えておき、使い所を見極めて活用できると良さそうです。

AWS IAM の結果整合性がネックになるとは

改めて、AWS IAM の結果整合性がネックになるとはどういうことかをおさらいします。

まず前提として、以下を押さえてください。

  • AWS IAM はコントロールプレーンをバージニア北部、データプレーンを各リージョンに持つ
  • コントロールプレーンに行われた変更はデータプレーンに伝播される
  • 各リージョンのサービスエンドポイントに送られたリクエストは当該リージョンの IAM データプレーンにパスされる
  • 各リージョンの IAM データプレーンは認証認可を行う

コントロールプレーンとデータプレーン全体像

わたしが IAM ユーザーBatchiの認証情報を利用して IAM ロールChickenを引き受け、東京リージョンの EC2 を操作する(例えばインスタンスを新規作成する)ケースを考えます。

AWS IAM dataplane

まず STS サービスエンドポイントへの AssumeRole により IAM ロールChickenの一時的な認証情報を取得し、次にその認証情報を含めて EC2 サービスエンドポイントへのリクエストの送信を行います。そのリクエストが許可されるか拒否されるかは、東京リージョンの IAM データプレーンでの評価により決定されます。 *2

ここで、IAM ロールChickenにアタッチしたポリシーのチューニングをしたく、何度も変更を加えるケースを考えます。ここでの変更は「アタッチ済みのポリシーを修正する」「ポリシーをつけ外しする」どちらでも構いません。また、変更を加える主体は特に問いません。ここでは IAM 管理者用のユーザーを用いる想定にします。

IAM リソース(今回は IAM ロールChickenやそのポリシー)への変更リクエストはバージニア北部リージョンに存在するグローバルエンドポイントに送信されます。リクエストが正常に許可されれば、バージニア北部リージョン上の IAM コントロールプレーンで IAM リソースの情報の更新が行われます。 *3

IAM コントロールプレーンで行われた変更は各リージョンの IAM データプレーンに伝播されますが、それは即時で行われるわけではありません。単純にリージョン間でのデータ転送の時間もありますし、データプレーンをホストするサーバーのレプリケーションの時間もあります。IAM ではパフォーマンス向上のためにキャッシュを使用しているため、そのデータアウトまでの時間がかかることもあるそうです。

この伝播が完了する前に IAM ロールChickenの認証情報を使用したリクエストが送信された場合、変更前の権限構成により評価が行われる可能性があります。1回目のリクエストで変更後を引いたとしても、2回目のリクエストで変更前を引く可能性もあります。

伝播は数秒〜数分で完了することが期待されるためその程度の時間を置けば結果整合性の影響は回避できますが、頻繁にテストしたい場合にはそれがネックになることがあるでしょう。

AWS IAM の結果整合性の影響をセッションポリシーで回避するとは

ここまでで IAM の結果整合性がネックになるケースを確認できました。その影響をセッションポリシーを活用して回避してみます。

セッションポリシーは以下のいずれかでセッションプリンシパルを生成する際に指定でき、そのセッションでのみ有効なポリシーです。

  • AssumeRole 系アクションで IAM ロールをベースにした一時的認証情報を生成
  • GetFederationTokenで IAM ユーザーをベースにした一時的認証情報を生成

今回のケースでは IAM ロールChickenAssumeRoleする際にセッションポリシーを指定します。

AWS IAM dataplane sessionpolicy

IAM ロールChickenにある程度広めの権限を与えておき、ポリシーの検証中には変更を加えないようにします。検証したいポリシーの内容を IAM ロールChickenの AssumeRole 時のセッションポリシーとして指定し、AssumeRole 後に EC2 サービスへリクエストを送ります。EC2 サービスエンドポイントを経由し IAM データプレーンにパスされたリクエストは、IAM データプレーンに保管された IAM ロールChickenのポリシー構成とリクエストに含まれるセッションポリシーを合算して評価されます。

検証したいポリシーを変更したい場合は都度セッションポリシーを指定し直して AssumeRole します。IAM コントロールプレーンの内容には変更が加わらないため、IAM データプレーンへの伝播を気にする必要がありません。

セッションポリシーの考慮点

セッションポリシーを利用する際に意識すべき観点がいくつかあります。

  • セッションポリシーは当該セッションでのみ有効
  • セッションポリシーはインラインポリシーとして直接JSONで指定するか既存の管理ポリシーの ARN のリスト(最大10個)で指定できる
    • 「インラインポリシーのJSON」と「管理ポリシーARNのリスト」の文字数の合計が 2,048 を超えてはいけない
  • セッションポリシーとアイデンティティベースポリシーの双方で許可されたもののみが許可される

最後の観点については以下のイメージです。ここでのアイデンティティベースポリシーとは、セッションの生成のベースになった IAM プリンシパル(ユーザーかロール。今回の例では IAM ロールChicken)にアタッチされた IAM ポリシーのことを指します。

sessionpolicy

他のポリシータイプとしてリソースリソースベースポリシーやPermissions boundaryもあるのですが、そこを考え出すとしんどいことになるので今回は気にしないことにしましょう。 *4

今回のようにポリシーの検証用途でセッションポリシーを用いるのであれば、「アイデンティティベースポリシーでは許可がないがセッションポリシーでは許可がある」という状態にするメリットはないでしょう。以下のように、「アイデンティティベースポリシーの中から範囲を絞っていく」という権限の持たせ方をするとテストしやすいのではないでしょうか。

sessionpolicy-scopedownpolicy

セッションポリシーのことを指してスコープダウンポリシーと呼ぶことがありますが、まさにスコープダウン(範囲を縮小)というイメージがピッタリきます。

AWS IAM の結果整合性を体験してみる

ここまでで IAM の結果整合性の影響について理解が深まりました。理屈上はそういったことが起きるというのは理解できましたが、せっかくなら実際に試してみたいものです。

お手軽さ重視に以下の構成で試してみます。

aws iam kekkaseigousei

  • AdministratorAccess がアタッチされた IAM ユーザーchiba-cliを使用し、以下を実行
    1. 自身に「S3アクションをDeny」するインラインポリシーをアタッチ
    2. S3 バケットchibayuki-hoge-hogeに PutObject を繰り返し実行
  • S3 バケットchibayuki-hoge-hogeにはリソースベースポリシーのアタッチなし

PutObject が成功した場合のイメージは以下です。

% aws s3api put-object --bucket chibayuki-hoge-hoge --key test.txt --body test.txt
{
    "ETag": "\"b1946ac92492d2347c6235b4d2611184\"",
    "ServerSideEncryption": "AES256",
    "VersionId": "4LGsE4l40VHfs0Iqvy96lq6rsSZxLp2l"
}

# Outputの全量はいらないので、`VersionId`だけ出力する

% aws s3api put-object --bucket chibayuki-hoge-hoge --key test.txt --body test.txt\
  --query "VersionId" --output text
H1AJQ2a2UYJwETRdUHJf725XcTHb8YNT

明示的な拒否により PutObject が失敗した場合のイメージは以下です。

% aws s3api put-object --bucket chibayuki-hoge-hoge --key test.txt --body test.txt --query "VersionId" --output text

An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

# エラー文の出力はいらないので標準エラー出力を捨てる

% aws s3api put-object --bucket chibayuki-hoge-hoge --key test.txt --body test.txt --query "VersionId" --output text 2> /dev/null
%

成功した時はバージョンIDが表示され、失敗したときは何も表示されないことになります。

これを試行回数とタイムスタンプと並べて表示してみます。Deny の IAM ポリシーをアタッチした直後は何度か成功が混じり、やがて失敗のみになる挙動を予想します。

# S3拒否ポリシーの内訳を変数にセット
% policy=$(cat <<EOM
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}
EOM
)

# 現在時刻を取得したのちIAMユーザーにS3拒否ポリシーをアタッチ
% gdate +'%Y-%m-%d %H:%M:%S.%3N' ; aws iam put-user-policy --user-name chiba-cli --policy-name "S3Deny" --policy-document "$policy"

2023-06-04 22:54:59.303

# PutObjectを繰り返し実施

% for i in {1..200}; do
 echo "[$i] $(gdate +'%Y-%m-%d %H:%M:%S.%3N') :\
 $(aws s3api put-object \
   --bucket chibayuki-hoge-hoge \
   --key test.txt \
   --body test.txt \
   --query VersionId \
   --output text 2>/dev/null \
  )";
done
[1] 2023-06-04 22:55:01.899 : yEvJhK.zrOcGGwPsYNTo5T28VqDqGl0M
[2] 2023-06-04 22:55:02.727 : xqS7_yvG6DIvO38pKrcXLDgZUZwU6D9U
[3] 2023-06-04 22:55:03.494 : 0L_TzdNMHfxIa6NdmdKV3omZl_w4kk2A
[4] 2023-06-04 22:55:04.194 : PzJpx5iwOL7alfPmjXkwkPWSNTP5F4Ar
[5] 2023-06-04 22:55:04.895 : djZb.jY.gT8CurN7i6bJuOD9LbdwAq2o
[6] 2023-06-04 22:55:05.646 : GOS.w2MdISmqB4J83g0fGVWhXxevkDf4
[7] 2023-06-04 22:55:06.363 : XiLN4ltb8obyHBWeO04p9kpPWbJXAQBT
[8] 2023-06-04 22:55:07.101 : 1tVo604PeJTDPJpWoj941BXxJ2KXCn1M
[9] 2023-06-04 22:55:07.837 :
[10] 2023-06-04 22:55:08.473 : 2AvUOKEYJZNUFmZxAd_XVCXhc62FQjxx
[11] 2023-06-04 22:55:09.227 :
[12] 2023-06-04 22:55:10.057 :
[13] 2023-06-04 22:55:10.754 :
[14] 2023-06-04 22:55:11.422 :
[15] 2023-06-04 22:55:12.048 :
[16] 2023-06-04 22:55:12.702 : PiIml2XLAlm5zSLNYuxVx1PdAzMZsPc0
[17] 2023-06-04 22:55:13.403 : G2x1txwgp5DHF7YioLFt_sy92lPUR1FH
[18] 2023-06-04 22:55:14.068 :
[19] 2023-06-04 22:55:14.733 : Pv5p7yPrDrplCTUDUO_PrAx8tq3m_Ydi
[20] 2023-06-04 22:55:15.419 : T9Vtjeah82phYDfni5QrMBYhaedaTevY
[21] 2023-06-04 22:55:16.119 : Hilu1Ukv3hMSwbCj0Yjmg5hy8ZxS2hhG
[22] 2023-06-04 22:55:16.864 :
[23] 2023-06-04 22:55:17.529 :
[24] 2023-06-04 22:55:18.300 :
[25] 2023-06-04 22:55:18.963 :
[26] 2023-06-04 22:55:19.644 :
[27] 2023-06-04 22:55:20.295 :
[28] 2023-06-04 22:55:20.977 :
[29] 2023-06-04 22:55:21.641 :
[30] 2023-06-04 22:55:22.276 :
……以下略。以降は全て失敗していました。

↑繰り返しになりますが、バージョンIDが返ってくるのが成功(変更前のポリシー構成に基づいた評価)、何も返ってこないのが失敗(変更後のポリシー構成に基づいた評価)です。

変更後から20秒程度弱([21]の試行まで)は変更前のポリシーをもとに評価されている結果が得られました。また、そこまでもチラホラ変更後のポリシーをもとに評価されていることも確認できます。

AWS IAM の結果整合性を実感できました。ここでは20秒程度で変更が反映されましたが、ケースによっては数分になることもあります。

AWS IAM の結果整合性をセッションポリシーで回避する

最後に、実際にセッションポリシーを利用して IAM の結果整合性の影響を回避した動作確認をする手段を確認します。

今回は AWS CLI を用います。単純に「IAMユーザーでマネジメントコンソールにサインイン」「コンソール上でスイッチロール」という流れにおいてはセッションポリシーを利用できません。 *5

AssumeRoleするパターンと、GetFedarationTokenするパターンの両方を試します。

AssumeRole-and-GetFedarationToken

  • セッションのベースとなる IAM プリンシパル(ロール/ユーザー)にはAdministratorAccessがアタッチされている
  • セッション生成時に以下のセッションポリシーを指定する
    • AdministratorAccess(ひとまずベースの IAM プリンシパルと同等の権限を付与)
    • AmazonS3FullAccess(複数の管理ポリシーを指定する例示のためで、動作上特に意味なし)
    • インラインポリシー(今回は S3 アクションを Deny する内容)
  • セッションプリンシパルから S3 へのアクションを実施
    • 対象の S3 バケットにはバケットポリシーなし

実際にセッションポリシーをテストの動作確認用に用いる際には、ここでのインラインポリシーを変更することになるでしょう。 *6 今回はわかりやすさ重視で Deny する内容にしましたが、実際には「アイデンティティベースポリシーより少し許可が少ない」ポリシーを指定することが多くなるかと思います。(逆に言うと、テスト期間中は本来与えたいポリシーより広めの権限をベースの IAM プリンシパルに与える必要がある。)

MFA なしで AssumeRole する場合

aws sts assume-roleコマンドを使用します。

AWS CLI で AssumeRole する場合にはコンフィグファイルを以下のように定義して明示的に AssumeRole しない(裏でやってくれるのに任せる)ことが多いと思いますが、セッションポリシーを使用したい場合はその手法は使えません。(コンフィグでセッションポリシーを指定できる項目がないため。)

~/.aws/config

[profile chiba]
region = ap-northeast-1
output = json
role_arn = arn:aws:iam::000000000000:role/role-name
source_profile = default

実際にこまめにポリシーを変更してテストすることを考えると、ポリシーは別ファイルとしてあらかじめ用意しておくのが都合がよさそうです。 *7

今回はtestpolicy.jsonという名前で以下の内容を定義しました。

testpolicy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}

AssumeRole 実行時のオプションの指定例は以下です。

$ aws sts assume-role \
  --role-arn arn:aws:iam::000000000000:role/test-chiba-role\
  --role-session-name $(date +%s)-session\
  --policy-arns "arn=arn:aws:iam::aws:policy/AdministratorAccess" "arn=arn:aws:iam::aws:policy/AmazonS3FullAccess"\
  --policy file:///PATH/TO/testpolicy.json

実行結果の例は以下のとおり。ここで返ってきたクレデンシャル情報を環境変数にセットするなどすることで、ロールセッションとしてコマンド実行可能になります。

{
    "Credentials": {
        "AccessKeyId": "ASIAQ3BIIH73WL2VMY53",
        "SecretAccessKey": "/Jdx+bt2e5L以下略",
        "SessionToken": "IQoJb3JpZ2luX2VjEPr//////////wEaDmFwL以下略",
        "Expiration": "2023-06-06T11:13:59+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAQ3BIIH73VP332ZACL:1686046438-session",
        "Arn": "arn:aws:sts::000000000000:assumed-role/test-chiba-role/1686046438-session"
    },
    "PackedPolicySize": 18
}

タイムスタンプの出力を加えて実行してみるとこんな感じ。

% gdate +'%Y-%m-%d %H:%M:%S.%3N'
OUTPUT=$(aws sts assume-role \
  --role-arn arn:aws:iam::000000000000:role/test-chiba-role \
  --role-session-name $(date +%s)-session \
  --policy-arns "arn=arn:aws:iam::aws:policy/AdministratorAccess" "arn=arn:aws:iam::aws:policy/AmazonS3FullAccess" \
  --policy file:///PATH/TO/testpolicy.json)

# クレデンシャルを環境変数にセット
export AWS_ACCESS_KEY_ID=$(echo $OUTPUT | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $OUTPUT | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $OUTPUT | jq -r .Credentials.SessionToken)

# AWS CLIの実行主体を確認
aws sts get-caller-identity

for i in {1..10}; do
 echo "[$i] $(gdate +'%Y-%m-%d %H:%M:%S.%3N') :\
 $(aws s3api put-object \
   --bucket chibayuki-hoge-hoge \
   --key test.txt \
   --body test.txt \
   --query VersionId \
   --output text 2>/dev/null \
  )";
done

上記をまとめて実行した結果が以下です。

2023-06-06 19:28:39.832
{
    "UserId": "AROAQ3BIIH73VP332ZACL:1686047319-session",
    "Account": "000000000000",
    "Arn": "arn:aws:sts::000000000000:assumed-role/test-chiba-role/1686047319-session"
}
[1] 2023-06-06 19:28:42.393 :
[2] 2023-06-06 19:28:43.139 :
[3] 2023-06-06 19:28:43.932 :
[4] 2023-06-06 19:28:44.668 :
[5] 2023-06-06 19:28:45.356 :
[6] 2023-06-06 19:28:46.100 :
[7] 2023-06-06 19:28:46.798 :
[8] 2023-06-06 19:28:47.515 :
[9] 2023-06-06 19:28:48.223 :
[10] 2023-06-06 19:28:48.926 :

↑おさらいをすると、最後の処理で結果が返ってこないことは S3 へのアクセスが拒否されていることを表します。元となる IAM ロールではAdministratorAccessがアタッチされていますが、セッションポリシーで指定した S3 の Deny により拒否されるようになっています。

即時に拒否が効いている様子から、セッションポリシーで指定した内容には IAM の結果整合性が影響を与えていないことがわかります。

MFA ありで AssumeRole する場合

上記の例では MFA のことを考えずにAssumeRoleしていましたが、多くの場合 人が使う IAM ロールでは引き受ける際に MFA を必須としているでしょう。

具体的には信頼ポリシーで以下のように条件句を指定し、引き受け時に MFA 認証の有無を確認する手法です。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": "arn:aws:iam::000000000000:root"
			},
			"Action": "sts:AssumeRole",
			"Condition": {
				"Bool": {
					"aws:MultiFactorAuthPresent": "true"
				}
			}
		}
	]
}

引き受けたい IAM ロールで MFA が要求される場合、AssumeRole 実行時のオプションが追加で以下が必要になります。

--serial-number #AssumeRoleを実行するIAMユーザーに設定しているMFAのARN
--token-code #MFAで発行した6桁のコード

特にトークンコードは都度入力が必要なので、そろそろ一連の流れを手作業で行うのが辛くなってきました。関数を作成して対応します。

今回は以下の関数を作成しました。なお、わたしの環境は macOS + zsh です。環境に合わせて細部のカスタマイズが必要かと思います。

function foo_assume {
  ROLE_ARN='arn:aws:iam::000000000000:role/test-chiba-role'
  SERIAL_NUMBER='arn:aws:iam::000000000000:mfa/cm-chiba.yukihiro'
  SOURCE_PROFILE='xxprofile'
  DATE=$(date +%s)
  POLICY_ARNS='file:///PATH/TO/policy_arns.json'
  POLICY='file:///PATH/TO/testpolicy.json'

  read "TOKEN_CODE?Input MFA Code: " #bashの場合は「read -sp "Input MFA Code: " TOKEN_CODE」と書いてください

  OUTPUT=$(aws sts assume-role \
    --role-arn          ${ROLE_ARN} \
    --serial-number     ${SERIAL_NUMBER} \
    --role-session-name ${DATE}-session \
    --profile           ${SOURCE_PROFILE} \
    --duration-second   1800 \
    --token-code        ${TOKEN_CODE} \
    --policy-arns       ${POLICY_ARNS} \
    --policy            ${POLICY}
    )

  export AWS_ACCESS_KEY_ID=$(echo $OUTPUT | jq -r .Credentials.AccessKeyId)
  export AWS_SECRET_ACCESS_KEY=$(echo $OUTPUT | jq -r .Credentials.SecretAccessKey)
  export AWS_SESSION_TOKEN=$(echo $OUTPUT | jq -r .Credentials.SessionToken)
}

実行例は以下です。今回は単に上記の関数をコピペした後に実行しました。(ターミナルを開く度に有効にしたけえればzprofileなどに登録が必要。)

% foo_assume
Input MFA Code: 536444 #6桁のコードを入力
% #正常に完了すれば出力なし

#プリンシパルがロールセッションになっているか確認
% aws sts get-caller-identity
{
    "UserId": "AROAQ3BIIH73VP332ZACL:1686054532-session",
    "Account": "000000000000",
    "Arn": "arn:aws:sts::000000000000:assumed-role/test-chiba-role/1686054532-session"
}

管理ポリシーを複数指定する場合、変数に直接埋め込む書き方がうまくいかなかったので、以下のように別ファイルでJSON形式で定義して読みこませる形にしました。

policy_arns.json

[
    {
      "arn": "arn:aws:iam::aws:policy/AdministratorAccess"
    },
    {
      "arn": "arn:aws:iam::aws:policy/AmazonS3FullAccess"
    }
]

GetFedarationToken する場合

aws sts get-federation-tokenを使用します。

基本的には AssumeRole する場合と変わりません。AssumeRole の場合は引き受ける対象のロールを任意のものを選択できましたが、こちらでは実行する IAM ユーザーがベースに固定されます。

コマンドの例は以下です。

$ aws sts get-federation-token \
  --name $(date +%s)-session\
  --policy-arns "arn=arn:aws:iam::aws:policy/AdministratorAccess" "arn=arn:aws:iam::aws:policy/AmazonS3FullAccess"\
  --policy file:///PATH/TO/testpolicy.json

実行結果はこのような形。こちらもクレデンシャルが出力されるので、これを環境変数などにセットして使用します。

{
    "Credentials": {
        "AccessKeyId": "ASIAQ3BIIH7345D36V3D",
        "SecretAccessKey": "oAN/8以下略",
        "SessionToken": "IQoJb3JpZ2luX2VjEP3//////////wE以下略",
        "Expiration": "2023-06-07T00:44:48+00:00"
    },
    "FederatedUser": {
        "FederatedUserId": "000000000000:1686055487-session",
        "Arn": "arn:aws:sts::000000000000:federated-user/1686055487-session"
    },
    "PackedPolicySize": 18
}

AssumeRole の時と同じように、一連の流れで確認してみます。

$ gdate +'%Y-%m-%d %H:%M:%S.%3N'
OUTPUT=$(aws sts get-federation-token \
--name $(date +%s)-session \
--policy-arns "arn=arn:aws:iam::aws:policy/AdministratorAccess" "arn=arn:aws:iam::aws:policy/AmazonS3FullAccess" \
--policy file:///PATH/TO/testpolicy.json)

export AWS_ACCESS_KEY_ID=$(echo $OUTPUT | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $OUTPUT | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $OUTPUT | jq -r .Credentials.SessionToken)

aws sts get-caller-identity

for i in {1..10}; do
 echo "[$i] $(gdate +'%Y-%m-%d %H:%M:%S.%3N') :\
 $(aws s3api put-object \
   --bucket chibayuki-hoge-hoge \
   --key test.txt \
   --body test.txt \
   --query VersionId \
   --output text 2>/dev/null \
  )";
done

結果はこちら。フェデレーテッドユーザーのベースとなった IAM ユーザーchiba-cliにはAdministratorAccessがアタッチされていますが、セッションポリシーとして指定したインラインポリシーによって S3 アクセスが拒否されています。

2023-06-06 21:50:54.203
{
    "UserId": "000000000000:1686055854-session",
    "Account": "000000000000",
    "Arn": "arn:aws:sts::000000000000:federated-user/1686055854-session"
}
[1] 2023-06-06 21:50:55.673 :
[2] 2023-06-06 21:50:56.402 :
[3] 2023-06-06 21:50:57.083 :
[4] 2023-06-06 21:50:57.746 :
[5] 2023-06-06 21:50:58.417 :
[6] 2023-06-06 21:50:59.077 :
[7] 2023-06-06 21:50:59.750 :
[8] 2023-06-06 21:51:00.460 :
[9] 2023-06-06 21:51:01.125 :
[10] 2023-06-06 21:51:01.786 :

セッションポリシーの使い方がイメージできました。

終わりに

AWS IAM の結果整合性を避けるため、こまめなポリシーの変更を伴う動作検証にセッションポリシーを利用する、という話でした。

この使い方は以下のブログを見ることで知りました。改めて考えると面白いなーと思います。

どんなケースでも使えるかというとそういう訳ではありませんが、以下のあたりを考慮して活用してください。

  • 単純に AWS マネジメントコンソールにサインイン/スイッチロールした際にセッションポリシーは使えない
  • IAM プリンシパルのアイデンティティベースポリシーでは広めに権限をつけておく必要がある
  • IAM プリンシパルに複数の IAM ポリシーがアタッチされる場合の検証の仕方には考慮が必要
  • アクション実行先のリソースがリソースベースポリシーを持つ場合、そこも踏まえた評価論理の意識が必要

「今回の権限構成での動作確認をセッションポリシーで賄えるのか?」と考える手間はかかりますが、個人的にロマン溢れるやり方だと思います!!!

ロマンが伝われば幸いです。

以上、 チバユキ (@batchicchi) がお送りしました。

参考

脚注

  1. 一般的な IAM の問題のトラブルシューティング - AWS Identity and Access Management
  2. なお、STS はリージョナルなエンドポイントの他にグローバルエンドポイントも用意されています。今回はリージョナルエンドポイントに AssumeRole リクエストを送る前提で考えます。IAM ユーザー Bacthi による IAM ロール Chicken を AssumeRole するリクエストの可否も、IAM データプレーンで評価されています。
  3. 冗長になるので表現を省きましたが、もちろんそのリクエストの評価はバージニア北部リージョンの IAM データプレーンで行われています。
  4. 例えばセッションポリシーに関係なくリソースベースポリシー側の許可だけで最終結果が許可になるケースとか、ロールセッションとフェデレーテッドユーザーセッションで評価論理が異なる場合があるとか……。少なくともリソースベースポリシーが絡む場合は、IAM 側かリソース側どちらの許可(クロスアカウントであれば双方)によって最終評価が許可になるのか?を意識しておくと良いです。
  5. 正確に言うと、カスタマーが望んだ任意のセッションポリシーを適用できないので今回やりたいことは満たせない、です。状況に応じてコンソールで操作している時に何らかのセッションポリシーが適用されることはあるようです。
  6. セッションポリシーとして渡す管理ポリシーには AWS 管理ポリシーだけでなくカスタマー管理ポリシーも対応していますが、カスタマー管理ポリシーの内訳を変更するアプローチをとると IAM の結果整合性の影響を受けるため意味がなくなります。指定する管理ポリシーをセッションごとに切り替える、という変更の仕方であれば影響は受けませんのでそういったパターンはありです。
  7. 「AWS IAM の結果整合性を体験してみる」部のコマンド例のように、ポリシーの内容を変数に格納する手法もあります。