LoginSignup
1
3

More than 3 years have passed since last update.

AWSWAFでブロックログのアラートをフィルタリングしつつSlackへ飛ばす

Posted at

構成

  • WAFで攻撃を検知したらSlackへ飛ばす

WAF → KinesisFirehose → Lambda → Slack
       ↓
       → S3バケット

WAF

ルール設定

  • ルールは汎用的なものを設定
    • AWSManagedRulesCommonRuleSet
    • AWS-AWSManagedRulesLinuxRuleSet
  • DefaultActionはAllow

image.png

接続リソース設定

  • 紐づけたいALBを指定

image.png

Logging and metrics設定

image.png

  • KinesisiFirehoseにログ出力するようにする image.png

KinesisFirehose

Transform設定

  • kinesisFirehoseのJson出力をLambdaへ渡す
    • Lambda側でのトリガーでKinesisFirehoseを指定することはできない

image.png

Lambda設定

  • トリガー、送信先は指定なし

image.png

ソースコード

  • ランタイムはpython3.8
  • AWSManagedRulesCommonRuleSetの中でNoUserAgent_HEADERUserAgent_BadBots_HEADERがやたらでるので、Slackへは飛ばしたくない
  • それ以外のルールはSlackへ飛ばす
import base64
import json
import os
import urllib.request

def lambda_handler(event, context):
    output = []

    for record in event['records']:
        output_record = {
            'recordId': record['recordId'],
            'result': 'Ok',
            'data': record['data']
        }
        output.append(output_record)

        a = base64.b64decode(record['data'])
        b = json.loads(a)
        # if b['action'] != 'ALLOW':
        #     print("action not ALLOW")
        if b["ruleGroupList"][0]["terminatingRule"]["ruleId"] != 'NoUserAgent_HEADER' and b["ruleGroupList"][0]["terminatingRule"]["ruleId"] != 'UserAgent_BadBots_HEADER':
            print("terminatingRule is not NoUserAgent_HEADER")
            response = post_slack(b)
            print(output_record)

    return {'records': output}

def post_slack(msg):
    send_data = {
        "username": "notify_slack",
        "icon_emoji": ":vampire:",
        "color":"#D00000",
        "text": "WAF攻撃検知",
        "attachments":[
            {
                "fallback":"fallback Test",
                "color":"#D00000",
                "fields":[
                    {
                        "title":"詳細内容",
                        "value":str(msg)
                    }
                ]
            }
        ]
    }


    send_text = "payload=" + json.dumps(send_data)
    # send_text = "payload=" + json.dumps(jsondata)

    method = 'POST'
    headers = {'Content-Type': 'application/json'}
    WEB_HOOK_URL = os.environ['WEBHOOK_URL']

    request = urllib.request.Request(
        WEB_HOOK_URL,
        data=send_text.encode('utf-8'),
        method=method
    )
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode('utf-8')
        print("send ok")

環境変数

  • SlackのWebhookURLを指定する

image.png

動作確認用のテストデータ

  • kinesisFirehoseから送られるであろうデータ形式
  • dataはkinesisFirehoseからs3バケットに出力されているWAFのログをbase64エンコードしたもの
  • lambda関数では、data部分をデコードし、terminatingRuleを判定するようにしている
{
  "invocationId": "invocationIdExample",
  "deliverySteamArn": "arn:aws:kinesis:EXAMPLE",
  "region": "ap-northeast-1",
  "records": [
    {
      "recordId": "49546986683135544286507457936321625675700192471156785154",
      "approximateArrivalTimestamp": 1495072949453,
      "kinesisRecordMetadata": {
        "sequenceNumber": "49545115243490985018280067714973144582180062593244200961",
        "subsequenceNumber": "123456",
        "partitionKey": "partitionKey-03",
        "shardId": "shardId-000000000000",
        "approximateArrivalTimestamp": 1495072949453
      },
      "data": "ew0KCSJ0aW1lc3RhbXAiOiAxNjA0Nzc0MzM1MTEyLA0KCSJmb3JtYXRWZXJzaW9uIjogMSwNCgkid2ViYWNsSWQiOiAiYXJuOmF3czp3YWZ2MjphcC1ub3J0aGVhc3QtMToxMTExMTExMTExMTpyZWdpb25hbC93ZWJhY2wvdmFtZGVtaWMtd2FmdjIveHh4eHh4eHgtY2UwYi00ZWYwLTkxZGUtYjQ0OTg1NmFmNDUxIiwNCgkidGVybWluYXRpbmdSdWxlSWQiOiAiQVdTTWFuYWdlZFJ1bGVzQ29tbW9uUnVsZVNldCIsDQoJInRlcm1pbmF0aW5nUnVsZVR5cGUiOiAiTUFOQUdFRF9SVUxFX0dST1VQIiwNCgkiYWN0aW9uIjogIkJMT0NLIiwNCgkidGVybWluYXRpbmdSdWxlTWF0Y2hEZXRhaWxzIjogW10sDQoJImh0dHBTb3VyY2VOYW1lIjogIkFMQiIsDQoJImh0dHBTb3VyY2VJZCI6ICIxMTExMTExMTExMS1hcHAvdmFtZGVtaWMtZGV2LWJ1c2luZXNzLWRldjItYWxiLzI4YWU3NjcyYzExMTExMWRjIiwNCgkicnVsZUdyb3VwTGlzdCI6IFsNCgkJew0KCQkJInJ1bGVHcm91cElkIjogIkFXUyNBV1NNYW5hZ2VkUnVsZXNDb21tb25SdWxlU2V0IiwNCgkJCSJ0ZXJtaW5hdGluZ1J1bGUiOiB7DQoJCQkJInJ1bGVJZCI6ICJOb1VzZXJBZ2VudF9IRUFERVIiLA0KCQkJCSJhY3Rpb24iOiAiQkxPQ0siLA0KCQkJCSJydWxlTWF0Y2hEZXRhaWxzIjogbnVsbA0KCQkJfSwNCgkJCSJub25UZXJtaW5hdGluZ01hdGNoaW5nUnVsZXMiOiBbXSwNCgkJCSJleGNsdWRlZFJ1bGVzIjogbnVsbA0KCQl9DQoJXSwNCgkicmF0ZUJhc2VkUnVsZUxpc3QiOiBbXSwNCgkibm9uVGVybWluYXRpbmdNYXRjaGluZ1J1bGVzIjogW10sDQoJImh0dHBSZXF1ZXN0Ijogew0KCQkiY2xpZW50SXAiOiAiMTI2LjIwOS4yMjEuNCIsDQoJCSJjb3VudHJ5IjogIkpQIiwNCgkJImhlYWRlcnMiOiBbDQoJCQl7DQoJCQkJIm5hbWUiOiAiSG9zdCIsDQoJCQkJInZhbHVlIjogImRldjIudmFtZGVtaWMuanAiDQoJCQl9LA0KCQkJew0KCQkJCSJuYW1lIjogIkFjY2VwdCIsDQoJCQkJInZhbHVlIjogIiovKiINCgkJCX0NCgkJXSwNCgkJInVyaSI6ICIvIiwNCgkJImFyZ3MiOiAiIiwNCgkJImh0dHBWZXJzaW9uIjogIkhUVFAvMS4xIiwNCgkJImh0dHBNZXRob2QiOiAiR0VUIiwNCgkJInJlcXVlc3RJZCI6ICIxLTVmYTZlOWJmLTIyMmU3NmNkNWJkNDQyYTEyYjg5YmM5YSINCgl9DQp9"
    }
  ]
}

テスト

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3