※本記事は、筆者とAI(Claude)との対話内容をもとに、AIが内容を整理・構成した備忘録です。記事内のロール名・ポリシー例は概念説明用のサンプルであり、実際のAWS環境での動作確認までは行っていません。導入の際は、ご自身の環境で検証してください。
はじめに
CloudFormationでデプロイ用のIAM権限を設計していると、必ずと言っていいほど iam:PassRole という権限に出会う。名前が似ている sts:AssumeRole と何が違うのか、最初はかなり混乱した。本記事ではこの2つの違いを、図解を交えて整理する。
登場人物
説明を具体的にするため、以下の3人(3つ)を登場させる。
-
開発者ロール:
DeliveryMgmtPlatformDeployRole(デプロイを実行する開発者やCI/CDパイプラインが引き受けるロール) -
サービスロール:
CfnDeliveryMgmtServiceRole(EC2やS3などの操作権限を持つ、CloudFormation専用のロール) - CloudFormation: AWSのサービスそのもの
開発者ロールは、CfnDeliveryMgmtServiceRole が持つEC2やS3の操作権限を直接は持っていない。
iam:PassRole とは 「特定のロールを、特定のサービスに使わせてよいと許可する権限」
iam:PassRole は、自分がそのロールの権限を得る権限ではない。「このロールを、指定したAWSサービスに使わせてよい」とIAMに申告するための権限である。
開発者は CfnDeliveryMgmtServiceRole の権限を直接持っていないが、iam:PassRole の許可があれば、そのロールをCloudFormationに渡して、代わりに使わせることができる。
開発者ロールのIDポリシーには、こう書く。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/CfnDeliveryMgmtServiceRole",
"Condition": {
"StringEquals": { "iam:PassedToService": "cloudformation.amazonaws.com" }
}
}
]
}
Resource で「渡してよいロールはこの1つだけ」、Condition で「渡してよい相手はCloudFormationだけ」と絞り込む。これがないと、開発者が強力な別のロールを見つけてCloudFormationに渡し、間接的に権限を昇格させる抜け道が生まれてしまう。
sts:AssumeRole とは 「実際にそのロールの権限を一時的に借りて実行する権限」
sts:AssumeRole は、あるロールの権限を一時的に借りて、そのロールとして処理を実行するための操作である。これを呼び出す主体はCloudFormation自身であって、開発者ではない。
ロール側(CfnDeliveryMgmtServiceRole)の信頼ポリシーは、こう書く。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "cloudformation.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
つまり sts:AssumeRole が成立すると、CloudFormationは一時的にそのロールの権限を持った状態になり、STSが発行した有効期限付きの認証情報を使っている間だけその権限で作業できる。作業が終われば、その権限は失効する。
2つを合わせると、全体の流れはこうなる
開発者からリソースへ直接つながる矢印はどこにもない。必ずこの2つの権限チェックを経由する。
信頼ポリシーと権限ポリシーはAND条件
ここでもう一段深掘りする。ロールが実際に何かを実行できるかどうかは、2つの独立したチェックのAND条件で決まる。
-
信頼ポリシー(Trust Policy): 「誰がこのロールを引き受けてよいか」を決める入口の審査。
sts:AssumeRoleが成立するかどうかはここだけで決まる - 権限ポリシー(Permissions Policy): 引き受けた後、「何をしてよいか」を決める
信頼されていなければ、どれだけ強力な権限ポリシーがロールに付いていても、そもそも引き受けることすらできない。逆に引き受けられたとしても、権限ポリシーに書かれていないアクション(例えばテンプレートにRDSの作成を追加したのに権限ポリシーの更新を忘れた場合)は実行できない。両方が揃って初めて、実際の操作が成立する。
持ち主で整理する
開発者ロールとCloudFormation側、それぞれが何を持っているかで分けると、こうなる。
2つの権限は、それぞれ別の側が持っている。
比較表でまとめる
| iam:PassRole | sts:AssumeRole | |
|---|---|---|
| 誰の権限として設定する? | ロールを渡す側(開発者ロールなど)のIDポリシー | 引き受けられる側のロールの信頼ポリシー |
| 何を許可する? | そのロールを指定したAWSサービスに使わせてよいこと | 実際にそのロールの権限を一時的に借りて実行すること |
| 誰が実行する? | 人間・CI/CDパイプラインなど、ロールを渡す側 | AWSサービス(CloudFormationなど)自身 |
| これがないと | サービスにロールを指定してもエラーになる | ロールを引き受けること自体が拒否される |
他にどんな場面で使われるか
この2つの権限はCloudFormationに限った話ではない。見分け方はシンプルで、「ロールになりきるのは誰か」で分類できる。
人間自身がロールになりきる場合は sts:AssumeRole だけで完結し、iam:PassRole は登場しない。スイッチロールやクロスアカウントアクセス、SAML/OIDCによるID連携(AssumeRoleWithSAML / AssumeRoleWithWebIdentity)がこれにあたる。間にサービスを挟まず、本人が直接そのロールに切り替えるだけなので、渡す相手を許可する iam:PassRole は不要になる。
一方、AWSサービスがロールになりきる場合は、これまで見てきた iam:PassRole + sts:AssumeRole のペアが必要になる。EC2にインスタンスプロファイルとしてロールをアタッチする、Lambda関数に実行ロールを設定する、ECS/Fargateのタスクロールを設定する、CodeBuildやCodePipelineにサービスロールを指定する、といった場面はすべて同じ構造である。
| ケース | 使う権限 | 実際になりきるのは誰か |
|---|---|---|
| スイッチロール / クロスアカウントアクセス |
sts:AssumeRole のみ |
人間自身 |
| SAML / OIDC によるID連携 |
sts:AssumeRole のみ |
連携されたユーザー本人 |
| EC2インスタンスプロファイル |
iam:PassRole + sts:AssumeRole
|
EC2サービス |
| Lambda実行ロール |
iam:PassRole + sts:AssumeRole
|
Lambdaサービス |
| ECS/Fargateタスクロール |
iam:PassRole + sts:AssumeRole
|
ECSサービス |
| CodeBuild / CodePipelineのサービスロール |
iam:PassRole + sts:AssumeRole
|
CodeBuild / CodePipelineサービス |
まとめ
-
iam:PassRoleは「ロールを特定のサービスに使わせてよい」という許可であり、自分がそのロールの権限を得る権限ではない -
sts:AssumeRoleは「実際にそのロールの権限を一時的に借りて実行する」操作そのもの - ロールには信頼ポリシー(誰が引き受けられるか)と権限ポリシー(引き受けた後に何ができるか)があり、両方が揃って初めて操作が成立するAND条件
-
iam:PassRoleのResourceとiam:PassedToService条件で渡せるロールと相手を絞り込むことで、権限昇格を防げる
「ロールになりきるのは人か、サービスか」を意識しておくと、IAMロールが絡むあらゆる設計で迷わなくなる。