CloudWatch LogsのログをKinesis Data Firehose経由でS3バケットに転送する

記事タイトルとURLをコピーする

最近ランニングを始めたCI部1課の山﨑です。

今回はCloudWatch LogsのログをKinesis Data Firehose経由でS3バケットに転送してみました。

はじめに

最近はAWSアカウントを単一アカウントで利用するのではなく、マルチアカウントで利用することが増えてきたように感じています。そこで今回はアカウントA からアカウントB に対してログのバックアップを出力し、ログ自体はアカウントB で長期保存するというログ運用を想定し、アカウントA で出力したCloudWatch LogsのログをKinesis Data Firehose 経由でアカウントB のS3バケットに転送する方法をご紹介します。

基本的には以下のリンクに従って実装すれば上手くいくのですが、イマイチ何をしているのかをイメージしずらいため今回は図を利用して実装イメージを可視化させながらご紹介します。

aws.amazon.com

f:id:swx-yamasaki:20211004205929p:plain
実装イメージ

ポリシー設計について

以降の実装手順の中には IAMロールの設計、リソースベースのポリシー設計が含まれています。それぞれのポリシー設計は以下のブログに記載の設計ポイントを沿って考えておりますのでこちらのページを別タブで開いた上で本ブログをお読み頂くとよりイメージしやすいと思います。

blog.serverworks.co.jp

以下、重要なポイントのみブログより一部抜粋します。

5Wで要件を整理する

以下の5Wで要件を整理することで定義すべきポリシーが可視化されます。

  • Who(何が)
    • ポリシーの適用主体
  • Where(何に対して)
    • Resource要素
  • When(どんな条件下で)
    • Condition要素
  • What(どんな操作を)
    • Action要素
  • Want(どうしたい)
    • Effect要素

ポリシー要件の早見表

ポリシーの関連付け対象 Who(何が) Where(何に対して) When(どんな条件下で) What(どんな操作を) Want(どうしたい)
Organizations SCP ・メンバーアカウント
・OU内の全アカウント
要定義 要定義 要定義 要定義(Allow or Deny)
リソースベースのポリシー 要定義 ポリシーを適用したAWSリソース
※AWSリソースを絞ることは可
要定義 要定義 要定義(Allow or Deny)
IAMユーザー IAMユーザー 要定義 要定義 要定義 要定義(Allow or Deny)
IAMロール 要定義 要定義 要定義 要定義 要定義(Allow or Deny)

アカウントB の実装

Step1. S3バケットの作成

f:id:swx-yamasaki:20211004210451p:plain
Step1

まずはアカウントA から転送されてくるログを保管するS3バケットを任意の名前で作成してください。実際にS3バケットに書き込むのは Kinesis Data Firehose のストリームであるため、バケットポリシーでKinesis Data Firehose からの書き込みを許可します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowWritingFromKinesisDataFirehose",
            "Effect": "Allow",
            "Principal": {
                "Service": "firehose.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

Step2. Kinesis Data Firehose に関連付けるIAMロールの作成

f:id:swx-yamasaki:20211004210547p:plain
Step2

Kinesis Data Firehose に関連付けるIAMロールを作成します。Kinesis Data Firehose は S3 にログを書き込む必要があるためIAMロールにはS3バケットへの書き込みを許可するバケットポリシーを割り当てます。

# IAMロール作成
aws iam create-role \ 
    --role-name FirehosRoleToS3 \ 
    --assume-role-policy-document file://~/TrustPolicyForFirehose.json

# IAMポリシー作成
aws iam create-policy \
    --policy-name Permissions-Policy-For-Firehose \
    --policy-document file://~/PermissionsForFirehose.json

# IAMポリシーを割り当て
aws iam attach-role-policy \
    --policy-arn IAMポリシーのARN \
    --role-name FirehosRoleToS3

TrustPolicyForFirehose.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "firehose.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "アカウントBのAccount ID"
        }
      }
    }
  ]
}

PermissionsForFirehose.json

{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:GetBucketLocation",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads",
                "s3:PutObjectAcl",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

Step3. Kinesis Data Firehose の配信ストリームの作成

f:id:swx-yamasaki:20211004211319p:plain
Step3

Kinesis Data Firehose がログデータを受け取ってS3へと配信する配信ストリームを作成します。RoleARNはStep2. で作成したIAMロールのARNを、BucketARNにはStep1. で作成したS3バケットのARNを、--s3-destination-configuration のプロパティは必要に応じて追加してください。

aws firehose create-delivery-stream \
   --delivery-stream-name 'delivery-test-stream' \
   --s3-destination-configuration \
  '{"RoleARN": "arn:aws:iam::アカウントBのAccount ID:role/FirehosRoleToS3", "BucketARN": "arn:aws:s3:::バケット名", "Prefix": "プレフィックス"}'

docs.aws.amazon.com

Step4. CloudWatch Logs destination の作成

f:id:swx-yamasaki:20211004212414p:plain
Step4

アカウントA で出力したCloudWatch LogsのログをKinesis Data Firehose 経由でアカウントB のS3バケットに転送する場合、CloudWatch Logs から Kinesis Data Firehose に直接データが転送されるわけではありません。Kinesis Data Firehose の前段でCloudWatch Logs destination と呼ばれる転送先に先にログデータが集約され、この転送先からKinesis Data Firehose にログデータが転送されます。つまり、以下の経路でアカウントB のS3までログデータは転送されます。

CloudWatch Logs(アカウントA) → CloudWatch Logs destination(アカウントB) → Kinesis Data Firehose(アカウントB) → S3バケット(アカウントB)

上記の経路よりCloudWatch Logs destination は Kinesis Data Firehose にログデータを転送するので、Kinesis Data Firehose に対するアクセス許可を付与する必要があり、これはIAMロールを関連付けることで実現することができます。

まずはIAMロールを作成します。

# IAMロール作成
aws iam create-role \
    --role-name CWLtoKinesisFirehoseRole \
    --assume-role-policy-document file://~/TrustPolicyForCWL.json

# IAMポリシー作成
aws iam create-policy \
    --policy-name CWL-Policy-For-Kinesis \
    --policy-document file://~/PermissionsForCWL.json

# IAMポリシーを割り当て
aws iam attach-role-policy \
    --policy-arn IAMポリシーのARN \
    --role-name CWLtoKinesisFirehoseRole

TrustPolicyForCWL.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "logs.ap-northeast-1.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

PermissionsForCWL.json

{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "firehose:*"
            ],
            "Resource": [
                "arn:aws:firehose:ap-northeast-1:アカウントBのAccount ID:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::アカウントBのAccount ID:role/CWLtoKinesisFirehoseRole"
            ]
        }
    ]
}

IAMロールが作成できたらCloudWatch Logs destination を作成します。

aws logs put-destination \
    --destination-name "delivery-test-destination" \
    --target-arn "arn:aws:firehose:ap-northeast-1:アカウントBのAccount ID:deliverystream/delivery-test-stream" \
    --role-arn "arn:aws:iam::アカウントBのAccount ID:role/CWLtoKinesisFirehoseRole"

Step5. CloudWatch Logs destination にリソースベースのポリシーを割り当てる

f:id:swx-yamasaki:20211004214751p:plain
Step5

本StepではアカウントA がアカウントB のCloudWatch Logs destination にサブスクリプションフィルター経由でログデータを転送する権限をリソースベースのポリシーで割り当てます。

aws logs put-destination-policy \
    --destination-name "delivery-test-destination" \
    --access-policy file://~/AccessPolicy.json

AccessPolicy.json

{
  "Version" : "2012-10-17",
  "Statement" : [
    {
      "Sid" : "",
      "Effect" : "Allow",
      "Principal" : {
        "AWS" : "アカウントA のAccount ID"
      },
      "Action" : "logs:PutSubscriptionFilter",
      "Resource" : "arn:aws:logs:ap-northeast-1:アカウントBのAccount ID:destination:delivery-test-destination"
    }
  ]
}

アカウントA の実装

Step6. サブスクリプションフィルターの作成

f:id:swx-yamasaki:20211004215216p:plain
Step6

アカウントA で転送したいCloudWatch Logs のロググループに対してサブスクリプションフィルターを作成します。

--filter-pattern プロパティで転送するログデータをフィルタリングすることが可能ですが、今回はフィルタリングなしで設定します。

aws logs put-subscription-filter \
    --log-group-name "delivery-test" \
    --filter-name "delivery-test-filter" \
    --filter-pattern "" \
    --destination-arn "arn:aws:logs:ap-northeast-1:アカウントBのAccount ID:destination:delivery-test-destination"

docs.aws.amazon.com

ログ配信の確認

f:id:swx-yamasaki:20211004215935p:plain
CloudWatch Logs のロググループでテストメッセージを流す

f:id:swx-yamasaki:20211004220004p:plain

S3バケットを確かめてみると無事にログが配信されていました。

f:id:swx-yamasaki:20211004220515p:plain
S3 Select を利用してログの内容を確認

まとめ

今回はCloudWatch LogsのログをKinesis Data Firehose経由でS3バケットに転送してみました。マルチアカウントになるとアイデンティティベースのポリシーやリソースベースのポリシーを用いて、アカウントをまたぐアクセス制御を行う必要があるため理解が難しくなります。面倒ではありますが、実装イメージを図で表現して整理すると結果的に短時間で理解が進みますので是非お試しください。

また、AWSで取り扱われるポリシーについては以下のブログで整理しておりますのでお手すきの際にご覧ください。

blog.serverworks.co.jp

山﨑 翔平 (Shohei Yamasaki) 記事一覧はコチラ

カスタマーサクセス部所属。2019年12月にインフラ未経験で入社し、AWSエンジニアとしてのキャリアを始める。2023 Japan AWS Ambassadors/2023-2024 Japan AWS Top Engineers