
EventBridge API Destinationsを使ってGuardDuty検知をBacklogに自動起票してみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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
}
}