LoginSignup
1
0
はじめての記事投稿
Qiita Engineer Festa20242024年7月17日まで開催中!

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

Posted at

背景

あるプロジェクトでお客様から下記2つの要件がありました。

  • セキュリティ要件として、ログの出力時にパスワードといった個人情報を出力してはダメ
  • 監査目的で7年間ログを保持する必要があるが、出来るだけログ保持のランニングコストを安くしていきたい

解決策

背景で述べた課題について下記のような解決策で対応。

No 課題 解決策
1 ログの出力時、個人情報の出力を抑制 Cloudwatchにマスキングの設定を行う ※1
2 出来るだけログの保持のランニングコストを安くする CloudWatchのログを毎日S3へ出力し、Cloudwatchでは3か月間のみ保持する ※2

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

疑問

お客様から出されたそれぞれの要件に対して
解決策に記載した通りの内容で対処を進めていましたが
No1にてCloudwatch上でマスキングしていたものを、No2でS3へ出力した際
マスキングしていたものはどうなるのか気になったので検証してみました。

検証準備

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に出力されたログファイルを確認すると、マスキングされたまま出力されました。

◆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" }

注意事項

Cloudwatchにログを作成してから、すぐに「CreateExportTaskCommand」を実行してもログが出力されないことがありました。
→ ログの出力対象と認識されるまでに多少のラグがある・・・?

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