1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudwatchでマスキングしたログをS3に出力したらマスキングはどうなる?

Last updated at Posted at 2024-06-15

AWSでCloudWatchを活用していると、「ログの保持コストをできるだけ抑えたい」という要望はよくあります。
その対策として、よりコスト効率の良いS3にログを出力し、CloudWatch側のログは削除するという方法が1つの選択肢となります※1。

また、CloudWatchではログ内に個人情報が含まれないよう、マスキング機能※2を利用することがあります。
しかし、コスト削減のためにS3へログを出力する際に、マスキングが外れてしまっては本末転倒です。
そのため、S3出力時にマスキングの状態がどうなるのか、しっかり確認していきましょう。

※1:Cloudwatchの保存料金はUSD 0.033/GB。S3(標準)の保存料金は0.025USD/GB。 (2024/6時点の東京リージョンでの料金)
※2:https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/mask-sensitive-log-data.html

検証準備

Cloudwatchでマスキング設定

マスキング対象のログ

今回マスキング対象のログとして下記のサンプルを使用します。
本サンプルでマスキング対象となるのは、「password」と「mailAddress」になります。

{
    "id": "199",
    "password": "cX3T*FMZ",
    "mailAddress": "hogehoge@gmail.com",
    "createdAt": "2020-02-20T11:00:28.107Z"
}

マスキングの設定

マスキングの設定は「Cloudwatch->設定->ログ」から行い
「メールアドレス」、「パスワード」のマスキングはそれぞれ下記の機能を利用します。

  • メールアドレスのマスキング:データ保護アカウントポリシー
  • パスワードのマスキング  :カスタムデータ識別子

Cloudwatch設定_ログ画面.png

メールアドレスのマスキング

メールアドレスは、[1]にあるようにAWS側でマスキングの機能が用意されているためこちらの機能を使用します。

[1]保護できるデータの種類
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/protect-sensitive-log-data-types.html

【設定手順】

①:データ保護アカウントポリシー設定を選択

データ保護アカウントポリシー選択.png

②:アカウントポリシーからマスキングの対象としてEmailAddressを選択し、データ保護をアクティブ化する

アカウントポリシー_データ識別子選択.png

パスワードのマスキング

メールアドレスとは違い、パスワードはマスキングできる対象に含まれていないようなので
カスタムデータ識別子を利用して、マスキングを行います。

【設定手順】

①:データ保護アカウントポリシーの設定を選択 (メールアドレスのマスキングの手順①と同様)

②:アカウントポリシーからカスタムデータ識別子を選択し、パスワードを検出する正規表現を設定し、データ保護をアクティブ化する

今回使用した正規表現("password"[^,]*")は
「"password"から始まり、シングルクオーテーションまで」をマスキング対象としています。

アカウントポリシー_カスタムデータ識別子の設定.png

マスキングの結果を確認

マスキング対象のログ を実際にCloudWatchに出力した結果が下記になります。
→ 「メールアドレス」と「パスワード」がマスキングされています。

CloudWatchマスキング結果.png

CloudwatchのログをS3に出力する

Lambda作成

CloudwatchからS3へ出力するLambdaを作成します。(Node.js 20.x)
CloudwatchからS3への出力は、「CreateExportTaskCommand」を使用します。

import { CloudWatchLogsClient, CreateExportTaskCommand, DescribeExportTasksCommand } from "@aws-sdk/client-cloudwatch-logs";

const client = new CloudWatchLogsClient({ region: "your-region" });

export const handler = async (event) => {
    // パラメータの設定
    const logGroupName = 'your-log-group-name';
    const bucketName = 'your-bucket-name';

    // 現在の日時を取得
    const now = new Date();
    const year = now.getUTCFullYear();
    const month = String(now.getUTCMonth() + 1).padStart(2, '0');
    const day = String(now.getUTCDate()).padStart(2, '0');

    // プレフィックスの設定 (ロググループ名/yyyy/mm/dd)
    const destinationPrefix = `${logGroupName}/${year}/${month}/${day}`;

    // エクスポートタスクの時間範囲を設定
    const startTime = now.setHours(now.getHours() - 1);  // 1時間前
    const endTime = Date.now();  // 現在

    // エクスポートタスクの作成
    try {
        const createCommand  = new CreateExportTaskCommand({
            taskName: 'ExportLogsToS3',
            logGroupName: logGroupName,
            from: startTime,
            to: endTime,
            destination: bucketName,
            destinationPrefix: destinationPrefix
        });

        const createResponse = await client.send(createCommand);
        const taskId = createResponse.taskId;

        console.log(`Export task created with ID: ${taskId}`);

        // エクスポートタスクのステータスを確認
        const describeCommand = new DescribeExportTasksCommand({
            taskId: taskId
        });

        let taskStatus = 'PENDING';
        let taskMessage = '';

        while (taskStatus === 'PENDING' || taskStatus === 'RUNNING') {
            const describeResponse = await client.send(describeCommand);
            const exportTasks = describeResponse.exportTasks;

            if (exportTasks && exportTasks.length > 0) {
                const task = exportTasks[0];
                taskStatus = task.status.code;
                taskMessage = task.status.message || '';
                console.log(`Task Status: ${taskStatus}, Message: ${taskMessage}`);
            }

            if (taskStatus === 'PENDING' || taskStatus === 'RUNNING') {
                await new Promise(resolve => setTimeout(resolve, 10000));  // 10秒待機
            }
        }

        if (taskStatus === 'COMPLETED') {
            console.log('Export task completed successfully.');
        } else {
            console.log(`Export task failed with status: ${taskStatus}, message: ${taskMessage}`);
        }

        return {
            statusCode: 200,
            body: `Export task completed with status: ${taskStatus}, message: ${taskMessage}`
        };
    } catch (error) {
        console.error(`Error creating or describing export task: ${error}`);

        return {
            statusCode: 500,
            body: `Error creating or describing export task: ${error}`
        };
    }
};

Lambdaが使用するロールに適切な権限を付与

S3へ出力する権限をLambdaに持たせるため、下記の内容をLambdaにアタッチするIAMロールに含めます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateExportTask",
                "logs:DescribeExportTasks",
                "logs:CancelExportTask"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::your-bucket-name/*"
        }
    ]
}

S3バケットポリシーの修正

Cloudwatchからのログ出力を受け止められるようにS3のバケットポリシーに下記を設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::your-bucket-name",
            "Condition": {
                "StringLike": {
                    "aws:SourceArn": "your-log-group-arn"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::from-cloudwatch-to-s3-yy/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                },
                "StringLike": {
                    "aws:SourceArn": "your-log-group-arn"
                }
            }
        }
    ]
}

検証結果

マスキング対象のログ をCloudwatchに出力後、Lambda作成 で用意したLambdaを実行し、S3に出力されたログファイルを確認すると、マスキングされたまま出力されました。

本記事の内容を実施することで、ログ保持コストを25%削減することができ、コスト負担を軽減しながらセキュリティレベルを維持することが可能ですので、ぜひやってみてください!

◆Cloudwatchのログ
image.png

◆S3に出力されたログ

2024-06-15T04:39:21.385Z test
2024-06-15T04:35:41.269Z {     "id": "199",     **********************,     "mailAddress": "******************",     "createdAt": "2020-02-20T11:00:28.107Z" }
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?