CloudFormationを扱う際のIAMの考え方


あるスタックを作成するケースを考える

SQSのキューを1つだけ作るテンプレートを用意する。

sample.cf.yml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  SampleQueue:
    Type: AWS::SQS::Queue

とあるIAMユーザーのプロファイルを指定してcreate-stackを実行。

このIAMユーザーはcloudformation:*のみを許可されているものとする。

$ aws cloudformation create-stack --stack-name sample --template-body file://./sample.cf.yml --profile sample-user
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxx:stack/sample/b579cc80-aaf1-11ea-bafc-0a62df4444de"
}

マネコンからステータスを確認すると、キューの作成に失敗している。

これはIAMポリシーの不足が原因。

基本的にcreate-stackを実行するユーザーは、スタックに含まれるリソースそれぞれに対して作成・取得・削除等の権限を有している必要がある。

*:create*だけを与えればいいかというとそうでもない。例のIAMユーザーにsqs:CreateQueueのポリシーを付与して再度実行すると今度は以下のエラーが発生する。

API: sqs:GetQueueAttributes Access to the resource https://sqs.ap-northeast-1.amazonaws.com/xxxx/sample-SampleQueue-G5X7EGA2HGMA is denied.

ステータスの取得やロールバック時のリソースの削除などにも対応するために、結局はフルアクセスを与えておくのが早かったりする。

ただここで問題となるのがメンバーのIAM管理の話。

スタックに併せてリソースの作成・削除権限をやみくもに与えていくのはセキュリティ上好ましくない。

リソースで制限をかけるとしても、まだ作成される前のスタックに関してはリソース名が不定なことが多いのでそもそも不可能。

じゃあどうするのか

結論: サービスロールを使いましょう

What is サービスロール

AWS の多くのサービスでは、ロールを使用して、ユーザーに代わって該当サービスが他のサービスのリソースにアクセスすることを許可する必要があります。サービスがお客様に代わってアクションを実行するために引き受けるロールは、サービスロールと呼ばれます。

IAMユーザーがユーザーに対してAWS APIのアクセスを許可するように、サービスロールはAWSのサービスに対して別のサービスへの操作権限を許可します。

AWS サービスにアクセス許可を委任するロールの作成 - AWS Identity and Access Management

例えば Lambda なんかは CloudWatch Logs にログを送信するためにlog:PutLogEventsを持っている必要があったりしますが、この権限をLambdaサービスに対して許可するのがサービスロールです。

Lambdaの場合関数作成時にデフォルトのLambdaBasicExecutionRoleで諸々付与してくれるのでマネコンで作ってるとあまり意識しないかもしれません。

ロールの引受先としてLambdaサービス(lambda.amazonaws.com)が設定されていることがわかります。

CloudFormationで作る場合はこんな感じでロールを自分で作ります。

IAMRoleForLambda:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess

そんな感じで、これをCloudFormationのスタック作成時に応用するのがベストプラクティスになります。

つまり、CloudFormationサービスにサービスロールを引き受けさせて、そのロールの権限のもとスタックに含まれるリソースの作成を行うというわけです。

閑話休題:Service Linked Role

てなわけでサービスロールの作成に移りたいんですが、ここでService Linked Roleについても少し触れておきます。

日本語にするとサービスにリンクされたロールですが、

サービスにリンクされたロールは、AWS サービスに直接リンクされた一意のタイプの IAM ロールです。サービスにリンクされたロールは、サービスによって事前定義されており、お客様の代わりにサービスから他の AWS サービスを呼び出す必要のあるアクセス権限がすべて含まれています。

サービスにリンクされたロールの使用 - AWS Identity and Access Management

AWS側で事前に定義してくれているもの、という理解でよさそうです。

例えばECSの場合、AWSServiceRoleForECSというものが定義されています。

ECSが必要とするロードバランサー、Route53、オートスケーリングなどの各種操作権限が含まれています。

こういうテンプレートがあると aws iam create-service-linked-role
--aws-service-name <value>
で簡単にロールが作成できるのですが、このService Linked Role、全てのサービスに定義されているわけではありません。

こちらに一覧がありますが、 CloudFormation は No となっていますね。

AWS Services That Work with IAM

閑話休題でした。

サービスロールの作成

CloudFormationサービスを引き受け先として、SQSのフルアクセスを許可したサービスロールを作成します。

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  CloudFormationServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - cloudformation.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: create-sqs-resource
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action: 
                  - sqs:*
                Resource: 
                  - "*"

サービスロールの使用

create-stackのオプションとして--role-arnがあります。

これを指定すると、CloudFormationがそのロールを引き受けてスタックの操作を行います。ちなみに、指定していない場合はAPIを実行したユーザーの一時的なセッションが使われます。

IAMユーザーにはiam:PassRole権限が必要になります。サービスに対してロールを渡す操作の許可です。

$ aws cloudformation create-stack --stack-name sample --template-body file://./sample.cf.yml --role-arn arn:aws:iam::xxxxxxx:role/xxxxxx

これで、IAMユーザーにSQSリソースに対する権限がなくてもスタックの作成を実行することができるようになりました。

直接ユーザーに権限を付与していくのではなく、サービスロールを使おうねという話でした。