CloudWatch Logsのカスタムデータ保護ポリシーをCDKで実装する
はじめに
以前、以下の記事を書きました。
[小ネタ]CloudWatch Logsの機密データ保護機能でシークレットアクセスキーがマスキングされるパターンを調べてみた | DevelopersIO
上記の記事の通り、CloudWatch Logsには機密データをマスキングして表示する機能があります。今回はこの機能をCDKで実装してみました。
CloudWatch Logs データ保護ポリシーとは
CloudWatch Logsに取り込まれたログのうち、特定のパターンをもつ文字列をマスキングして表示できる機能です。
機密性の高いログデータをマスキングで保護する - Amazon CloudWatch Logs
パターンの指定方法は、以下の2通りあります。
マネージドデータ識別子
AWSが事前に定義したデータ型のパターンです。メールアドレス、IPアドレス、氏名、クレジットカード番号など、一般的な機密データを検出できます。CDKではDataIdentifierクラスの定数として提供されています。
利用可能なデータ識別子の一覧は以下の公式ドキュメントを参照してください。
機密データの種類についての CloudWatch Logs マネージドデータ識別子 - Amazon CloudWatch Logs
カスタムデータ識別子
ユーザーが正規表現を使って独自に定義するパターンです。CDKではCustomDataIdentifierクラスを使って定義します。
マネージドデータ識別子には個人情報に関連する様々なデータが定義されていますが、マイナンバーや日本の電話番号、運転免許証番号など日本固有の形式には対応していません。カスタムデータ識別子を使うことで、このような日本固有のデータや、プロジェクト独自の機密データ(社員IDやプロジェクトコードなど)にも対応できます。
注意点
この機能は取り込まれたログに含まれる機密データをマスキングしてくれますが、logs:Unmask権限を持つユーザは元のデータを参照できます。マスキングはあくまで表示上の保護であり、CloudWatch Logs上に機密データが取り込まれること自体を制限するわけではありません。
また、データ保護ポリシーはロググループに設定した以降に取り込まれたログにのみ適用されます。ポリシー設定前にすでに取り込まれたログはマスキングされません。
CDKで実装
コードは以下のようになります。
import { CustomDataIdentifier, DataIdentifier, DataProtectionPolicy, LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
export class LogStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const dataProtectionPolicy = new DataProtectionPolicy({
name: 'dataProtectionPolicy',
identifiers: [
DataIdentifier.EMAILADDRESS,
DataIdentifier.IPADDRESS,
DataIdentifier.NAME,
new CustomDataIdentifier('EmployeeId', 'EmployeeId-\\d{9}'),
new CustomDataIdentifier('CustomCreditCardNumber', '\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\\b'),
new CustomDataIdentifier('JapanesePhoneNumber', '\\b0\\d{1,4}[-\\s]?\\d{1,4}[-\\s]?\\d{4}\\b'),
new CustomDataIdentifier('JapaneseMyNumber', '\\b[0-9]{4}[-\\s]?[0-9]{4}[-\\s]?[0-9]{4}\\b'),
new CustomDataIdentifier('JapanesePostalCode', '\\b[0-9]{3}[-]?[0-9]{4}\\b'),
new CustomDataIdentifier('JwtToken', '\\beyJ[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+\\b'),
new CustomDataIdentifier('JapaneseDriversLicense', '\\b[0-9]{12}\\b'),
new CustomDataIdentifier('InternalProjectCode', '\\bPRJ-[A-Z]{3}-\\d{4}\\b'),
new CustomDataIdentifier('ApiToken', '\\btoken_[A-Za-z0-9]{32}\\b'),
new CustomDataIdentifier('SecretKeyPattern', '\\bsecret_[A-Za-z0-9_\\-]{16,}\\b'),
],
});
new LogGroup(this, 'LogGroup', {
logGroupName: "sampleLogGroup",
retention: RetentionDays.ONE_WEEK,
dataProtectionPolicy,
});
}
}
まず、DataProtectionPolicyを作成しデータの識別子を指定します。ここではマネージドデータ識別子を3件、カスタムデータ識別子を上限の10件指定しています。
続いて、作成したポリシーをロググループのdataProtectionPolicyに指定します。
動作確認
マネジメントコンソールでsampleLogGroupを開き、「アクション」⇒「データ保護ポリシーを編集」をクリックします。

CDKで指定した識別子の一覧が表示されていることが確認できます。

次に、ログストリームを作成してログイベントを書き込みます。

正規表現にマッチする文字列を含むログイベントを書き込みます。(検証用のダミーデータです)

カスタムデータ識別子のうち、SecretKeyPatternにマッチしているため、「入力されたシークレット:」の部分以外がマスキングされました。

まとめ
この記事ではCloudWatch Logsのデータ保護ポリシーをCDKで実装してみました。
注意点にも記載しましたが、あくまで表示上マスキングされるだけであり、権限をもったユーザであれば復元できます。そのため、この権限の付与を最小限に抑えることが重要です。
この記事がどなたかの参考になれば幸いです。







