はじめに
こんにちは、MSPの田所です。
我々の住む情報社会では、もっと情報を絞りたい、もっと情報を見やすくしたい、というのは普遍的な悩みかと思います。
そんな時は、不要な情報を遮断し、必要な情報を最適化したいものです。
今回は Trend Micro Cloud One – Workload Security (C1WS) の情報をフィルター、加工した話です。
Amazon SNS と AWS Lambda を使用するよくあるユースケースになるかと思います。
どうぞお付き合いください。
C1WS メールインテグレーションの悩み
C1WS には、サーバーに対する攻撃や変更のイベントを検知し、メール通知する機能があります。
その際に、イベントの種類を限定してアラートを作成することはできるのですが、その条件を細かくフィルターすることはできません。
侵入防御や変更監視などの指定はできますが、ルール番号や重要度などによるフィルターはできません。
そのため以下のような悩みが生じます。
1. 情報過多
全てのアラートが上がってくるため、見る必要がないと分かっているものも含まれてしまいます。
10件のアラートが上がっているのに、本当に見るべきは1件、のような状況が起こりえます。
その1件だけにフィルターしたいですね。
2. 情報不足
通知されたアラートメールには基本的な情報が記載されますが、載っていない情報は C1WS コンソールに入って確認する必要があります。
イベントに対して設定した処理内容やインスタンスのOS情報など、その情報さえあれば対応判断ができるのに、、!
という状況が起こりえます。
必要な情報をカスタマイズして記載したいですね。
解決のステップ
フィルターと加工を実装して悩みを解決します。
1. フィルター
C1WS にはメールインテグレーションの他に、Amazon SNS に通知することができます。
その時に JSON 形式のフィルターを設定することができます。
そこではイベントの種類をはじめ、ルール番号や重要度など細かい条件のフィルターを作成可能です。
今回は重要度が「重大」に設定された侵入防御イベントのみを検知するよう設定します。
Amazon SNS と PagerDuty を連携する
PagerDuty サービスの Integration Email を控えます。
SNS トピックを作成し、プロトコル E メールで控えたメールエンドポイントを入力してサブスクリプションを作成します。
PagerDuty 側で “Subscription Confirmation” を検知するのでサブスクリプションの確認を行います。
Confirm subscription のアドレスをコピーして、AWS コンソール側で確認することをおすすめします。
C1WS と Amazon SNS を連携する
AWS の IAM からユーザー作成を行い、sns:Publish の権限を付与します。
アクセスキーを作成してダウンロードします。
ちなみに IAM ロールでの権限付与はできないようです。
C1WS にログイン > 管理 > システム設定 > イベントの転送
から「Amazon Simple Notification Serviceにイベントを公開」にチェックを入れ、アクセスキーと秘密鍵の情報を入力します。
JSON SNS設定の編集… から以下 JSON フィルターを入力します。
重要度が「重大」に設定された侵入防御イベントのみを、指定の SNS トピックに通知する設定です。
応用すれば、特定のルール番号、文字列、アラートの種類などを AND/OR 条件でフィルターする、柔軟な設定が可能です。
{ "Version": "2014-09-24", "Statement": [ { "Topic": "arn:aws:sns:ap-northeast-1:123456789012:tadokoro-c1ws-sns-pd-topic", "Condition": { "StringEquals": { "EventType": "PayloadLog", "SeverityString": "Critical" } } } ] }
メールインテグレーションから侵入防御アラートを除外する
管理 > システム設定 > アラート > [アラートの設定]
から「侵入防御ルールアラート」のアラートをオフにします。
これにより、侵入防御イベントの発生時にメール通知が飛ばなくなります。
結果、侵入防御イベントは SNS 経由でのみ通知されることになります。
そしてそこでは、重要度が「重大」のものが通知され、「重大」以外のものはフィルターされます。
アラートを発報する
サーバーへの攻撃を再現することになるためここでは省略しますが、SNS 経由のアラートを発報するとこのようになります。
“AWS Notification Message” というなかなかに質素なタイトル。
そして目視で確認するには無理がある雑多な本文です。
2. 加工
C1WS から Amazon SNS に連携すると、タイトルは “AWS Notification Message” となり、本文は JSON オブジェクトを [ ] で囲んだ配列形式のデータとなることが分かりました。
ここから必要な情報を抜き出して整えます。
SNS トピック2 と PagerDuty を連携する
2つ目の SNS トピックを作成し、PagerDuty の Integration Email をサブスクライブします。
先程作成した SNS トピック (tadokoro-c1ws-sns-pd-topic) と同じ状態です。
Lambda 関数を作成する
Lambda 関数を作成します。
Python コードの例は以下のようになります。
メッセージの情報からタイトルや本文を加工した上で、SNS トピック2に渡します。
関数の設定から環境変数 “DESTINATION_TOPIC_ARN” に SNS トピック2のARNを登録します。
また実行ロールには SNS トピック2 への sns:Publish 権限をつけておきます。
import json import boto3 import os def lambda_handler(event, context): sns_client = boto3.client('sns') destination_topic_arn = os.environ['DESTINATION_TOPIC_ARN'] # 環境変数からARNを取得 # SNSイベントからメッセージを取得 message = event['Records'][0]['Sns']['Message'] data_list = json.loads(message) # 配列全体を取得 # 件名を生成(配列内の最初のJSONオブジェクトを使って件名を作成) subject = f"侵入防御ルール {data_list[0]['Reason']} のアラートが発生しました" # 件名が100文字を超えている場合、100文字以内に切り詰める if len(subject) > 100: subject = subject[:100] formatted_messages = [] for data in data_list: reason = data.get('Reason', '不明') log_date = data.get('LogDate', '不明') severity = data.get('SeverityString', '不明') host_id = data.get('HostID', '不明') hostname = data.get('Hostname', '不明') host_os = data.get('HostOS', '不明') action = data.get('ActionString', '不明') formatted_message = ( f"アラート:{reason}\n" f"時刻:{log_date}\n" f"重要度:{severity}\n" f"インスタンスID:{host_id}\n" f"インスタンス名:{hostname}\n" f"ホストOS:{host_os}\n" f"処理:{action}" ) formatted_messages.append(formatted_message) # 各メッセージ間に改行を挿入して結合 final_message = "\n\n".join(formatted_messages) # SNSメッセージを送信 sns_client.publish( TopicArn=destination_topic_arn, Message=final_message, Subject=subject ) return { 'statusCode': 200, 'body': json.dumps('Message processed and sent to SNS') }
SNS トピック1 と Lambda を連携する
Lambda を SNS トピック1にサブスクライブします。
先程作成した SNS トピック1をそのまま使います。
PagerDuty に直接通知するサブスクリプションは削除しておきます。
C1WS と SNS トピック1 を連携する
C1WS 側で SNS トピック1 への通知やフィルターの設定を行います。
今回は先程の設定を使い回すので省略します。
アラートを発報する
この設定でアラートを発報すると PagerDuty では以下のように検知します。
Lambda なしの時と比べるとずいぶん見やすくなりましたね。
これで必要なアラートを必要な情報と共に検知することができるようになりました。
これなら対応不要のアラートに埋もれることも、情報が足りなくてもどかしい思いをすることもありません。
おわりに
SNS と Lambda を使って、C1WS のアラートをフィルター、加工した上で PagerDuty に検出する方法を見てきました。
ここに Terraform や CloudFormation で構築を自動化したり、Lambda のエラー監視を入れることで、より盤石な構築、運用体制を築いていけそうです。
情報であれ、物であれ、何にせよ整理整頓してすっきりとした生活を送りたいものですね。
おしまい