この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS事業本部コンサルティング部の後藤です。
本日8月4日より弊社メンバーズのプレミアムサービスをご利用頂く事でWafCharmのエントリープランが無償で利用可能になりました!そこで本記事では、WafCharmでメール通知、レポート機能を試してみました。
AWS WAFの導入・運用をAIを使って効率化する「WafCharm」のエントリープランをクラスメソッドメンバーズ プレミアムサービス契約者に無償提供開始
WafCharmとは
WafCharmは世界中のWebに対する攻撃パターンをAIによって学習し、環境に応じたアクセスのパターン等を判別、AWS WAFのルールを最適化してくれるサービスとなります。これらの事から、WafCharmを利用する事でAWS WAFのルール設定や運用の工数削減が見込めます。
本記事ではAWS WAF v2 (以下、AWS WAF)での利用を想定しております。AWS WAF Classicで利用する際には本記事通りの設定ではない可能性がありますのでご注意ください。 WafCharm利用におけるAWS WAF ClassicとAWS WAFの変更点については以下をご参照頂ければと存じます。
WafCharm が new AWS WAF に対応しました
また、本記事は公開されている作業手順を元にした内容となりますので詳細を知りたい方は以下をご参照ください。
レポート機能/通知機能 利用マニュアル new AWS WAF ( S3 )
それでは、やっていきましょう!
前提条件
本作業を実施するには以下を準備頂く必要があります。
- AWSアカウント
- WafCharmで設定されたAWS WAFのWeb ACLが作成済み
- ALB、CloudFront等AWS WAFのWeb ACLを紐づけるリソースが作成済み
- IAM、Lambda、CloudWatchの作成、設定変更が可能な権限
- WafCharmアカウント
- Web ACL Configが設定済み
- Web Site Configが設定済み
WafCharmやWeb ACLの設定に関しては以下の記事でご紹介させて頂いております。
構成図
本記事の構成図は以下のようになります。
目次
Step.1 AWS WAFログをS3出力設定 (作業時間目安 : 10分)
Step.2 Lambda用IAMポリシー、IAMロール作成 (作業時間目安 : 10分)
Step.3 WafCharm連携用Lambda作成 (作業時間目安 : 15分)
Step.4 WafCharm レポート閲覧方法
Step.5 WafCharm メール通知設定 (作業時間目安 : 5分)
Step.1 AWS WAFログをS3出力設定
WafCharmのレポートはAWS WAFのログを収集し、集計データを元に作成しています。WafCharmにAWS WAFのログを転送するために、まずはAWS WAFのログを自身のAWSアカウント上のS3バケットに出力する設定を行います。
Step.1-1 web ACL Logging設定
AWS WAFのコンソール画面からWafCharmのルールが適用されたWeb ACLを選択、上部項目の「Logging and metrics」を選択してLogging項目の「Enable」を実行します。
既にCloudWatchLogs等にログを出力している場合はEnableではなくEditを選択してください。
Step.1-2 Web ACL ログ出力用S3バケット作成
Logging destination項目で「S3 Bucket」を選択、「Create new」 を実行してS3バケットを作成します。
S3バケット作成画面に遷移したら、バケット名とAWSリージョンを指定してS3バケットを作成します。ここで注意頂きたいのがバケット名です。AWS WAFではログ出力先にS3を指定した場合、バケット名のPrefixに「aws-waf-logs-」を付ける必要があります。
Step.1-3 ログ出力先にS3を指定
AWS WAFのログ出力設定画面に戻ったら、Step1-2で作成したS3バケットを選択します。
それ以外の項目は設定不要です。画面下の「Save」を実行して設定を保存して完了となります。
Step.1-4 ログ出力確認
意図的にWeb ACLを通過する通信を行い、Web ACLで検知したログがS3バケットに出力されるか確認を行います。ログ出力間隔は5分となっておりますので多少待ちますが、以下のようなログファイルが出力されていれば設定完了です。
S3コンソール画面上部に記載されているPATHですが、 Step.2で行うIAMロールの設定で一部使用するため「S3 URIをコピー」で取得し、メモしておいてください。
Step.2 Lambda用IAMポリシー、IAMロール作成
Step.2ではS3に出力したAWS WAFログをLambdaが扱えるようにするため、IAMポリシーとIAMロールを作成します。
Step.2-1 ログ読み取りIAMポリシー作成
まずは自身のS3バケットに出力したAWS WAFログを読み取るためのIAMポリシーを作成します。
IAMコンソール画面から左項目の「ポリシー」を選択、右上の「ポリシーを作成」を選択します。
ポリシー作成画面にて以下JSONを設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WafLogGetObject",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::/[BucketName]/AWSLogs/[AccountID]/WAFLogs/[Region]/[Web ACL Name]/*"
}
]
}
Resourceで指定するARN値の [] 内の項目は環境毎に異なりますので、自身の環境に合うように置き換えてください。
[BucketName] : Step.1で作成したS3バケット名に置き換えてください。
[AccountID] : AWSアカウントIDに置き換えてください。
[Region] : Web ACLが作成されているリージョン名に置き換えてください。
[Web ACL Name] : Web ACLの名前に置き換えてください。
上記設定後、幾つか次のステップに進むと「ポリシーの確認」画面に遷移しますので任意のポリシー名を設定頂き、「ポリシーの作成」を実行頂く事でログ読み取り用のIAMポリシー作成が完了となります。
Step.2-2 ログ転送IAMポリシー作成
次にLambdaがWafCharmのS3にログを転送するためのIAMポリシーを作成します。
Step.2-1同様、IAMポリシー作成画面に移ったらポリシー作成画面にて以下JSONを設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WafLogPutObject",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::wafcharm.com/*"
}
]
}
上記設定後、幾つか次のステップに進むと「ポリシーの確認」画面に遷移しますので任意のポリシー名を設定頂き、「ポリシーの作成」を実行頂く事でログ転送用のIAMポリシー作成が完了となります。
Step.2-3 IAMロール作成
IAMコンソール画面より左項目「ロール」を選択頂き、「ロールの作成」を実行頂きます。
信頼されたエンティティを選択画面から、以下項目を選択して「次へ」を選択してください。
- 信頼されたエンティティタイプ : 信頼されたエンティティタイプ
- ユースケース : Lambda
許可を追加する画面ではIAMロールに紐づけるIAMポリシーを選択します。以下IAMポリシーを選択して「次へ」を選択してください。
- AWSLambdaExecute
- Step.2-1で作成したIAMポリシー
- Step.2-2で作成したIAMポリシー
作成するIAMロールの詳細が表示されたら、任意のIAMロール名を設定頂き、設定した内容に問題がなければ「IAMロールを作成」を実行して完了となります。
Step.3 WafCharm連携用Lambda作成
自身のAWSアカウントにあるS3からAWS WAFログを読み取り、WafCharmのS3に転送するLambdaを作成していきます。
Step.3-1 Lambda関数の作成
Lambdaのコンソール画面より「関数の作成」を実行してください。
関数の作成画面に遷移したら、以下のように設定致します。
- 関数名
- 任意の関数名を入力します。
- ランタイム
- Node.js 12.x 以上を選択します。
- アーキテクチャ
- x86_64 を選択します。
- デフォルトの実行ロールの変更
- 「既存のロールを使用する」から Step.2-3で作成したIAMロールを指定します。
上記設定後、「関数の作成」を実行します。
Step.3-2 Lambda関数のコード記入
Lambda関数の作成が出来たら、以下コードをindex.jsにコピペします。コード元はこちらになります。
'use strict';
const toBucket = process.env.WAFCHARM_BUCKET || 'wafcharm.com';
const wafVersion = process.env.WAF_VERSION || 'v2';
const acl = 'bucket-owner-full-control';
const AWS = require('aws-sdk');
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
});
function destinationPath(version) {
const v = version || wafVersion;
return `waflog/acceptance/${v}`;
}
function send(toParams, retryNum) {
if (retryNum === 0) {
return;
}
s3.putObject(toParams, (err, data) => {
if (err) {
console.error('Cannot put object.');
console.error(err);
send(toParams, retryNum - 1);
} else {
return;
}
});
}
function getDestKeyWhenMatched(str, ptn, callback) {
let match = str.match(ptn);
if (!match) {
return false;
}
return callback(match);
}
exports.handler = (event) => {
const s3BucketName = event.Records[0].s3.bucket.name;
const s3ObjectKey = decodeURIComponent(event.Records[0].s3.object.key);
// false or string
let destObjectKey = false;
if (!destObjectKey) {
// via KDF
const toPath = destinationPath();
destObjectKey = getDestKeyWhenMatched(s3ObjectKey, /\d{4}\/\d{2}\/\d{2}\/\d{2}\/[^\/]*$/, (match) => {
return [toPath, match[0]].join('/');
});
}
if (!destObjectKey) {
// via S3 direct (v2 only)
const toPath = destinationPath('v2');
destObjectKey = getDestKeyWhenMatched(s3ObjectKey, /(\d{4}\/\d{2}\/\d{2}\/\d{2})\/\d{2}\/([^\/]*$)/, (match) => {
return [toPath, match[1], match[2]].join('/');
});
}
if (!destObjectKey) {
// via KDF (hive format)
const toPath = destinationPath();
destObjectKey = getDestKeyWhenMatched(s3ObjectKey, /year=(\d{4})\/month=(\d{2})\/day=(\d{2})\/hour=(\d{2}\/[^\/]*$)/, (match) => {
return [toPath, match[1], match[2], match[3], match[4]].join('/');
});
}
if (!destObjectKey) {
console.error('Not match the AWS WAF full log event. : ', s3ObjectKey);
return;
}
const fromParams = {
Bucket: s3BucketName,
Key: s3ObjectKey
};
s3.getObject(fromParams, (err, data) => {
if (err) {
console.error('Cannot get object.');
console.error(err);
return;
}
const toParams = {
Bucket: toBucket,
Key: destObjectKey,
ACL: acl,
Body: data.Body
};
send(toParams, 3);
});
};
コードをコピペし終えたら、「Deploy」を実行してLambda関数を更新します。
Step.3-3 Lambda関数のトリガー設定
関数の概要にある「トリガーを追加」からトリガーの設定を行います。
トリガー設定では、以下のように設定をします。
- トリガー
- S3を選択します。
- Bucket
- Step.1-2 で作成したS3バケットを選択します。
- Event Type
- 「All Object create events」を選択します。
- Prefix
- 「AWSLogs/[AccountID]/WAFLogs/[Region]/[Web ACL Name]/」と入力します。
Prefixの値 [] 内の項目は環境毎に異なりますので、自身の環境に合うように置き換えてください。
[AccountID] : AWSアカウントIDに置き換えてください。
[Region] : Web ACLが作成されているリージョン名に置き換えてください。
[Web ACL Name] : Web ACLの名前に置き換えてください。
上記設定後、「Recursive invocation」の項目にチェックを入れ、「追加」でトリガー設定は完了となります。
Step.3-4 Lambda関数のタイムアウト時間変更
次にLambda関数のタイムアウト時間をデフォルトの3秒から1分に変更していきます。
「設定」の「一般設定」から「編集」を実行します。
基本設定の項目「タイムアウト」を「3秒」から「1分」に変更後、保存で完了となります。
以上でLambda関数の作成、及び設定が全て完了となります。
実際にAWS WAFを通過するアクセスを発生させ、S3に検知ログ出力でLambdaが実行する事、エラーログが無い事を確認してみてください。
Step.4 WafCharm レポート閲覧方法
本記事の事前準備、及びStep.1 ~ Step.3 までの設定が正しく行われていればWafCharmのレポート機能が利用頂けます。こちらではWafCharmのレポート閲覧方法を記載させて頂きます。
注意事項として、WafCharmのレポートは前月の検知ログを元に作成、次の月初にレポートが閲覧可能となりますので、本記事の設定を行ってすぐにレポートを閲覧する事が出来ません。
WafCharmでレポートの閲覧が可能になるとメールで通知が届きます。その後WafCharmの管理画面右上のメニューより、「Report」を選択頂く事でレポートを閲覧する事が可能です。
Step.5 WafCharm メール通知設定
最後にAWS WAFの検知(Block or Count)に対してメール通知を行う設定となります。メール通知設定もレポート同様、本記事の事前準備、及びStep.1 ~ Step.3までの設定が完了している必要があります。
Step.5-1 通知先のメールアドレス登録
WafCharm管理画面の左上メニューより「Web ACL Configs」を選択、メール通知が必要なWeb ACL Configを選択したら「Notification」を実行します。
Notification emailの「Edit」を実行します。
Emailの送信先設定画面に遷移しますので、検知時に通知を受け取りたいメールアドレスを最大10件登録し、「Update」で更新します。
Notification email に先程登録したメールアドレス一覧が確認出来たら、通知先のメールアドレス登録は完了となります。
Step.5-2 メール通知有効化
通知先となるメールアドレスの登録が完了したら、次はメール通知の有効化を行うため、Notification の「Edit」を実行します。
「WafCharm Email Notification」を ON に設定して Save します。
以上でメール通知の有効化作業は完了となります。実際にアクセスをBlock、もしくはCountで検知してメールが通知されるか確認してみてください。
メール通知の補足として、通知内容には検知したアクセスログが記載されるのですが最大10件までとなっており、かつメール通知の間隔は5分となっているようです。そのため通知内容で全ての検知を把握する事は難しいかと思います。
最後に
以上でWafCharmのメール通知、レポート作成設定は完了となります。
以前はWafCharmのレポート機能/通知機能を設定するのにAWS WAFからKinesis Firehoseを経由してS3にログを出力する必要がありましたが、2022年5月のアップデートでAWS WAFから直接S3に出力する構成にも対応しました。
WafCharm AWS版でレポート機能/通知機能を Kinesis Data Firehose 経由から S3 直接出力へ変更する場合
このアップデートによりKinesis Firehoseを利用しないためKinesis Firehoseの費用分安くご利用頂ける機能となりました。しかし、以前は通知間隔をKinesis FirehoseのBuffer intervals , Buffer sizeで設定していましたが、S3にログを出力する間隔「5分」となった事で1メールあたりに記載される検知内容が最大の10件を超える可能性が高くなったため、頻繁にメール通知を受け取りたい方はKinesis FirehoseでログをS3に出力しても良いかもしれません。
また、AWS WAFのログやLambdaの実行ログはそのままの状態では容量が増え続け、保存費のコストが余分に取られてしまいます。特に保持する必要性が無ければ一定期間で削除するようなライフサイクル設定を入れてください。
まとめ
本記事ではWafCharmのメール通知、及びレポート作成に必要な設定を実際に試してみました。WafCharmでは追加費用無しでレポート作成を行ってくれますので、気になる方は是非この機会にWafCharmを試してみてください。