EventBridge API Destinationsを使ってGuardDuty検知をBacklogに自動起票してみた
GuardDuty検知をBacklogの課題に自動起票する仕組みを作ってみました。
ちなみに本ブログは、以下ブログの「GuardDuty」版です。
EventBridgeのイベントパターン、入力トランスフォーマー設定が違うのみで、 他の部分(API Destinations設定など)は殆ど同じです。
なので共通部分やマネコンからの作成手順は割愛します。
イメージ図
アーキテクチャイメージは以下のとおり。 GuardDutyの検知イベントをSecurity Hub経由で取得します(注1)。 EventBridgeから、API Destinationを使ってBacklogへ課題を起票します。
(注1)GuardDutyとSecurity Hubはデフォルトで統合されています。 Security Hubにはリージョン集約機能があります。Security Hub経由にすることで、その恩恵を活用できます。
Backlog起票時のイメージはこんな感じ。
作ってみる
細かい手順は 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に以下課題が自動起票されました。
--- - 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の課題に自動起票する仕組みを作ってみました。
「驚異検知の対応〜記録」を効率化するためのパーツとして活用できると思います。
参考
- EventBridge API Destinationsを使ってSecurity Hub検知をBacklogに自動起票してみた | DevelopersIO
- AWS Security Hub の検出結果を自動で「通知済み」にする | DevelopersIO
- Backlog API とは | Backlog Developer API | Nulab
補足(構築時のコード)
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 } }