ちょっと話題の記事

[アップデート]新IAM Policy Condition aws:CalledVia を学ぶ

2020.02.24

IAM PolicyのConditionにaws:CalledViaというキーが追加されました。どういったキーなのかご説明します。

一言でいうと 「特定のサービス経由で実行している/いない」という権限付与の条件が設定可能になりました。

具体例を出して説明します。

これまで: aws:CalledViaが無い世界

CloudFormation(以下CFn)を使って、VPCを作成したいとします。 CFnテンプレートは至極シンプルです。

AWSTemplateFormatVersion: "2010-09-09"
Description: Create VPC
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "192.168.0.0/16"
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: "CalledViaTest-VPC"

まずは、CFnの実行権限しか無いユーザーでこのテンプレートを使ってCFnスタックを作成してみましょう。

実行結果です。 はい。当然失敗しますね。CFnの実行権限はあっても、VPCを作成する権限が無いからです。

というわけで、VPC作成権限も追加します。ec2:CreateVpcだけで済むかと思ったのですが、タグを付与していたりなどで以下4つの権限が必要でした。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "hoge",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcs",
                "ec2:CreateVpc",
                "ec2:ModifyVpcAttribute",
                "ec2:createTags"
            ],
            "Resource": "*"
        }
    ]
}

この状態ですと、 はい。VPC作成成功します。

ですがここで一つ問題が発生します。 このユーザー、CFnを使わずコンソールで直接VPCを作成することもできちゃいます。

IaCを徹底したい場合など、このユーザーにはCFnでリソースを作成するのは許可しても、CFnを介さずリソースを作成することは許可したくない、といった場合、あると思います。それができないんですね。

※ 厳密には、この要件であれば(aws:CalledViaを使わなくても)iam:PassRoleを使えば実現可能です。そのやり方についてはエントリ最下部補足欄に記載しましたのであとでご確認下さい。

これから: aws:CalledViaがある世界

さて、ここでaws:CalledViaConditionキーです。先程のポリシーに追加します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "hoge",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcs",
                "ec2:CreateVpc",
                "ec2:ModifyVpcAttribute",
                "ec2:createTags"
            ],
                "Resource": "*",
                "Condition": {
                    "ForAnyValue:StringEquals": {
                        "aws:CalledVia": [
                            "cloudformation.amazonaws.com"
                        ]
                    }
                }
        }
    ]
}

この権限でコンソールからVPCを作成しようとすると… はい。権限不足で失敗します。

CFn経由だと変わらず成功します!

Condition句をもうすこし詳しく解説

"Condition": {
    "ForAnyValue:StringEquals": {
        "aws:CalledVia": [
            "cloudformation.amazonaws.com"
        ]
    }
}
  • ForAnyValueは、複数のメンバーからなる要素において、そのうち最低一つのメンバーがForAnyValue:の後ろの判定条件で真になった場合に真という条件です。上記だとForAnyValue:StringEquals なので、複数のメンバーからなる要素のうち最低一つのメンバーの値が一致している場合に真になります。
  • aws:CalledVia はリスト形式の値を取ります。なぜなら経由するサービスは一つとは限らないからです。上記例だと、経由するサービス群のいずれかにCFnがあれば真、という条件になります。
  • 「経由するサービスは一つとは限らない」例として、公式ドキュメントでは以下のような例を挙げています。
    • ユーザーがDynamoDB テーブルから読み書きするためにCFnを使い、DynamoDB テーブルはKMSの暗号化機能を利用する
    • この場合のaws:CalledViaキーの状態を図示すると以下のようになります。KMS利用時にはaws:CalledViaキーにはCFnとDynamoDB2つの値が格納されています。

aws:CalledVia キー値にできるサービス

サービス名 ポリシーJSONに記載する値(サービスプリンシパル)
Amazon Athena athena.amazonaws.com
AWS CloudFormation cloudformation.amazonaws.com
Amazon DynamoDB dynamodb.amazonaws.com
AWS Key Management Service (AWS KMS) kms.amazonaws.com

aws:CalledViaFirstaws:CalledViaLastも追加されました

aws:CalledViaに加えて、aws:CalledViaFirstaws:CalledViaLastも追加されました。これらは、CalledViaリストの最初/最後の要素を判定条件に使うものです。例えば先程のCFnとDynamoDBとKMSを使う例であれば、KMSでの暗号化権限に以下のような条件を設定した場合、結果は偽になります。上記例でのaws:CalledViaFirstの値はCFnになるからです。

"Condition": {
    "StringEquals": {
        "aws:CalledViaFirst": [
            "dynamodb.amazonaws.com"
        ]
    }
}

補足: iam:PassRoleでやる方法

最初の、「CFnでVPCを作成する、でもCFn実行ユーザーにCFn外でVPCを作らせたくはない」場合はiam:PassRoleという権限を使用することでも実現可能です。

CFnスタック作成フォームの途中でIAMロールを選ぶ欄があるのをご存知でしょうか。 このIAMロールに、(CFnでの)各リソース作成を移譲する(=PassRoleする)ことができます。「スタック作成まではやるけど、その後のことはこのロールにおまかせよろしくー」みたいなイメージです。

1. リソース作成用ロールを作成する

「これまで: aws:CalledViaが無い世界」で作成したのと同じポリシーを付与したロールを作成します。 信頼関係でCFnを設定します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudformation.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

2. CFn実行用ユーザーのポリシーを修正

CFn実行用ユーザーのポリシーは、CFn全権限のAWS管理ポリシーであるAWSCloudFormationFullAccessと、以下のインラインポリシーです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "listrole",
            "Effect": "Allow",
            "Action": [
                "iam:ListRoles"
            ],
            "Resource": "*"
        },
        {
            "Sid": "passrole",
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": "arn:aws:iam::012345678901:role/createvpc-role"
        }
    ]
}
  • iam:ListRolesはCFnスタック作成フォーム内でIAMロールリストを表示するのに必要です。
  • iam:PassRoleはユーザーが IAM ロールを AWS サービスに渡すアクセス権限を定義します。今回の場合createvpc-roleというロールをCFnに渡す権限がこのユーザーに必要ですので、その設定を行なっています。(さらに厳密にやるなら、Conditionでiam:PassedToServiceを使って渡すサービスをCFnに絞っても良いでしょう。(参考:AWSサービスに渡すIAMロールを制限する))

3. スタック作成

上記CFn実行用ユーザーでスタックを作成します。途中アクセス許可欄で先程作ったIAMロールを指定します。

作成できました!

ちなみに

ロールのポリシーにaws:CalledViaを足してみました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "createvpc",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcs",
                "ec2:CreateVpc",
                "ec2:ModifyVpcAttribute",
                "ec2:createTags"
            ],
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "aws:CalledVia": [
                        "cloudformation.amazonaws.com"
                    ]
                }
            }
        }
    ]
}

これでスタック作成した場合はエラーになります。

PassRoleした場合はパス元のaws:CalledViaを引き継がないみたいですね。

参考リンク