- クラウド技術
AWS Lambdaを使ってWAFでブロックしたIPアドレスを自動送信させよう
- #AWS
別記事『AWS WAFの検知メトリクスをメールやSNSに通知する方法』はWAFの検知メトリクスをCloudWatchアラームに通して、メールやSNSに通知する方法について紹介しました。ただし、CloudWatchアラームのメールにはIPアドレスやユーザーエージェントなどのメトリクスの詳細が乗らないため、WAFのコンソール画面から取得しないといけません。そのため、今回はWAF検知の詳細をそのままメールから取得できる方法を紹介します。構成図は以下の通りです。
Cloudwatch Logsロググループの作成
【エンジニア募集中】フルリモート可◎、売上/従業員数9年連続UP、平均残業8時間、有給取得率90%、年休124日以上 etc. 詳細はこちらから>
まず、WAF検知メトリクスの保管するCloudWatch Logsロググループを作成します。ロググループを作成したら、WAFの「Logging and metrics」からログの保存を有効化します。
*WAFのログを保管するにはロググループ名が「aws-waf-logs-」で始まる必要があります。
SES IDの作成
次にメールアドレスをAWS SESに登録します。登録後、メールアドレスに送られてくる認証メールから承認すれば、利用可能になります。
*こちらに登録されたメールアドレスは送信先ではなく、送信元になります。実際の検知メールはここに登録されているメールアドレスから送信されます。
S3バケットの作成
続いて、S3バケットを作成します。設定はすべてデフォルトのままで大丈夫です。バケットを作成し終えたら、ローカルにて「blocked_ip.txt」という空のテキストファイルを作成し、バケットの直下にアップロードします。
こちらの空ファイルはLambdaの重複送信を防ぐために使われます。このファイルがないと、大量なメールが一気に送信される可能性があります。
Lambdaの作成
上記の作業が全部終わったら、メール送信用のLambdaを作成します。ランタイムはPython3系の最新版を選択します。実行ロールは「基本的なLambdaアクセス権限で新しいを作成」のままでLambdaを作成します。
Lambdaが作成されたら、実行ロールに新たなポリシーを付与します。付与するポリシーは「AmazonSESFullAccess」と下記の2つのインラインポリシーです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Action": [ "logs:*" ], "Resource": [ "作成したロググループのARN" ] } ] } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": [ "作成したS3のARN", "作成したS3のARN/*" ] } ] } |
ロールを編集したら、Lambdaの「lambda_function.py」のコードを下記のコードに置き換えます。送信元と送信先のメールアドレスおよびS3バケット名を編集する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
import base64 import json import zlib import datetime import os import boto3 import re from botocore.exceptions import ClientError # 送信元メールアドレス SRC_MAIL = "SESに登録しているメールアドレス" # 送信先メールアドレス DST_MAIL = "送信先となるメールアドレス" # リージョン REGION = "ap-northeast-1" def send_email(source, to, subject, body): client = boto3.client('ses', region_name=REGION) response = client.send_email( Source=source, Destination={ 'ToAddresses': [ to, ] }, Message={ 'Subject': { 'Data': subject, }, 'Body': { 'Text': { 'Data': body, }, } } ) return response def lambda_handler(event, context): s3 = boto3.client('s3') # S3のバケット名 bucket_name = '作成したS3のバケット名' # S3バケットにあるIP除外リストファイル名 file_name = 'blocked_ip.txt' s3_object = s3.get_object(Bucket=bucket_name, Key=file_name) body = s3_object['Body'].read().decode('utf-8') data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False)) log_entire_len = len(log_entire_json) for i in range(log_entire_len): log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False)) log_message = log_json['message'] # ブロックしたIPアドレス pattern_ip = r'\"clientIp":"(.+?)\"' match_ip = re.search(pattern_ip, log_message) captured_ip = match_ip.group(1) str_captured_ip = str(captured_ip) # ブロックしたIPアドレスの国 pattern_country = r'\","country":"(.+?)\"' match_country = re.search(pattern_country, log_message) captured_country = match_country.group(1) # ブロックしたIPアドレスのユーザーエージェント pattern_agent = r'\"User-Agent","value":"(.+?)\"' match_agent = re.search(pattern_agent, log_message) captured_agent = match_agent.group(1) add_ip = body + '\n' + str_captured_ip # メールタイトル email = "WAFがアクセスをブロックしました" # 本文 message = f'''WAFがアクセスをブロックしました。 以下はブロックしたIPアドレスとユーザーエージェントです。 ---- ''' message += f'''{captured_ip}({captured_country})\n{captured_agent}''' message += f''' ---- ※こちらのメールは自動送信となっております。 ''' if str_captured_ip in body: print("This IP is already in the list.") else: send_email(SRC_MAIL, DST_MAIL, email, message) s3.put_object(Body=add_ip, Bucket=bucket_name, Key=file_name) print("The mail has been sended.") |
上記の作業が全部終わったら、「設定」からCloudWatch LogsをLambdaのトリガーに追加します。
今回はすべてのWAFルールにおいて、IPアドレスがブロックされたら、メールが来るようにFilter patternを設定しています。もし特定のルールだけを通知させたいであれば、下記のFilter pattern設定例を参照してください。
1 2 3 4 5 6 7 8 9 10 11 |
パラメーター基準の記述例 例1 : 独自のWAFルールでブロックされたIPアドレスを受信したい場合 {($.terminatingRuleType=RATE_BASED)&&($.action=BLOCK)} 例2 : 複数あるWAFルールのうち、特定の独自のWAFルールでブロックされたIPアドレスを受信したい場合 {($.terminatingRuleType=RATE_BASED)&&($.terminatingRuleId=ルール名)&&($.action=BLOCK)} 文字検索基準の記述例 例1 : 複数あるWAFルールのうち、特定の独自のWAFルールでブロックされたIPアドレスを受信したい場合 [(msg = "*BLOCK*" && msg = "*WAFルール名*") && (msg != "*AWSManagedRulesCommonRuleSet*")] 例2 : AWS公式ルールでブロックされたIPアドレスを受信したい場合 [(msg = "*BLOCK*" && msg = "*AWSManagedRulesCommonRuleSet*")] |
動作確認
以上の設定が全部終わったら、動作確認を行ってみましょう。試しにWAFにブロックされてみたら、スクショのようにブロックされたIPアドレスやユーザーエージェントなどの情報が記載されているメールが届きました。これで再びWAFのコンソール画面に入って情報を取得する必要がなくなりました。皆さんもこの方法を使って、面倒な作業を無くしてみませんか。
【エンジニア募集中】フルリモートも◎(リモート率85.7%)、平均残業8時間、年休124日以上、有給取得率90% etc. 詳細はこちらから>