CloudFormationサービスロールの概要と注意点

背景

serverless frameworkではCloudFormationのサービスロールをdeploymentRoleというオプションで指定するのですが、そこら辺の記事を書こうとした際にそもそもCloudFormationのサービスロールとは何かということを書いてみたらすごく長くなったので、別記事に切り出しました。


CloudFormationサービスロールとは

CloudFormationサービスロールとはどんなものかというと、スタックを作成する際に、「実行者のIAMユーザーの権限ではなく、サービスに委譲したIAMロールに紐づいた権限でスタック作成が行われる様にしたい」というようなものです。

※CloudFormation以外のサービスロールにも共通する話です。


例えば、普段開発環境でCloudFormationスタックを作成する場合、基本的には「自分のIAMユーザー・グループに権限がアタッチされているかどうか」という視点で実行されている方が多いかと思います。

そして、スタックを作成しようとして権限エラーになった場合、自分のIAMユーザー(またはIAMグループ)にIAMポリシーを追加することで、スタック作成を成功させようとするかと思います。


これは一見当たり前のように見えますが、チーム開発を行う際に複数メンバーでそのスタックを管理していくということを考えた場合、それぞれのIAMユーザー・IAMグループに同じ権限が揃っていた方が管理の都合上良い場合があります。

全員を同じIAMグループに割り当てられれば多少は解消するかもしれませんが、そうもいかない様なケースも多いかと思われます。

その様な場合、もしある人がスタック変更をすることになったが権限エラーで変更ができなかったような時、たまたまその実行者一人に対してIAM権限を割り当ててその場を乗り切ったようなことが起きたとします。

ただそれは、その場ではスタック変更が問題なく行われたとしても、その次に今度は別の人がスタック変更を行うとなった際に同じく権限エラーが発生し、その対処のための権限を今度はその人にも割り当てて・・・みたいに段々とIAM権限の管理まわりで統制が取れなくなっていきます。(人によってついている権限がばらばら・ぐちゃぐちゃ)


また、管理の統制が取れなくなる以外にも問題があり、開発者のIAMユーザー・グループにそのスタックとそれに含まれるリソース作成に必要な権限を割り当てた際、CloudFormationスタック以外を通して(例えばコンソールやCLIで)そのリソース作成や変更・削除を行える様になってしまいます。


これは開発環境では特に問題にはならないかもしれませんが、本番環境のようなところでは、CloudFormationを通してのみリソースを作れれば良いはずなのに、スタック作成以外でもその権限を使用してリソースを操作できてしまいます。

このようにスタック作成のために開発者に権限を付与することで、本来不要・過剰な権限まで許可されてしまい、思わぬ出来事が起きかねません。

例)コンソール画面から間違えてアプリのリソースを削除してしまったり


そこで、CloudFormationスタックには、サービスロールというものをアタッチできます。

docs.aws.amazon.com


これは、CloudFormation APIコマンドのオプションに--role-arn ${IAM_ROLE_ARN}というように指定することで実現できます。

aws cloudformation deploy \
    --stack-name "test-stack" \
    --template-file "test.yaml" \
    --role-arn "arn:aws:iam::123456789012:role/Test-Service-Role"

こうすることで、このコマンドを投げたIAMユーザーに付与されている権限ではなく、arn:aws:iam::123456789012:role/Test-Service-Roleというサービスロール(IAMロール)に紐づいた権限でデプロイが行われます。


サービスロールを指定する場合、コマンド実行者は、そのIAMロールに対するiam:PassRole権限と、CloudFormationサービスへのアクション権限(cloudformation:~~)があれば、スタックを作成できます。

例)スタックにLambdaがあり、コマンド実行者にLambda作成の権限がなかった場合にも、CloudFormationを通してデプロイができる。


CloudFormationサービスロールの定義方法

ちなみに、CloudFormationサービスロールはこのように定義します。

Policies部分は例ですので、適宜変更してください)

  DeployRoleForCloudFormation:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "Test-Service-Role"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service:
                - "cloudformation.amazonaws.com"
      Policies:
        - PolicyName: "Test-Service-Policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ecs:*"
                Resource:
                  - "*"

CloudFormationのテンプレートとしてyamlで書いていますが、AssumeRolePolicyDocumentが大事で、cloudformation.amazonaws.comというサービスがこのRoleをsts:AssumeRoleできますよ、というものです。


このIAMロールをサービスロールとしてスタックをデプロイする場合、実行者にはこのような権限をアタッチします。

          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "iam:PassRole"
                Resource:
                  - !GetAtt DeployRoleForCloudFormation.Arn
              - Effect: "Allow"
                Action:
                  - "cloudformation:*"
                Resource:
                  - "*"

これは、このポリシーがアタッチされたIAMユーザーは、上記で定義したDeployRoleForCloudFormationというIAMロールを、CloudFormationのサービスロールとして使用(PassRole)できますよ、というものです。

また、CloudFormation APIを叩くためにCloudFormation自体の操作権限も付与してあります。(ここではワイルドカードを割り当てていますが、実際は絞ってください)


注意点

ただし、気をつける点があります。

一度サービスロールをアタッチしてスタックを作成したら、次にそのスタックを変更するIAMユーザーがiam:PassRole権限を持っていなくても、デプロイコマンド(CloudFormation API)に--role-arnをつけずに実行すれば、スタック操作ができてしまう点は注意が必要です。

--role-arnをつけて実行した場合は、ちゃんとiam:PassRoleエラーになります)

(CloudFormationに対するアクション権限は必要です)


これは不可解な点もありますが、PassRoleというのはロールをそのサービスに委譲するアクションであり、一度委譲したら再度委譲せずとも(委譲が済んでいるため)、そのサービスは委譲された権限をずっと使えますよってことなのでしょう。


しかし、該当サービスロールに対するiam:PassRole権限を持っていなくても、CloudFormationアクション権限さえ持っていればスタックを更新・削除できてしまうので、以下のような対策が必要です。

  • そもそもCloudFormation権限を必要な開発者以外に与えない
  • スタックポリシーを設定して想定外の更新を防ぐ

docs.aws.amazon.com


また、一度サービスロールをアタッチしてスタックを作成したら、その後--role-arnをつけないでデプロイしても、サービスロールがスタックからデタッチされることはありません。(別のRole ARNを指定して、サービスロールを付け直すことはできます)

--role-arnに空文字を渡してデプロイした場合、バリデーションエラーで失敗します。

Parameter validation failed:
Invalid length for parameter RoleARN, value: 0, valid range: 20-inf


ただ、このような注意点を以ってしても、やはりサービスロールを使用することによるメリットは大きいでしょう。

大きな組織・チームほど利用した方が良いのではないかと思われます。