Dev Study
AWSサービスの内部原理 コース

82. タスクは2枚のロールを持つ — 荷物を運ぶ胴元の権限と、中で働くアプリの権限

Lambdaでは実行ロールは1枚でしたが、ECS/Fargateのタスクには性質の違うロールが2枚あります — なぜ2枚に分かれるのか、その2枚がどう配られ、どちらが足りないと何が壊れるのかを図で追っていきます。

① なぜ2枚に分かれるのか

タスクを立ち上げる
それぞれの主体に最小権限を
エージェント(胴元)ECS/Fargateが動かす
イメージをpull・ログを送る・秘密を取ってくる
コンテナ内アプリあなたのコード
S3を読む・DynamoDBを叩く 等
タスク実行ロールtask execution role
タスクロールtask role
  • 実行ロールは「Amazon ECS container and Fargate agents に、あなたに代わってAWS APIを呼ぶ権限を与える」もの(=荷物を運ぶ胴元の権限)。公式の言い回しは “grants the Amazon ECS container and Fargate agents permission to make AWS API calls on your behalf”。
  • タスクロールは「タスク内のコンテナで動くアプリケーションコードが、S3などの他のAWSサービスを使うための権限」。公式は “allows your application code (running in the container) to use other AWS services”。逆側も明言されている: タスクロールの権限は “aren't accessed by the Amazon ECS container and Fargate agents” — 胴元はタスクロールに触れない。分離は双方向。
  • Lambdaで1枚だったのは、コードを動かす人=コードそのもの、が実質同一だったから。ECSでは「箱を運ぶ人(エージェント)」と「箱の中で働く人(アプリ)」が別なので、権限も2枚に割れる。

ロールが2枚あるのは冗長ではなく、「胴元の仕事」と「アプリの仕事」という別々の主体に、それぞれ最小権限を与えるため。これは認証暗号編の「権限の分離」というCS基礎そのものです。

② 実行ロールは起動前の下ごしらえ一式

タスク実行ロール胴元=ECS/Fargateエージェントが使う
[2] 秘密を取ってくる(任意)
イメージをpullECRプライベートリポジトリ
ecr:GetAuthorizationToken / ecr:BatchGetImage / ecr:GetDownloadUrlForLayer
[3] コンテナ起動 → ログの口を開ける
秘密を注入Secrets Manager / SSM Parameter Store
環境変数や設定に注入(アプリには生値だけ届く)
ログの口を開けるawslogsドライバ → CloudWatch Logs
logs:CreateLogStream + logs:PutLogEvents
  • この用途をまとめて満たすのがマネージドポリシー AmazonECSTaskExecutionRolePolicy。公式は “contains the permissions the common use cases previously described require”。ECRのpull権限もこの中に含まれる。
  • Secrets Manager参照には secretsmanager:GetSecretValue、SSM Parameter Store参照には ssm:GetParameters を追加(マネージドポリシーに含まれない用途はインラインポリシーで自分で足す、が公式の案内)。カスタムKMSキー使用時は kms:Decrypt も。
  • 重要: これらの認証情報は “aren't directly accessible by the containers in the task” — コンテナの中のアプリからは見えない。秘密の「取得役」は胴元であって、アプリは取得済みの生値を受け取るだけ。
  • 実行ロールは container agent version 1.16.0 以降でサポート。

実行ロールは「アプリが1行も動く前」の下ごしらえ担当。イメージ・秘密・ログの口という、タスクを成立させるインフラ配線を胴元が握ります。可観測性編で見たLambdaのログ3権限(AWSLambdaBasicExecutionRole)が、ECSではこの実行ロール側に現れる、というのが横のつながりです。

③ タスクロールの配給経路

コンテナ内アプリAWS SDK / CLI
② ECS既定ホストの後ろに連結してGET
相対URI環境変数AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
値は相対パス(例: /v2/credentials/…)
③ HTTP GETで一時的認証情報を取得
169.254.170.2リンクローカルアドレス
ルーティングされず同一ホスト内でだけ届く局所回線
タスクロールの権限で(期限付き・自動更新)
一時的認証情報アクセスキーID・シークレット・セッショントークン・有効期限
S3などを呼べる起動タイプに依存しない同じ取得口
  • SDKがエンドポイントを見つける仕組み(container credential provider): ECSが環境変数 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI をセットし、SDKはその値を既定ホスト 169.254.170.2 に連結してGETする。公式SDKリファレンスの原文は “The value is appended to the default Amazon ECS hostname of 169.254.170.2”。
  • 図中の /v2/credentials/… というパス形式は実際にECSエージェントが払い出す値の例で、公式に裏取りできる骨格は「相対URIが 169.254.170.2 に連結される」という点。EKS Pod Identityでは代わりに AWS_CONTAINER_CREDENTIALS_FULL_URI + 認可トークンファイルを使う別ルートもある。
  • 169.254.170.2 はリンクローカルアドレス(169.254.0.0/16)。EC2のインスタンスメタデータサービス(IMDS, 169.254.169.254)と同型の「メタデータ配給専用の局所回線」という手法。
  • Fargateでは “EC2 instance profiles are not available for containers”。それでもアプリのコードは起動タイプ(Fargate / EC2 / 外部インスタンス)を意識せず同じ 169.254.170.2 から認証情報を受け取れる — 公式がこれを uniform credentials delivery と呼ぶ。

タスクロールは、リンクローカルの局所回線から一時的認証情報として配給されます。アプリは長期キーを埋め込む必要がなく、起動タイプにも依存しません。「リンクローカルからメタデータを配る」というEC2 IMDSと同じ設計が、ここでも使われています。

④ 信頼ポリシーと監査

実行ロール
タスクロール
confused deputy(混同した代理人)対策で入口を絞る
ecs-tasksPrincipal: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
実行後: 借用の履歴が出口に残る
Conditionaws:SourceAccount / aws:SourceArn
自分のアカウント発・自分のECS発の要求かを検証(SourceArn は arn:aws:ecs:region:acct:* のワイルドカード)
CloudTrailセッションに taskArn のコンテキストが付く
どのタスクが権限を借りて呼んだかを後から追える
  • 両ロールとも信頼するのは ecs-tasks.amazonaws.com。sts:AssumeRole により、EC2インスタンスのロールを継がずタスク独自のロールを名乗れる(公式: タスクは “doesn't inherit the role associated with the Amazon EC2 instance”)。
  • confused deputy対策の公式推奨は、タスクロール作成の節に明記: “When creating your task IAM role, we recommend that you use the aws:SourceAccount or aws:SourceArn condition keys in the trust relationship policy ... to prevent the confused deputy security issue”。実行ロールも同じ ecs-tasks.amazonaws.com を信頼するため、同じ条件キーで絞る設計がそのまま適用できる。
  • aws:SourceArn で特定クラスタを指すのは現状未対応なので、ワイルドカード :* で全クラスタを指定する(これも公式明記)。
  • 監査(Auditability): “Task credentials have a context of 'taskArn' that is attached to the session, so CloudTrail logs show which task the role credentials were vended for.” — CloudTrailで「どのタスクに払い出された認証情報か」が見える。

信頼ポリシーは入口(誰が名乗れるか)を絞り、CloudTrailのtaskArnは出口(誰が借りて何をしたか)を記録します。認証暗号編の「ロール=一時的認証情報の借用」に、借用者の身元証明と借用履歴が付いた形です。

⑤ トラブル切り分け

動く前の失敗イメージpull失敗・ログ欠落・秘密注入で起動失敗
pull失敗は CannotPullContainerError
動いた後の失敗アプリのS3アクセスが AccessDenied
EC2起動タイプに残る裏口
実行ロール不足胴元の準備権限が無い
ECRのpull権限 / awslogsのlogs:* / GetSecretValue・GetParameters
タスクロール不足アプリ自身の権限が無い
IMDSの裏口169.254.169.254
awsvpc: ECS_AWSVPC_BLOCK_IMDS=true / bridge: iptablesでDROP
  • pull失敗・ログ欠落・秘密注入失敗は、いずれもアプリが動く前の準備段階=胴元の仕事。だから実行ロールを疑う。ECRのpull権限は「基本実装ではタスクロールではなく実行ロール側に置く」のが公式の設計(アプリ自身がECR APIを直接叩く場合だけタスクロールにもECR権限を足す)。
  • アプリからのS3が AccessDenied は、アプリが動いた後にアプリ自身が呼んだ結果。だからタスクロールを疑う。CannotPullContainerError は公式トラブルシューティングに実在する症状名。
  • EC2起動タイプの落とし穴: コンテナがEC2の IMDS(169.254.169.254)経由でインスタンスプロファイルの認証情報に手が届いてしまうことがある(公式もEC2ではタスク間の分離が無く、コンテナがIMDS経由の認証情報にアクセスしうると注意)。
  • awsvpcモードでの公式の塞ぎ方: “To prevent containers run by tasks that use the awsvpc network mode from accessing the credential information supplied to the Amazon EC2 instance profile ... set the ECS_AWSVPC_BLOCK_IMDS agent configuration variable to true in the agent configuration file and restart the agent” — 設定ファイルに書いてエージェントを再起動する。bridgeモードでは iptables で 169.254.169.254/32 をDROPする方法が案内されている。

「アプリが動き出す前」の失敗は実行ロール、「動いた後」の失敗はタスクロール — この1本の軸で切り分けられることが、2枚に分ける設計の実務上の見返りです。EC2ではIMDSという裏口が残るので、最小権限を守るなら塞ぐ、というのが③のリンクローカル設計の裏返しの注意点です。

公式ドキュメントで詳しく ↗