【AWS IAM】Condition の条件キーやポリシー変数は可用性を意識しよう!という話
突然ですが問題です
あなたはAWS環境の管理者です。 IAMロールに付与したタグベース で、 EC2インスタンス開始/停止を制御しようと試みています。
現状 利用者ロールに割り当てている権限は PowerUserAccess
相当です。
以下のポリシーを追加で付与して制御を実現しました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": ["ec2:StartInstances", "ec2:StopInstances"], "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotEquals": { "aws:PrincipalTag/Project": "${aws:ResourceTag/Project}" }}}]}
これの適用後、実環境で 2つのミス をしてしまいました。
1つは「あるロールへのタグの付け忘れ (ケース#1)
」、
もう1つは「あるインスタンスへのタグの付け忘れ (ケース#2)
」です。
ケース#1, ケース#2
の状態で Start/StopInstances されたときに、
追加したポリシーが働くのか(=Denyされるか)どうか、
以下選択肢から正しい組み合わせを選んでください(10点) 。
- A. ケース#1は Deny される、ケース#2は Deny される
- B. ケース#1は Deny される、ケース#2は Deny されない
- C. ケース#1は Deny されない、ケース#2は Deny される
- D. ケース#1は Deny されない、ケース#2は Deny されない
(Thinking time…)
・
・
・
・
・
はい、正解は B. でした。正解できたでしょうか?
以降で なぜ B. になったのか説明していきます。
はじめにまとめ
- 【リクエストコンテキスト】の キーの可用性を意識しよう
- 【条件キー】に指定したキーが無いとき、
それは "不一致" (false) として扱われる
- 符号反転系の【条件演算子】の場合、 true になる
- 【ポリシー変数】に指定したキーが無いとき そのステートメントは無効になる
【】でくくった用語を次の "前提知識" で説明します。 下線部分の解説もその後に行います。
前提知識
リクエストコンテキスト
この単語は IAMポリシー周りのドキュメントによく見られます。
「プリンシパル」が AWSに「リクエスト」を送る際に、 リクエストに関する情報 をまとめます。 この リクエストに関する情報 を リクエストコンテキスト と言います。
ここでいう「プリンシパル」は、IAMユーザーやIAMロール等を指します。 そして「リクエスト」は、マネジメントコンソールや AWS API, AWS CLIの使用を指します。
このリクエストコンテキストを使って、リクエストの評価、及び 許可/拒否 の判断(ポリシーを適用するか)を行います。
リクエストコンテキストのキー(条件キー)
リクエストコンテキストの 特定要素を指すためのキー です。
IAMポリシーの Condition
(後述) で主に使用するため、 条件キー(or 条件コンテキストキー) とも言います。
大きく分けて サービスに依存しない グローバル条件キー と 各サービス固有の条件キー が存在します。
- 例えば aws:username はグローバル条件キーです。 リクエスト時の プリンシパルのユーザー名 を指します。
- 例えば ec2:Vpc はサービス固有の条件キーです。 EC2サービスで使用します。操作対象リソースの 所属するVPC ARN を指します。
使用できる条件キーは以下ドキュメントで調べられます。
- AWS グローバル条件コンテキストキー - AWS Identity and Access Management
- AWS のサービスのアクション、リソース、および条件キー - サービス認証リファレンス
ポリシー変数
ポリシー変数を使うことで、リクエストコンテキストのキーを IAMポリシーの値として活用 できます。
これは IAMポリシーの Resource
および Condition
で使用できます。
以下 ポリシー例を見ると使い方がよく分かると思います。 ${}
で括ります。
{ "Version": "2012-10-17", "Statement": [ { "Action": ["s3:ListBucket"], "Effect": "Allow", "Resource": ["arn:aws:s3:::mybucket"], "Condition": {"StringLike": {"s3:prefix": ["${aws:username}/*"]}} }, { "Action": [ "s3:GetObject", "s3:PutObject" ], "Effect": "Allow", "Resource": ["arn:aws:s3:::mybucket/${aws:username}/*"] } ] }
– 引用: IAM ポリシーの要素: 変数とタグ - AWS Identity and Access Management
※Tipsその1
ポリシー変数変数を利用するには IAMポリシーに "Version": "2012-10-17",
の記載が必要です。
※Tipsその2
デフォルト値を指定できます。
例えば ${aws:userid, 'anonymous'}
は aws:userid (プリンシパル識別子)を指しますが、
このキーが含まれない(=匿名リクエスト) 場合は、 anonymous
文字列を返します。
IAMポリシーの Condition
IAMポリシーの Condition要素 でポリシーが適用されるかどうかの判断を行うことができます。
一番シンプルな Conditionは以下のような構文です。
"Condition" : { "(条件演算子)" : { "(条件キー)" : "(条件キーの値)" } }
条件演算子 を使用して「条件キーの値」と「実際のリクエストコンテキストの値」との比較を行います。
代表的な条件演算子として 文字列の完全一致比較に使う StringEquals
や
数値の大小比較に使う NumericLessThan/NumericGreaterThan
があります。
「条件キー」には先程の リクエストコンテキストのキー が入ります。
「条件キーの値」には特定文字列/数値、もしくは先程の ポリシー変数 が入ります。
ここで冒頭の問題に示したポリシーを改めて見てみましょう。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": ["ec2:StartInstances", "ec2:StopInstances"], "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotEquals": { "aws:PrincipalTag/Project": "${aws:ResourceTag/Project}" }}}]}
このポリシーの「条件演算子」、「条件キー」、「条件キーの値」を整理してみます。
- 条件演算子 …
StringNotEquals
(文字列完全一致の符号反転) - 条件キー …
aws:PrincipalTag/Project
(プリンシパルのProjectタグ値) - 条件キーの値 …
${aws:ResourceTag/Project}
(リソースのProjectタグ値)
つまり 「プリンシパルのProjectタグ値」と「リソースのProjectタグ値」 が「文字列一致しない」場合に、ポリシー適用(Deny)される内容となっています。
各キーには可用性がある
以降が本題です。
リクエストコンテキストのキーには可用性があります。 例えば aws:ResourceTag/tag-key というキーは、 対象リソースにそのタグが付与されている場合に存在します。 当然ですが付与されていない場合は存在しません。
この「キーの可用性」、グローバル条件キーの場合はドキュメントに記載があります。
– 画像: AWS グローバル条件コンテキストキー - AWS Identity and Access Management
サービス固有の条件キーの場合は、「アクション × リソース」毎に条件キーが決まります。 ドキュメントで調べることができます。
– 画像: Amazon EC2 のアクション、リソース、および条件キー - サービス認証リファレンス
条件キーに指定したキーが存在しないとき、どうなる?
条件キーに指定したキーが存在しないとき、 不一致(=false) として扱われます。
例えば以下のような Condition の場合、 プリンシパルに Projectタグがアタッチされていない場合、 Condition全体としては falseを返します 。
"Condition": { "StringEquals": { "aws:PrincipalTag/Project": "XXX" } }
ここで注意したいのが 符号反転系の条件演算子 を使うケースです。
StringNotEquals
や NumericNotEquals
など、 Not
が付与される条件演算子です。
これらを使用した場合、条件キーが無い場合は trueを返します 。
ポリシー変数に指定したキーが存在しないとき、どうなる?
次にポリシー変数に指定したキーが存在しないとき。 こちらは ステートメントが無効になります 。
無効になる は以下のようなイメージです。
前の章で説明した 不一致 のケースでは、 条件演算子次第で true になりました(=ステートメントが評価される)。
しかしポリシー変数に指定したキーが存在しないときは、 無効 です (=ステートメントは評価されない)。もちろん条件演算子に依存しません。
改めて問題の答え合わせ
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": ["ec2:StartInstances", "ec2:StopInstances"], "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotEquals": { "aws:PrincipalTag/Project": "${aws:ResourceTag/Project}" }}}]}
改めて冒頭の問題を見てみましょう。
まず説明しやすい ケース#2 から見ます。 リソースタグはポリシー変数として利用しています。 つまりリソースタグの付け忘れは ステートメント無効化 に繋がります。 よって追加のポリシーは評価されず、 Denyされません 。
次に ケース#1 を見てみます。
プリンシパルタグを条件キーとして利用しています。
つまりプリンシパルタグの付け忘れは 不一致(false) として扱われます。
そして使用している条件演算子は StringNotEquals
です。
符号反転の条件演算子なので、
Condition 部分は true になります。
よって追加のポリシーは評価され、 Denyされます 。
…といった理由で正解は 「B. ケース#1は Deny される、ケース#2は Deny されない」 でした。
キーが無いときに備えてどう対処するか?
条件キーの場合
指定したキーが無いときに 「trueとしたいか」、「falseとしたいか」 ポリシー設計段階で決めましょう。
「trueとしたい」 場合は IfExists を活用します。 (Nullを除く) 全ての条件演算子の末尾に付けることができます。 キーが存在しない場合に 条件要素は true として評価されます 。
「falseとしたい」 場合は Null 条件演算子 を使った条件を 明示的に追加 しましょう。
例えば冒頭問題のポリシーが「プリンシパルタグが無い場合は Denyされないようにしたい」設計だったとします。 その場合は以下のような Null条件を追加します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": ["ec2:StartInstances", "ec2:StopInstances"], "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "Null": { "aws:PrincipalTag/Project": false }, "StringNotEquals": { "aws:PrincipalTag/Project": "${aws:ResourceTag/Project}" }}}]}
Null条件を入れることで、 条件演算子に左右されずに 「条件キーが無いときは false」になります。
ポリシー変数の場合
「デフォルト値を設定する」 、 「明示的に Null条件を付与する」 の 2つをポリシー設計段階で検討しましょう。
▼デフォルト値を設定する
前提知識 > ポリシー変数
の Tips に書いたとおり、
ポリシー変数にデフォルト値を設定できます。これを設定することで、
とりあえずステートメントが無効になる事態は回避できます。
▼明示的に Null条件を付与する
Null条件を付与することで 「指定したキーが有る前提のステートメント」 であることを明示的に示せます。 具体的には指定したキーが無いときに false となるように Null条件を追加します。
この Null条件の有無で、 以下のように「指定したキーが無いときの挙動」が異なります。
- [Null条件が無い場合]
- ポリシー変数に指定したキーが存在しないため無効
- 無効のためステートメントは評価されない
- [Null条件が有る場合]
- Null 条件の部分で false が返る
- Condition は 全条件のAND で評価されるためこの時点で Condition 部分が false となる
- Condition 部分が false のためステートメントは評価されない
おわりに
以上、条件キーとポリシー変数の「キーがないときの挙動」の話でした。 タグベースの制御(ABAC)をする際は、今回話したようなポリシー設計の考慮点多々あると思います。
「キーが無いときにどうするか」までを考慮した設計を進めましょう。
参考
- AWS Documents
- DevelopersIO