AWS Organizationsを使用できない場合にAWS Configを全リージョンで有効化する方法 [AWS CloudFormationテンプレート付き]

こんにちは。SCSK渡辺(大)です。

今回は、
AWS Organizationsを使用できない場合にAWS Configを全リージョンで有効化する方法
を考えてみました。

背景

AWSアカウントのセキュリティ監視をしたい

AWSアカウントのセキュリティ監視をしたいとなった場合、AWS Security Hubを利用することが候補に挙がるかと思います。
AWS Security Hubを利用するための前提として、AWS Configを有効化している必要があります。
AWS Configのベストプラクティスには以下のように書かれています。

AWS Config ベストプラクティス | Amazon Web Services ブログ
—抜粋—————————————————————
1.すべてのアカウントとリージョンで AWS Config を有効にします。
これは、 Center for Internet Security (CIS) が推奨する業界のベストプラクティスです。AWS Config を使用すると、AWS リソースの設定を監査し、設定のベストプラクティスに確実に準拠することができます。 AWS CloudFormation StackSets を使用すると、共通の CloudFormation テンプレートを使用して、複数のアカウントとリージョンで AWS Config を有効にできます。
———————————————————————-

上記の通り、AWS CloudFormation StackSetsを使用することで複数アカウントとリージョンでAWS Configを有効にできます。
AWS CloudFormation StackSetsには2種類あります。

  • セルフマネージド型 : AWS Organizationsを使えなくても問題ありません
  • サービスマネージド型: AWS Organizationsを使えることが前提です

今回はAWS Organizationsを使用できない場合を想定しているため、セルフマネージド型を利用します。
セルフマネージド許可を持つ CloudFormation StackSets を作成する – AWS CloudFormation

上記リンク先にAWS Configを有効化するためのAWS CloudFormationテンプレートが用意されていますが、自分が作りたい構成には過剰だったため、オリジナルのAWS CloudFormationテンプレート(以降、Cfnテンプレート)を作ることにしました。

余談

ちなみに、「利用しないリージョンは無効化にしておけばセキュリティ監視が不要になるから、使用するリージョンだけ有効化して、AWS Management ConsoleからAWS Configを有効化するならそこまで手間ではないのでは?」と一度は考えましたが、悲しいことに、デフォルトで有効になっているリージョンは無効にすることはできません。
デフォルトで有効になっているリージョンは記事執筆時点で17個ありますので、仮に監視対象のAWSアカウントが10個あったとしたら、AWS Management Consoleでリージョン切替とAWS Config有効化を170回繰り返す必要があります…。

アーキテクチャ

AWSアカウント

今回は2つのアカウントを用意しました。

  • AWS Configアグリゲータアカウント(以降、アグリゲータアカウント)
  • AWS Configソースアカウント(以降、ソースアカウント)

使用するサービス

以下のサービスを使用します。

  • Amazon S3
  • AWS CloudFormation

作業内容

以下の作業を実施します。

  • From:アグリゲータアカウント、To:アグリゲータアカウント
    • AWSマネジメントコンソールからAmazon S3バケットを作成し、AWS CloudFormationテンプレート(以降、Cfnテンプレート)をアップロードする
    • AWS CloudFormationでAWS CloudFormation StackSets セルフマネージド型用のIAMロールを作成する
    • AWS CloudFormationでAWS Configの配信先(Amazon S3バケット)を作成する
    • AWS CloudFormationでAWS Config用のIAMロールを作成する
  • From:ソースアカウント、To:ソースアカウント
    • AWS CloudFormationでAWS CloudFormation StackSets セルフマネージド型用のIAMロールを作成する
    • AWS CloudFormationでAWS Config用のIAMロール、を作成する
  • From:アグリゲータアカウント、To:ソースアカウント
    • AWS CloudFormation StackSets セルフマネージド型でAWS Configを有効化にする
  • From:ソースアカウント、To:アグリゲータアカウント
    • AWS CloudFormation StackSets セルフマネージド型でAWS Configを有効化にする

構成図

上記の設計を図にすると以下になります。

 

事前作業

Amazon S3バケットにCfnテンプレートをアップロードします。
ゴールは下図のようにアップロードされていることです。

Amazon S3バケット作成

作業実施アカウント

  • アグリゲータアカウント

推奨設定

  • ブロックパブリックアクセスを有効化
  • ソースアカウントからもGetObjectできるようにバケットポリシーを設定する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ソースアカウントのAWSID}:root"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::${S3バケット名}/*"
        }
    ]
}

 

配置するCfnテンプレートは次の通りです。

Cfnテンプレート: AWSCloudFormationStackSetAdministrationRole

AWS CloudFormation StackSets セルフマネージド型で別アカウントにリソースを作成することが出来るようにするためのAWS IAMローカルを作成します。(AWSユーザーガイドからダウンロードできます)

AWSTemplateFormatVersion: 2010-09-09
Description: Configure the AWSCloudFormationStackSetAdministrationRole to enable use of AWS CloudFormation StackSets.

Parameters:
  AdministrationRoleName:
    Type: String
    Default: AWSCloudFormationStackSetAdministrationRole
    Description: "The name of the administration role. Defaults to 'AWSCloudFormationStackSetAdministrationRole'."
  ExecutionRoleName:
    Type: String
    Default: AWSCloudFormationStackSetExecutionRole
    Description: "The name of the execution role that can assume this role. Defaults to 'AWSCloudFormationStackSetExecutionRole'."

Resources:
  AdministrationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref AdministrationRoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudformation.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: AssumeRole-AWSCloudFormationStackSetExecutionRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - sts:AssumeRole
                Resource:
                  - !Sub 'arn:*:iam::*:role/${ExecutionRoleName}'

Cfnテンプレート: AWSCloudFormationStackSetExecutionRole

AWS CloudFormation StackSets セルフマネージド型で別アカウントからリソースを作成することが出来るようにするためのAWS IAMロールを作成します。(AWSユーザーガイドからダウンロードできます)

AWSTemplateFormatVersion: 2010-09-09
Description: Configure the AWSCloudFormationStackSetExecutionRole to enable use of your account as a target account in AWS CloudFormation StackSets.

Parameters:
  AdministratorAccountId:
    Type: String
    Description: AWS Account Id of the administrator account (the account in which StackSets will be created).
    MaxLength: 12
    MinLength: 12
  ExecutionRoleName:
    Type: String
    Default: AWSCloudFormationStackSetExecutionRole
    Description: "The name of the execution role. Defaults to 'AWSCloudFormationStackSetExecutionRole'."

Resources:
  ExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref ExecutionRoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Ref AdministratorAccountId
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess

Cfnテンプレート: PreAWSConfigAggregator

アグリゲータアカウントで集約するアグリゲータアカウント自身およびソースアカウントのAWS Configデータを集約するためのAmazon S3バケットを作成します。また、アグリゲータアカウント自身のAWS Config用のAWS IAMロールを作成します。

Q. aws:PrincipalOrgIDを使っている理由は?
A. ソースアカウントが増えるたびにAmazon S3バケットポリシーを変更したくないからです。予期せぬアカウントからアクセスされる懸念については、Amazon S3バケット名が漏洩しない限りは問題ないと考えての設計です。「危険だ!」という場合にはアカウント単位での指定方法(aws:PrincipalAccount)も用意されているので、以下リンク先の内容を参考にカスタマイズしてご利用ください。
Amazon global condition context keys
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  AdministratorAccountS3Bucket:
    Description: AWS account S3Bucket of the administrator account (security monitoring administrator account)
    Type: String
  OrganizationID:
    Description: Organization ID
    Type: String

Resources:
  ConfigBucket:
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref AdministratorAccountS3Bucket
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
  ConfigBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ConfigBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: AWSConfigBucketPermissionsCheck
            Effect: Allow
            Principal: "*"
            Action: s3:GetBucketAcl
            Resource: !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}"
            Condition:
              StringEquals:
                "aws:PrincipalOrgID": !Ref OrganizationID
          - Sid: AWSConfigBucketExistenceCheck
            Effect: Allow
            Principal: "*"
            Action: s3:ListBucket
            Resource: !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}"
            Condition:
              StringEquals:
                "aws:PrincipalOrgID": !Ref OrganizationID
          - Sid: AWSConfigBucketDelivery
            Effect: Allow
            Principal: "*"
            Action: s3:PutObject
            Resource: !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}/AWSLogs/*/Config/*"
            Condition:
              StringEquals:
                "s3:x-amz-acl": "bucket-owner-full-control"
                "aws:PrincipalOrgID": !Ref OrganizationID
          - Sid: AWSConfigBucketSecureTransport
            Effect: Deny
            Principal: "*"
            Action:
              - s3:*
            Resource:
              - !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}"
              - !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}/*"
            Condition:
              Bool:
                aws:SecureTransport: false
  ConfigRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AWSConfigCustomRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: config.amazonaws.com
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWS_ConfigRole

Outputs:
  ConfigBucketName:
    Description: Name of the S3 bucket for AWS Config data
    Value: !Ref ConfigBucket
  ConfigRoleArn:
    Description: ARN of the IAM role for AWS Config
    Value: !GetAtt ConfigRole.Arn

Cfnテンプレート: PreAWSConfigSource

ソースアカウント自身のAWS Config用のAWS IAMロールを作成します。

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  AdministratorAccountS3Bucket:
    Description: AWS account S3Bucket of the administrator account (security monitoring administrator account)
    Type: String

Resources:
  ConfigRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AWSConfigCustomRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: config.amazonaws.com
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWS_ConfigRole
      Policies:
        - PolicyName: ConfigS3BucketAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                Resource:
                  - !Sub "arn:aws:s3:::${AdministratorAccountS3Bucket}/AWSLogs/*"
                Condition:
                  StringLike:
                    "s3:x-amz-acl": "bucket-owner-full-control"
              - Effect: Allow
                Action:
                  - "s3:GetBucketAcl"
                Resource: "arn:aws:s3:::${AdministratorAccountS3Bucket}"

Outputs:
  ConfigRoleArn:
    Description: ARN of the IAM role for AWS Config
    Value: !GetAtt ConfigRole.Arn

Cfnテンプレート: EnableAWSConfig

アグリゲータアカウントまたはソースアカウントでAWS Configを有効化にします。

Q. IncludeGlobalResourceRegion: !Equals [!Ref AWS::Region, us-east-1]を使っている理由は?
A. ベストプラクティスに準拠するためです。
AWS Config ベストプラクティス
—抜粋—————————————————————
3.1 つのリージョンでのみグローバルリソース (IAM リソースなど) を記録します。
これにより、IAM 設定アイテムの冗長コピーをすべてのリージョンで取得することがなくなります。それは費用の節約にもなります。
———————————————————————-
AWSTemplateFormatVersion: "2010-09-09"
Conditions:
  IncludeGlobalResourceRegion: !Equals [!Ref AWS::Region, us-east-1]

Parameters:
  AdministratorAccountS3Bucket:
    Description: AWS account S3Bucket of the administrator account (security monitoring administrator account)
    Type: String

Resources:
  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      Name: !Sub "config-${AWS::AccountId}"
      RecordingGroup:
        AllSupported: true
        IncludeGlobalResourceTypes:
          !If [IncludeGlobalResourceRegion, true, false]
      RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/AWSConfigCustomRole
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    Properties:
      Name: !Sub "config-${AWS::AccountId}"
      S3BucketName: !Ref AdministratorAccountS3Bucket
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: TwentyFour_Hours

Outputs:
  ConfigRecorderName:
    Description: Name of the AWS Config Recorder
    Value: !Ref ConfigRecorder
  DeliveryChannelName:
    Description: Name of the AWS Config Delivery Channel
    Value: !Ref ConfigDeliveryChannel

余談

本記事では説明のためにCfnテンプレートを分けていますが、AWSCloudFormationStackSetAdministrationRoleとAWSCloudFormationStackSetExecutionRoleはPreAWSConfigAggregatorやPreAWSConfigSourceにまとめることが出来ると思います。

 

設定作業

AWS CloudFormationで設定していきます。

Cfnテンプレート: AWSCloudFormationStackSetAdministrationRole

作業実施アカウント

  • アグリゲータアカウント
  • ソースアカウント

パラメーター

  • 何も変更しません

確認画面

  • 下図のようになっていればOKです

Cfnテンプレート: AWSCloudFormationStackSetExecutionRole

作業実施アカウント

  • アグリゲータアカウント
  • ソースアカウント

パラメーター

  • AdministratorAccountIdを設定します
    • アグリゲータアカウント側ではソースアカウントAWSIDを設定します
    • ソースアカウント側ではアグリゲータアカウントAWSIDを設定します

確認画面

  • 下図のようになっていればOKです

Cfnテンプレート: PreAWSConfigAggregator

作業実施アカウント

  • アグリゲータアカウント

パラメーター

  • AdministratorAccountS3Bucketを設定します
    • 任意の名前で設定します(AWSの全てのリージョンにわたって一意である必要があります)
  • OrganizationID(組織ID)を設定します
    • 組織IDを設定します
    • 組織IDの確認方法は、画面右上のログインユーザーをクリックした後に「組織」をクリックすると見れます

確認画面

  • 下図のようになっていればOKです

Cfnテンプレート: PreAWSConfigSource

作業実施アカウント

  • ソースアカウント

パラメーター

  • AdministratorAccountS3Bucketを設定します
    • PreAWSConfigAggregatorで設定した名前を指定します

確認画面

  • 下図のようになっていればOKです

Cfnテンプレート: EnableAWSConfig

これまでのCfnテンプレートとは異なり、AWS CloudFormation StackSets セルフマネージド型で実行します。

作業実施アカウント

  • アグリゲータアカウント
  • ソースアカウント

パラメーター

  • アグリゲータアカウントとソースアカウントとも共通で、AdministratorAccountS3Bucketを設定します
    • PreAWSConfigAggregatorで設定した名前を指定します

作業画面

  • 基本的にはアグリゲータアカウントとソースアカウントとも共通です。
  • 「StackSetを作成」をクリックします

 

  • CfnテンプレートのURLを入力して「次へ」をクリックします

 

  • StackSet名とAdministratorAccountS3Bucketを設定し、「次へ」をクリックします

 

  • StackSetオプションは何も変更せずに「次へ」をクリックします

 

  • アグリゲータアカウントの場合には、アカウント番号にはソースアカウントのAWSIDを設定します
       

 

  • ソースアカウントの場合には、アカウント番号にはアグリゲータアカウントのAWSIDを設定します

 

  • リージョンを指定します。
    以下は有効化になっている全てのリージョンに対してAWS Configを有効化する場合の設定方法です。
    まず、有効化になっているリージョンを確認します。
    画面右上のログインユーザーをクリックした後に「アカウント」をクリックすると見れます。
                                 

     

 

  • AWS CloudFormation StackSets の画面に戻り、「すべてのリージョンを追加」をクリックします。
     

 

  •  ステータスが無効になっているリージョンを削除します。

 

  • デプロイオプションでは、リージョンの同時実行で「並行」を選択して、「次へ」をクリックします。

確認画面

  • 下図のようになっていればOKです


 

  • 実行すると下図のようになります。

 

  • 実行が完了すると下図のようにステータスが「SUCCEEDED」になります。

 

  • AWS Configが有効化されており、設定から配信先のAmazon S3バケット名が想定通りであることを確認します。

 

以上です。

ソースアカウントを追加したい時はどうしたら良いの?

追加したいソースアカウントでPreAWSConfigAggregatorを実行した後、アグリゲータアカウントのAWS CloudFormation StackSetsからEnableAWSConfigを選択した後、アクションから「StackSetにスタックを追加」をクリックすることで、追加したいソースアカウントに対してスタックを作成することができます。

 

なお、aws:PrincipalOrgIDを使わずにaws:PrincipalAccountを使う場合には、配信先のAmazon S3バケットにおいて、バケットポリシーの変更が必要になります。

 

まとめ

AWS CloudFormation StackSetセルフマネージド型はAWS Configの有効化の他にも、様々なことに活用できそうだと感じました。

タイトルとURLをコピーしました