こんにちは。katoです。
今回はGuardDutyで検知した脅威をSlackに通知する仕組みをご紹介したいと思います。
概要
GuardDutyはセキュリティ監視と脅威の検知を行ってくれるサービスとなっておりますが、GuardDuty単体ではアラート通知等を行うことができず、AWSマネジメントコンソールやAWS CLIなどを利用して検出した脅威を確認する必要が御座います。
定期的にサービスページから確認するのもいいのですが、脅威の検知に気づくのが遅れたり、脅威を一つ一つチェックしていくのが手間になったりします。
GuardDutyが脅威を検出したら、SlackへのPOSTやSNSでのメール送信等で脅威の内容を自動で通知するよう設定し、運用の手間を削減したいと思います。
手順
GuardDutyの有効化
初めに、GuardDutyを有効化します。
GuardDutyのサービスページにアクセスし、「GuardDutyの有効化」をクリックしてください。
Lambda関数の作成
GuardDutyのイベントを受け取り、Slackに通知するためのLambda関数を作成します。 なお、今回はPython3.6を利用しており、requestsモジュールにてメッセージをPOSTしております。 requestsモジュールの含んだZIPファイルのアップロードにてLambda関数を作成してください。
#!/usr/bin/python import requests import json import os def lambda_handler(event, context): url = os.environ['slack'] message = str(event['detail']['title'])+"\n"+str(event['detail']['description'])+"\nseverity: "+str(event['detail']['severity'])+"\neventFirstSeen: "+str(event['detail']['service']['eventFirstSeen']) if event['detail']['severity'] > 3.9: requests.post(url, data = json.dumps({ 'text': message, 'username': u'GuardDuty', 'icon_emoji': u':abc:', 'link_names': 1, }))
上記関数は、脅威度が4(medium)以上のものを通知対象としています。 SlackのURLはLambdaの環境変数として指定しています。
CloudWatch Eventsの設定
作成したLambda関数を呼び出すCloudWatch Eventsを設定します。
CloudWatchサービスページのルールから「ルールの作成」をクリックします。
下記設定にてイベントソースの設定を行います。
・イベントパターン ・サービス別のイベントに一致するイベントパターンの構築 ・サービス名:GuardDuty ・イベントタイプ:GuardDuty Finding
ターゲットとして作成したLambda関数を指定し、ルールを作成します。
動作確認
GuardDutyのサービスページにアクセスし、サイドメニューの「全般」から「結果サンプルの生成」をクリックします。
サンプルイベントが生成され、脅威度がmedium以上のものがSlackに通知されます。
おまけ
SNS通知
SlackではなくSNSを利用してメールにて通知を行う場合は、下記のLambda関数でメール通知が可能です。
import json import os import boto3 def lambda_handler(event, context): sns = boto3.client('sns') message = str(event['detail']['title'])+"\n"+str(event['detail']['description'])+"\nseverity: "+str(event['detail']['severity'])+"\neventFirstSeen: "+str(event['detail']['service']['eventFirstSeen']) if event['detail']['severity'] > 3.9: request = { 'TopicArn': 'arn:aws:sns:ap-northeast-1:012345678901:sns-topic-name', 'Message': message, 'Subject': 'GuardDuty Alarm' } response = sns.publish(**request)
Eventsサンプル
CloudWatch Eventsでの呼び出しから取得可能なGuardDutyのイベントサンプを以下に記載します。
アカウントIDやリソース情報など、要件に応じてメッセージの内容を指定することが可能です。
{ 'version': '0', 'id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'detail-type': 'GuardDuty Finding', 'source': 'aws.guardduty', 'account': '012345678901', 'time': '2018-03-16T08:25:01Z', 'region': 'ap-northeast-1', 'resources': [], 'detail': { 'schemaVersion': '2.0', 'accountId': '012345678901', 'region': 'ap-northeast-1', 'partition': 'aws', 'id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'arn': 'arn:aws:guardduty:ap-northeast-1:012345678901:detector/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/finding/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'type': 'Trojan:EC2/DNSDataExfiltration', 'resource': { 'resourceType': 'Instance', 'instanceDetails': { 'instanceId': 'i-99999999', 'instanceType': 't2.small', 'launchTime': '2017-01-25T13:25:34Z', 'productCodes': [ { 'productCodeId': 'GeneratedFindingProductCodeId', 'productCodeType': 'GeneratedFindingProductCodeType' } ], 'iamInstanceProfile': { 'arn': 'GeneratedFindingInstanceProfileArn', 'id': 'GeneratedFindingInstanceProfileId' }, 'networkInterfaces': [ { 'ipv6Addresses': [], 'networkInterfaceId': 'eni-xxxxxxxx', 'privateDnsName': 'GeneratedFindingPrivateDnsName', 'privateIpAddress': '10.0.0.1', 'privateIpAddresses': [ { 'privateDnsName': 'GeneratedFindingPrivateName', 'privateIpAddress': '10.0.0.1' } ], 'subnetId': 'GeneratedFindingSubnetId', 'vpcId': 'GeneratedFindingVPCId', 'securityGroups': [ { 'groupName': 'GeneratedFindingSecurityGroupName', 'groupId': 'GeneratedFindingSecurityId' } ], 'publicDnsName': 'GeneratedFindingPublicDNSName', 'publicIp': '198.51.100.0' } ], 'tags': [ { 'key': 'GeneratedFindingInstaceTag1', 'value': 'GeneratedFindingInstaceValue1' }, { 'key': 'GeneratedFindingInstaceTag2', 'value': 'GeneratedFindingInstaceTagValue2' }, { 'key': 'GeneratedFindingInstaceTag3', 'value': 'GeneratedFindingInstaceTagValue3' }, { 'key': 'GeneratedFindingInstaceTag4', 'value': 'GeneratedFindingInstaceTagValue4' }, { 'key': 'GeneratedFindingInstaceTag5', 'value': 'GeneratedFindingInstaceTagValue5' }, { 'key': 'GeneratedFindingInstaceTag6', 'value': 'GeneratedFindingInstaceTagValue6' }, { 'key': 'GeneratedFindingInstaceTag7', 'value': 'GeneratedFindingInstaceTagValue7' }, { 'key': 'GeneratedFindingInstaceTag8', 'value': 'GeneratedFindingInstaceTagValue8' }, { 'key': 'GeneratedFindingInstaceTag9', 'value': 'GeneratedFindingInstaceTagValue9' } ], 'instanceState': 'running', 'availabilityZone': 'GeneratedFindingInstaceAvailabilityZone', 'imageId': 'ami-99999999', 'imageDescription': 'GeneratedFindingInstaceImageDescription' } }, 'service': { 'serviceName': 'guardduty', 'detectorId': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'action': { 'actionType': 'DNS_REQUEST', 'dnsRequestAction': { 'domain': 'GeneratedFindingAdditionalDomainName', 'protocol': '0', 'blocked': True } }, 'resourceRole': 'ACTOR', 'additionalInfo': { 'domain': 'GeneratedFindingThreatListName', 'sample': True }, 'eventFirstSeen': '2018-03-16T08:20:05.560Z', 'eventLastSeen': '2018-03-16T08:20:05.560Z', 'archived': False, 'count': 1.0 }, 'severity': 8.0, 'createdAt': '2018-03-16T08:20:05.560Z', 'updatedAt': '2018-03-16T08:20:05.560Z', 'title': 'Data exfiltration through DNS queries from EC2 instance i-99999999.', 'description': 'EC2 instance i-99999999 is attempting to query domain names that resemble exfiltrated data. This could be an indication of a compromised instance.' } }
まとめ
GuardDutyは、AWSリソースのセキュリティ状態やアタックの有無などを検知してくれる便利なサービスとなっております。
今回ご紹介したような通知機能を導入することで、GuardDutyの機能をより有効に利用することが可能になります。
GuardDutyは簡単に導入することが出来るので、まだGuardDutyを利用していない方はぜひ一度お試しになってみてはいかがでしょうか。