Security HubのイベントをZabbixに連携させてみた

どうしてもZabbixにAWSのイベントを送りつけたいという時に
2023.08.09

AWSのイベントをZabbixに連携させたい

こんにちは、のんピ(@non____97)です。

皆さんはAWSのイベントをZabbixに連携させたいと思ったことはありますか? 私はもちろんありません。皆さんもないですよね。

先日、以下記事を投稿しました。

こちらはCloudWatchアラームのイベントをZabbix Serverに送りつけるというものです。

つまりは、CloudWatchアラームで検出できない = CloudWatchメトリクスで表現されないものについてはZabbixに連携することができません。例えば、Security HubやGuardDuty、AWS Healthなどがありますね。

そのような場合も上述の記事の類似の方法で対応することが可能です。

以降その方法について紹介します。

対応方法

対応方法はEventBridge RuleでLambda関数をキックして、Zabbix SenderでCloudWatchアラームのイベントをZabbix Serverに送ります。ただそれだけです。

ポイントはZabbix Senderのホストとアイテムキーの情報をEvenBridge RuleのInput Transformerで渡すというところです。

対応方法を図示すると以下の通りです。

Security HubのイベントをZabbixに連携させてみた対応策

EventBridge Rule、Lambda関数、Zabbixの事前準備としては以下があります。

  • 監視したいイベントのEventBridge RuleのInput Transformerで以下の情報をLambda関数に渡すように設定
    • Zabbixのホスト名
    • Zabbixのアイテムのキー名
    • オリジナルのイベント
  • Lambda関数の環境変数に以下を設定
    • Zabbix ServerのIPアドレス
    • Zabbix Serverのポート番号
  • Zabbixのホストとアイテムの設定
  • Zabbixのトリガー設定

CloudWatchアラームの状態が遷移した場合は以下の処理が行われます。

  1. EventBridgeでイベント拾う
  2. Input Transformerに従い、Zabbixの情報をイベントに追加してLambda関数を起動
  3. Lambda関数に設定された環境変数を取得
  4. (2)と(3)で取得した情報をもとに、Zabbix SenderでCloudWatch AlarmのイベントをJSON stringifyして送信

使用するLambda関数のコードは以下の通りです。

./lib/lambda/send-alarm-event-to-zabbix/index.py

import boto3
import os
import json
import logging
from pyzabbix import ZabbixMetric, ZabbixSender

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    try:
        logger.info(event)

        # Get zabbix host and item key from event.
        zabbix_host = event['zabbixHost']
        zabbix_item_key = event['zabbixItemKey']

        if zabbix_host is None or zabbix_item_key is None:
            logger.error("Zabbix host and item key are not properly set")
            return

        zabbix_server_ip = os.getenv('ZABBIX_SERVER_IP_ADDRESS')
        zabbix_server_port = int(os.getenv('ZABBIX_SERVER_PORT'))

        zabbix_event_data = [ZabbixMetric(zabbix_host, zabbix_item_key, json.dumps(event['event']))]

        zabbix_sender = ZabbixSender(zabbix_server_ip, zabbix_server_port)
        zabbix_sender_response = zabbix_sender.send(zabbix_event_data)
        
        logger.info(zabbix_sender_response)

    except Exception as e:
        logger.error("Error occurred: {}".format(e))

py-zabbixによりLambda関数の環境変数で指定したZabbix Serverに対して、イベントに含まれるZabbixのホストとキーを使ってアイテムを送りつけます。

今回の手法は監視対象が増えた場合に以下リソースを増やすだけです。監視対象の数だけLambda関数を用意する必要はありません。

  • EventBridge Rule
  • Zabbix側のホストとアイテム設定

そのままイベントを送りつけると、Zabbix側で見づらい場面もあるかと思います。その場合はEventBridge のInput TransformerやStep Functionsでイベントを整形すると良いでしょう。

Security Hub、GuardDuty、IAM Access Analyzerについては、セキュアアカウントをお申し込みいただいた場合やCMPからオプトインする「Amazon EventBridge通知設定」によって、先述のイベントが人間が見やすい形に整形されます。

009_secure_account

抜粋 : [安全なAWSセキュリティ運用ナレッジ2022]セキュアアカウントの使い方 | DevelopersIO

こちらの整形後のイベントをLambda関数でZabbixに送りつける形でも良いと思います。

やってみた

検証環境

検証環境の構成図は以下の通りです。

Security HubのイベントをZabbixに連携させてみた検証環境構成図

今回はSecurity HubとGuardDutyのイベントを検知するようにしています。

こちらの検証環境は基本的にAWS CDKで用意しました。使用したコードは以下にあります。

先述の記事で使用したコードをforkして、イベント連携部分を追加しています。そのため、先述の記事と同様にデプロイ後はLambda関数のENIにElastic IPアドレスを手動で割り当てます。

Zabbixのホストとアイテムの作成

Zabbix Serverのセットアップは先述の記事と全く同じなので省略します。

Zabbixのホストとアイテムの作成から行います。

ZabbixのWebコンソールからConfiguration-HostsCreate hostをクリックします。

Create host

ホスト名とグループを入力および選択してAddをクリックします。

New host

続いてアイテムの登録です。

適当なTypeをZabbix trapperにして適当なキー名を設定します。

Add Item

GuardDuty用のアイテムも同様に設定します。

Add GuardDuty Item

動作確認

それでは動作確認を行います。

GuardDutyは適当にサンプルイベントを生成させます。

GuardDutyのサンプルイベント

サンプルイベントの生成方法は以下記事をご覧ください。

Security HubはSSM Documentを作成して、そちらをパブリックに公開させます。

SSM Documentをパブリック

早くSecurity Hubで検知して欲しいので関連するAWS Config Ruleであるsecurityhub-ssm-document-not-public-87050418を手動で再評価させます。

securityhub-ssm-document-not-public-87050418

Lambda関数のログを確認すると以下のようにログストリームが作成されていました。

Lambdaロググループ

記録されたログは以下のようになっていました。

START RequestId: 46d67f84-8bdd-491e-b4b4-7b9948c9a639 Version: $LATEST
[INFO]	2023-08-09T01:15:12.070Z	46d67f84-8bdd-491e-b4b4-7b9948c9a639	{'event': {'version': '0', 'id': 'e3fbb9d3-cd37-db05-cf12-8cffafb37f9b', 'detail-type': 'Security Hub Findings - Imported', 'source': 'aws.securityhub', 'account': '<AWSアカウントID>', 'time': '2023-08-09T01:15:11Z', 'region': 'us-east-1', 'resources': ['arn:aws:securityhub:us-east-1::product/aws/guardduty/arn:aws:guardduty:us-east-1:<AWSアカウントID>:detector/00c2e1501b6f04f7be8f4d67c45ece60/finding/8c5fd1d14a8d467ca7a0686ffa721036'], 'detail': {'findings': [{'ProductArn': 'arn:aws:securityhub:us-east-1::product/aws/guardduty', 'Types': ['TTPs/Command and Control/CryptoCurrency:EC2-BitcoinTool.B', 'Effects/Resource Consumption/CryptoCurrency:EC2-BitcoinTool.B'], 'SourceUrl': 'https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=8c5fd1d14a8d467ca7a0686ffa721036', 'Description': 'EC2 instance i-99999999 is communicating outbound with a known Bitcoin-related IP address 198.51.100.0.', 'ProductName': 'GuardDuty', 'FirstObservedAt': '2023-08-09T01:13:43.000Z', 'CreatedAt': '2023-08-09T01:13:43.115Z', 'LastObservedAt': '2023-08-09T01:13:43.000Z', 'CompanyName': 'Amazon', 'FindingProviderFields': {'Types': ['TTPs/Command and Control/CryptoCurrency:EC2-BitcoinTool.B', 'Effects/Resource Consumption/CryptoCurrency:EC2-BitcoinTool.B'], 'Severity': {'Normalized': 60, 'Label': 'HIGH', 'Product': 8}}, 'ProductFields': {'aws/guardduty/service/additionalInfo/threatListName': 'GeneratedFindingThreatListName', 'aws/guardduty/service/action/networkConnectionAction/remotePortDetails/portName': 'Unknown', 'aws/guardduty/service/archived': 'false', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/organization/asnOrg': 'GeneratedFindingASNOrg', 'aws/guardduty/service/additionalInfo/value': '{"threatListName":"GeneratedFindingThreatListName","sample":true}', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/geoLocation/lat': '0', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/ipAddressV4': '198.51.100.0', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/geoLocation/lon': '0', 'aws/guardduty/service/action/networkConnectionAction/blocked': 'false', 'aws/guardduty/service/action/networkConnectionAction/remotePortDetails/port': '8333', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/country/countryName': 'United States', 'aws/guardduty/service/serviceName': 'guardduty', 'aws/guardduty/service/action/networkConnectionAction/localIpDetails/ipAddressV4': '10.0.0.23', 'aws/guardduty/service/detectorId': '00c2e1501b6f04f7be8f4d67c45ece60', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/organization/org': 'GeneratedFindingORG', 'aws/guardduty/service/action/networkConnectionAction/connectionDirection': 'OUTBOUND', 'aws/guardduty/service/eventFirstSeen': '2023-08-09T01:13:43.000Z', 'aws/guardduty/service/eventLastSeen': '2023-08-09T01:13:43.000Z', 'aws/guardduty/service/evidence/threatIntelligenceDetails.0_/threatListName': 'GeneratedFindingThreatListName', 'aws/guardduty/service/action/networkConnectionAction/localPortDetails/portName': 'Unknown', 'aws/guardduty/service/action/actionType': 'NETWORK_CONNECTION', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/city/cityName': 'GeneratedFindingCityName', 'aws/guardduty/service/resourceRole': 'TARGET', 'aws/guardduty/service/action/networkConnectionAction/localPortDetails/port': '2000', 'aws/guardduty/service/action/networkConnectionAction/protocol': 'TCP', 'aws/guardduty/service/count': '1', 'aws/guardduty/service/additionalInfo/sample': 'true', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/organization/asn': '-1', 'aws/guardduty/service/additionalInfo/type': 'default', 'aws/guardduty/service/action/networkConnectionAction/remoteIpDetails/organization/isp': 'GeneratedFindingISP', 'aws/guardduty/service/evidence/threatIntelligenceDetails.0_/threatNames.0_': 'GeneratedFindingThreatName', 'aws/securityhub/FindingId': 'arn:aws:securityhub:us-east-1::product/aws/guardduty/arn:aws:guardduty:us-east-1:<AWSアカウントID>:detector/00c2e1501b6f04f7be8f4d67c45ece60/finding/8c5fd1d14a8d467ca7a0686ffa721036', 'aws/securityhub/ProductName': 'GuardDuty', 'aws/securityhub/CompanyName': 'Amazon'}, 'SchemaVersion': '2018-10-08', 'GeneratorId': 'arn:aws:guardduty:us-east-1:<AWSアカウントID>:detector/00c2e1501b6f04f7be8f4d67c45ece60', 'Sample': True, 'RecordState': 'ACTIVE', 'Title': 'EC2 instance i-99999999 communicating with a known Bitcoin-related IP address.', 'Workflow': {'Status': 'NEW'}, 'Severity': {'Normalized': 60, 'Label': 'HIGH', 'Product': 8}, 'UpdatedAt': '2023-08-09T01:13:43.115Z', 'WorkflowState': 'NEW', 'AwsAccountId': '<AWSアカウントID>', 'Region': 'us-east-1', 'Id': 'arn:aws:guardduty:us-east-1:<AWSアカウントID>:detector/00c2e1501b6f04f7be8f4d67c45ece60/finding/8c5fd1d14a8d467ca7a0686ffa721036', 'Resources': [{'Partition': 'aws', 'Type': 'AwsEc2Instance', 'Details': {'AwsEc2Instance': {'Type': 'm3.xlarge', 'VpcId': 'GeneratedFindingVPCId', 'ImageId': 'ami-99999999', 'IpV4Addresses': ['10.0.0.1', '198.51.100.0'], 'SubnetId': 'GeneratedFindingSubnetId', 'LaunchedAt': '2016-08-02T02:05:06.000Z', 'IamInstanceProfileArn': 'arn:aws:iam::<AWSアカウントID>:example/instance/profile'}}, 'Region': 'us-east-1', 'Id': 'arn:aws:ec2:us-east-1:<AWSアカウントID>:instance/i-99999999', 'Tags': {'GeneratedFindingInstanceTag1': 'GeneratedFindingInstanceValue1', 'GeneratedFindingInstanceTag2': 'GeneratedFindingInstanceTagValue2', 'GeneratedFindingInstanceTag3': 'GeneratedFindingInstanceTagValue3', 'GeneratedFindingInstanceTag4': 'GeneratedFindingInstanceTagValue4', 'GeneratedFindingInstanceTag5': 'GeneratedFindingInstanceTagValue5', 'GeneratedFindingInstanceTag6': 'GeneratedFindingInstanceTagValue6', 'GeneratedFindingInstanceTag7': 'GeneratedFindingInstanceTagValue7', 'GeneratedFindingInstanceTag8': 'GeneratedFindingInstanceTagValue8', 'GeneratedFindingInstanceTag9': 'GeneratedFindingInstanceTagValue9'}}], 'ProcessedAt': '2023-08-09T01:15:05.315Z'}]}}, 'zabbixHost': 'AWS Events', 'zabbixItemKey': 'aws.event.guardduty'}
[INFO]	2023-08-09T01:15:12.091Z	46d67f84-8bdd-491e-b4b4-7b9948c9a639	
{
    "processed": 1,
    "failed": 0,
    "total": 1,
    "time": "0.000303",
    "chunk": 1
}

END RequestId: 46d67f84-8bdd-491e-b4b4-7b9948c9a639
REPORT RequestId: 46d67f84-8bdd-491e-b4b4-7b9948c9a639	Duration: 42.00 ms	Billed Duration: 43 ms	Memory Size: 128 MB	Max Memory Used: 62 MB

エラーにはならず、Zabbix Senderの実行ができていそうですね。

ZabbixのWebコンソールからGuardDutyのアイテムを確認してみます。

GuardDuty Item

このようなイベントが大量に登録されていました。実際に運用をする際は整形しないと見づらそうです。

Security Hubのアイテムも確認してみましょう。

Security Hub Item

こちらも問題なくアイテムにデータが登録されています。

どうしてもZabbixにAWSのイベントを送りつけたいという時に

Security HubのイベントをZabbixに連携させてみました。

監視をするにあたって、Zabbix側の設定が必要になるためオススメはしませんが。このような実装をせざるを得ない場面もあると思うので検証しました。

特段要件がなければ素直にAmazon SNSやAWS Chatbot、AWS User NotificationsなどAWSのサービスを使ってメールやSlack、Teamsに通知をするのが良いでしょう。

散々煽っていますがZabbixはサーバーやネットワーク機器を監視する際は非常に有用なソフトウェアです。私もお世話になりました。

ただし、AWSとの連携はどうしても不得手です。適材適所でサービスやツールを使い分けのがベターです。相性が悪いものを頑張って作り込もうとすると、むしろコストがかかったり、不幸になる人が出てきたりします。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!