Amazon GuardDuty to Slack

こんにちは。katoです。

今回はGuardDutyで検知した脅威をSlackに通知する仕組みをご紹介したいと思います。

 

概要

GuardDutyはセキュリティ監視と脅威の検知を行ってくれるサービスとなっておりますが、GuardDuty単体ではアラート通知等を行うことができず、AWSマネジメントコンソールやAWS CLIなどを利用して検出した脅威を確認する必要が御座います。

定期的にサービスページから確認するのもいいのですが、脅威の検知に気づくのが遅れたり、脅威を一つ一つチェックしていくのが手間になったりします。

GuardDutyが脅威を検出したら、SlackへのPOSTやSNSでのメール送信等で脅威の内容を自動で通知するよう設定し、運用の手間を削減したいと思います。

 

手順

GuardDutyの有効化

初めに、GuardDutyを有効化します。

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関数を指定し、ルールを作成します。

 

Lambda関数を指定し、ルールを作成

 

動作確認

GuardDutyのサービスページにアクセスし、サイドメニューの「全般」から「結果サンプルの生成」をクリックします。

 

GuardDuty 動作確認

 

サンプルイベントが生成され、脅威度がmedium以上のものがSlackに通知されます。

 

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を利用していない方はぜひ一度お試しになってみてはいかがでしょうか。

 

 

 

AWS相談会