はじめに

こんにちは、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 のエラー監視を入れることで、より盤石な構築、運用体制を築いていけそうです。

情報であれ、物であれ、何にせよ整理整頓してすっきりとした生活を送りたいものですね。

おしまい