この記事は公開されてから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
}
}