私の戦闘力は53万です

awsとgcpについて書きます

AWS organizationsでcloudtrailを別Accountへ権限移譲する

AWS Organizationsの連携機能を色々と試しており、
cloudtrailでも連携機能を試してみました。

下記を参考にさせて頂いており、
KMSも含めて権限移譲をやろうとすると割と面倒で、
最終的にcloudformationを作成したのでブログ化してみました。 fu3ak1.hatenablog.com

Organizations とCloudtrail連携機能(AWS純正)

AWS Organizationsでは、Cloudtrailと連携し、
複数アカウントでのcloudtrail設定を簡単に実現できます。
cloudtrailで単純にorganizations連携をすると
下記のようになります。

f:id:remmemento:20210501182513p:plain
単純なorganizations+cloudtrail

上記でも十分にcloudtrailログを集約できるのですが
AWSのマルチアカウントのベストプラクティスでは、
ログ集約用の目的別のアカウント
(以下、LogArchiveアカウントと表現します)の別途作成を推奨しています。
要は何でもかんでも、親アカウントに集めるのは
権限が集約しすぎて良くないということですね。
上記理由から、純正機能でのcloudtrail集約をそのまま利用すると
ベストプラクティスに沿っていないのかと思いました。
aws.amazon.com

ベストプラクティス風にするには

LogArchiveアカウントを別にした構成を考えると
下記のように設定したいと考えました。

f:id:remmemento:20210501180719p:plain
構成イメージ

別途LogArchiveアカウントを作成し、
AdminAccountで収集したcloudtrailログを
LogArchiveアカウントに集約します。

上記、手動で設定しても良いですが、
割とポリシー系が面倒だったので、
cloudformationを作ってしてみました。

1.LogArchiveアカウントでの作業

f:id:remmemento:20210501180719p:plain まず、LogArchiveアカウントで、
S3とKMSを作成します。
LogArchiveアカウントで以下のcloudformationを適応します。

AWSTemplateFormatVersion: "2010-09-09"
Description: A templete for

Parameters:
  EnvPrefix:
    Type: String
    Default: OrgTrail
  orgAccountId:
    Type: String
    Default: xxxxxxxxxxxx
    Description: "Organization Admin Account Id"
  S3BucketName:
    Type: String
    Default: xxxxxxxxxxxxxxxxxxxxxxxxxx
    Description: "bucket name for cloudtrail"

Resources:
  mykms:
    Type: AWS::KMS::Key
    Properties:
      Description: !Sub ${EnvPrefix}Key
      Enabled: true
      EnableKeyRotation: true
      KeyUsage: ENCRYPT_DECRYPT
      PendingWindowInDays: 30
      KeyPolicy:
        Version: '2012-10-17'
        Id: key-policy
        Statement:
        - Sid: Enable IAM User Permissions
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
          Action: kms:*
          Resource: "*"
        - Sid: Allow use of the key
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${orgAccountId}:root
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource: "*"
        - Sid: Allow attachment of persistent resources
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${orgAccountId}:root
          Action:
          - kms:CreateGrant
          - kms:ListGrants
          - kms:RevokeGrant
          Resource: "*"
          Condition:
            Bool:
              kms:GrantIsForAWSResource: 'true'
        - Sid: Enable IAM User Permissions
          Effect: Allow
          Principal:
            AWS:
            - !Sub arn:aws:iam::${orgAccountId}:root
          Action: kms:*
          Resource: "*"
        - Sid: Allow CloudTrail to encrypt logs
          Effect: Allow
          Principal:
            Service: cloudtrail.amazonaws.com
          Action: kms:GenerateDataKey*
          Resource: "*"
          Condition:
            StringLike:
              'kms:EncryptionContext:aws:cloudtrail:arn': !Sub arn:aws:cloudtrail:*:${orgAccountId}:trail/*
        - Sid: Allow CloudTrail to describe key
          Effect: Allow
          Principal:
            Service: cloudtrail.amazonaws.com
          Action: kms:DescribeKey
          Resource: "*"
        - Sid: Allow principals in the account to decrypt log files
          Effect: Allow
          Principal:
            AWS: "*"
          Action:
          - kms:Decrypt
          - kms:ReEncryptFrom
          Resource: "*"
          Condition:
            StringEquals:
              kms:CallerAccount: !Ref orgAccountId
            StringLike:
              'kms:EncryptionContext:aws:cloudtrail:arn': !Sub arn:aws:cloudtrail:*:${orgAccountId}:trail/*
        - Sid: Allow alias creation during setup
          Effect: Allow
          Principal:
            AWS: "*"
          Action: kms:CreateAlias
          Resource: "*"
          Condition:
            StringEquals:
              kms:CallerAccount: !Ref orgAccountId
              kms:ViaService: ec2.us-east-1.amazonaws.com
        - Sid: Enable cross account log decryption
          Effect: Allow
          Principal:
            AWS: "*"
          Action:
          - kms:Decrypt
          - kms:ReEncryptFrom
          Resource: "*"
          Condition:
            StringEquals:
              kms:CallerAccount: !Ref orgAccountId
            StringLike:
              'kms:EncryptionContext:aws:cloudtrail:arn': !Sub arn:aws:cloudtrail:*:${orgAccountId}:trail/*

# Outputs:
#   Outputskmsid:
#     Description: kms-trail-organizations
#     Value: !GetAtt mykms.Arn
#     Export:
#       Name: !Sub ${EnvPrefix}-kms



  S3BucketOrg:
      Type: "AWS::S3::Bucket"
      Properties:
        BucketName: !Ref S3BucketName
        PublicAccessBlockConfiguration:
          BlockPublicAcls: true
          BlockPublicPolicy: true
          IgnorePublicAcls: true
          RestrictPublicBuckets: true

  S3BucketpublicdataBucketPolicy:
      Type: AWS::S3::BucketPolicy
      Properties:
        Bucket: !Ref S3BucketOrg
        PolicyDocument:
          Statement:
            - Sid: AWSCloudTrailAclCheck
              Effect: Allow
              Principal:
                Service: cloudtrail.amazonaws.com
              Action: "s3:GetBucketAcl"
              Resource: !Sub arn:aws:s3:::${S3BucketName}
            - Sid: AWSCloudTrailWrite
              Effect: Allow
              Principal:
                Service: cloudtrail.amazonaws.com
              Action: 's3:PutObject'
              Resource: !Sub arn:aws:s3:::${S3BucketName}/AWSLogs/${orgAccountId}/*
              Condition:
                StringEquals:
                  's3:x-amz-acl': bucket-owner-full-control
            - Sid: AWSCloudTrailWriteMyaccount
              Effect: Allow
              Principal:
                Service: cloudtrail.amazonaws.com
              Action: 's3:PutObject'
              Resource: !Sub arn:aws:s3:::${S3BucketName}/AWSLogs/${AWS::AccountId}/*
              Condition:
                StringEquals:
                  's3:x-amz-acl': bucket-owner-full-control

2.AdminAccount(親アカウント)での作業

次にAdminAccount(organizationsの親アカウント)で、
cloudtrailを作成します。
この時、パラメータに上記で作成したLogArchiveアカウントの
S3の名前とKMSのidを設定します。
AdminAccountで以下のcloudformationを適応します。

AWSTemplateFormatVersion: "2010-09-09"
Description: A templete for taf init

Parameters:
  S3BucketName:
    Type: String
    Default: xxxxxxxxxxxxxxxxxx
    Description: "bucket name for cloudtrail. this is expected to be log Archive's account bucket name"
  kmsid:
    Type: String
    Default: 3e20a5ef-xxxxxxx-xxxx-xxxx
    Description: "kms-id in log Archive Account"
  AccountIdLog:
    Type: String
    Default: xxxxxxxxxx
    Description: "AWS Account Id of logArchive"

Resources :
  mytrail:
    Type: AWS::CloudTrail::Trail
    Properties:
#      CloudWatchLogsLogGroupArn: String
#      CloudWatchLogsRoleArn: String
      EnableLogFileValidation: true
#      EventSelectors:
#        - EventSelector
      IncludeGlobalServiceEvents: true
      IsLogging: true
      IsMultiRegionTrail: true
      KMSKeyId: !Sub arn:aws:kms:us-east-1:${AccountIdLog}:key/${kmsid}
      S3BucketName: !Ref S3BucketName
#      S3KeyPrefix: String
#      SnsTopicName: String
#      Tags:
#        - Resource Tag
      TrailName: !Ref S3BucketName

cloudtrailをorganizations連携する

上記適応後に、AWSコンソールから作成されたcloudtrailを開き、
「Enable for all accounts in my organization」を有効化します。
cloudformationでは、この設定値が設定できなかったので、
ここだけ手動設定になります。

f:id:remmemento:20210501190447p:plain
cloudtrail

上記でベストプラクティス風の構成が実現できました。 f:id:remmemento:20210501180719p:plain

さいごに

AWSでcloudtrailの権限移譲を構成してみました。
冒頭で紹介したブログは、cloudtrail以外にも、
Organiztions関連の連携まとめが記載されており分かりやすいです。
いつも参考にさせてもらっています。
ありがとうございます。
このブログもどなたか参考にして頂けると嬉しいです。 fu3ak1.hatenablog.com