EventBridge API Destinationsを使ってGuardDuty検知をBacklogに自動起票してみた

EventBridge API Destinationsを使ってGuardDuty検知をBacklogに自動起票してみた

Clock Icon2022.09.27 01:11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

GuardDuty検知をBacklogの課題に自動起票する仕組みを作ってみました。

ちなみに本ブログは、以下ブログの「GuardDuty」版です。

EventBridgeのイベントパターン、入力トランスフォーマー設定が違うのみで、 他の部分(API Destinations設定など)は殆ど同じです。

なので共通部分やマネコンからの作成手順は割愛します。

イメージ図

アーキテクチャイメージは以下のとおり。 GuardDutyの検知イベントをSecurity Hub経由で取得します(注1)。 EventBridgeから、API Destinationを使ってBacklogへ課題を起票します。

img

(注1)GuardDutyとSecurity Hubはデフォルトで統合されています。 Security Hubにはリージョン集約機能があります。Security Hub経由にすることで、その恩恵を活用できます。

Backlog起票時のイメージはこんな感じ。

img

作ってみる

細かい手順は Security Hub版ブログ をご覧ください。 主にSecurity Hub版との差分を説明します。

また、構築に使ったTerraformコードを後ろの 『補足(構築時のコード)』 に記載しています。

EventBridge設定(イベントパターン)

イベントパターンは以下のとおりです。

{
  "detail-type": ["Security Hub Findings - Imported"],
  "source": ["aws.securityhub"],
  "detail": {
    "findings": {
      "ProductName": ["GuardDuty"],
      "RecordState": ["ACTIVE"],
      "Workflow": {
        "Status": ["NEW"]
      },
      "Severity": {
        "Label": ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
      }
    }
  }
}
  • Security Hub経由のGuardDutyイベント
  • レコードが Active
  • ワークフローのステータスが NEW
  • 全ての重要度

上記に一致するイベントが対象になります。

EventBridge設定(入力トランスフォーマー)

次にターゲット設定です。 「入力トランスフォーマー」以外は Security Hub版ブログ と同じです。

入力トランスフォーマーの「入力パス」、「テンプレート」設定は以下のとおりです。

{
  "AwsAccountId": "$.detail.findings[0].AwsAccountId",
  "Description": "$.detail.findings[0].Description",
  "FindingType": "$.detail.findings[0].Types[0]",
  "Id": "$.detail.findings[0].Id",
  "Severity": "$.detail.findings[0].Severity.Label",
  "SourceUrl": "$.detail.findings[0].SourceUrl",
  "Title": "$.detail.findings[0].Title"
}
{
  "projectId": "1234567890",
  "summary": "<FindingType> found at AWS account <AwsAccountId>",
  "description": "<Title><br /><br />Description:<br /><Description><br /><br />Severity:<br /><Severity><br /><br />Links:<br />[GuardDuty](<SourceUrl>)",
  "issueTypeId": "1234567890",
  "priorityId": "3"
}

動作確認

GuardDutyにて サンプル検出結果 を生成しました。

しばらくするとBacklogに以下課題が自動起票されました。

img

---
- title: Unusual Behaviors/VM/Behavior:EC2-TrafficVolumeUnusual found at AWS account 123456789012
---

Unusually large amount of network traffic from EC2 instance i-99999999.

Description:
EC2 instance i-99999999 is generating unusually large amounts of network traffic to remote host 198.51.100.0.

Severity:
MEDIUM

Links:
[GuardDuty](https://ap-northeast-1.console.aws.amazon.com/guardduty/home?region=ap-northeast-1#/findings?macros=current&fId=14examplef4a5)

おわりに

GuardDuty検知をBacklogの課題に自動起票する仕組みを作ってみました。

「驚異検知の対応〜記録」を効率化するためのパーツとして活用できると思います。

参考

補足(構築時のコード)

terraform.tfvars

こちらのGitHub Gistにも上げています。

backlog_issues_url    = "https://xx.backlog.jp/api/v2/issues?apiKey=abcdefghijklmn"
backlog_project_id    = "1234567890"
backlog_issue_type_id = "1234567890"
backlog_priority_id   = "3"

main.tf

こちらのGitHub Gistにも上げています。

### Provider
provider "aws" {
  region = "ap-northeast-1"
}

### Locals
locals {
  prefix = "test"
}

### Variables
variable backlog_issues_url {}
variable backlog_project_id {}
variable backlog_issue_type_id {}
variable backlog_priority_id {}

### Resources(IAMロール)
resource aws_iam_role backlog {
  name = "${local.prefix}-backlog-events-role"

  assume_role_policy = <<-EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Principal": {
            "Service": "events.amazonaws.com"
          },
          "Effect": "Allow"
        }
      ]
    }
  EOF

  inline_policy {
    name   = "aws-actions"
    policy =  <<-EOF
      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": [
              "events:InvokeApiDestination"
            ],
            "Resource": "${aws_cloudwatch_event_api_destination.backlog.arn}"
          },
          {
              "Effect": "Allow",
              "Action": [
                  "secretsmanager:CreateSecret",
                  "secretsmanager:UpdateSecret",
                  "secretsmanager:DescribeSecret",
                  "secretsmanager:DeleteSecret",
                  "secretsmanager:GetSecretValue",
                  "secretsmanager:PutSecretValue"
              ],
              "Resource": "arn:aws:secretsmanager:*:*:secret:events!connection/*"
          }
        ]
      }
    EOF
  }
}

### Resources(EventBridge)
# Connection
resource aws_cloudwatch_event_connection backlog {
  name               = "${local.prefix}-backlog"
  authorization_type = "API_KEY"

  auth_parameters {
    api_key {
      key   = "dummy-key"
      value = "dummy-value"
    }
  }
}

# API Destination
resource aws_cloudwatch_event_api_destination backlog {
  name                             = "${local.prefix}-backlog-issues"
  invocation_endpoint              = var.backlog_issues_url
  http_method                      = "POST"
  invocation_rate_limit_per_second = 2
  connection_arn                   = aws_cloudwatch_event_connection.backlog.arn
}

# EventBridgeルール
resource aws_cloudwatch_event_rule guardduty {
  name           = "${local.prefix}-security-events-guardduty"
  event_bus_name = "default"

  event_pattern = <<-EOF
    {
      "detail-type": ["Security Hub Findings - Imported"],
      "source": ["aws.securityhub"],
      "detail": {
        "findings": {
          "ProductName": ["GuardDuty"],
          "RecordState": ["ACTIVE"],
          "Workflow": {
            "Status": ["NEW"]
          },
          "Severity": {
            "Label": ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
          }
        }
      }
    }
  EOF
}

resource aws_cloudwatch_event_target guardduty {
  event_bus_name = "default"
  rule           = aws_cloudwatch_event_rule.guardduty.name
  target_id      = "backlog"
  arn            = aws_cloudwatch_event_api_destination.backlog.arn
  role_arn       = aws_iam_role.backlog.arn

  input_transformer {
    input_paths = {
      Description  = "$.detail.findings[0].Description",
      Id           = "$.detail.findings[0].Id",
      Title        = "$.detail.findings[0].Title",
      Severity     = "$.detail.findings[0].Severity.Label",
      SourceUrl    = "$.detail.findings[0].SourceUrl",
      FindingType  = "$.detail.findings[0].Types[0]",
      AwsAccountId = "$.detail.findings[0].AwsAccountId"
    }
    input_template = <<-EOF
      {
        "projectId": "${var.backlog_project_id}",
        "summary": "<FindingType> found at AWS account <AwsAccountId>",
        "description": "<Title><br /><br />Description:<br /><Description><br /><br />Severity:<br /><Severity><br /><br />Links:<br />[GuardDuty](<SourceUrl>)",
        "issueTypeId": "${var.backlog_issue_type_id}",
        "priorityId": "${var.backlog_priority_id}"
      }
    EOF
  }
}

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.