🍒

AWS GuardDutyで実現するVPC内通信の包括的脅威検知

に公開

目的

• VPC内通信の不正アクセスをリアルタイムで検知

セキュリティパイプラインの現状

No. サービス 目的
1 Amazon GuardDuty 脅威検知(VPC Flow Logs + 脅威インテリジェンス)
2 EventBridge アラート通知(Slack連携)
3 Amazon Detective インシデント調査・フォレンジック
4 Security Lake 長期保存・相関分析

VPC Flow Logsの監視対象通信

1. 内部通信

• 同一/異なるサブネット間のEC2通信
• すべてのプロトコル(TCP/UDP/ICMP等)のプライベートIP間通信

2. 外部通信

• Internet Gateway経由の通信
• NAT Gateway経由のインターネットアクセス
• Elastic IP宛の着信通信

3. AWS サービス連携

• VPCエンドポイント(Interface/Gateway)経由の通信
• ロードバランサー関連トラフィック
• マネージドサービス(Lambda/EKS/RDS等)のENI通信

4. ハイブリッド接続

• VPN/Direct Connect経由のオンプレミス通信
• VPCピアリングによるVPC間通信
• Transit Gateway経由の通信

GuardDuty中心の検知アーキテクチャ

データソースと検知内容

データソース 主な検知内容 特徴
VPC Flow Logs ・不審な外部通信
・ポートスキャン
・異常な通信パターン
ネットワークレベルの脅威検知
CloudTrail ・権限昇格
・設定変更
・異常なAPI呼び出し
管理アクションの監視
DNS クエリ ・C2通信
・DNSトンネリング
・DGAドメインアクセス
マルウェア活動の早期発見
EKS 監査ログ ・コンテナ環境の異常
・権限昇格
・不正アクセス
コンテナセキュリティ

追加検討オプション

Malware Protection for S3

  • コスト目安(1TBのS3バケットの場合):
    • 初回フルスキャン:約¥1,024(一回限り)
    • 月間運用コスト:
      • 低更新率(5%/月):約¥51/月
      • 中更新率(10%/月):約¥102/月
      • 高更新率(20%/月):約¥205/月
    • 年間運用コスト:
      • 低更新率:約¥612/年
      • 中更新率:約¥1,224/年
      • 高更新率:約¥2,460/年

検知から対応までのフロー

  1. GuardDutyによる自動検知
  2. EventBridgeによるSlack通知
  3. Detectiveを用いた原因調査
  4. 必要に応じた対応アクション実行

主な検知シナリオ

不正アクセス:ブルートフォース攻撃、権限昇格
データ漏洩:異常な大量データ転送、不審な外部通信
マルウェア活動:C2通信、暗号通貨マイニング
設定ミス悪用:過剰な権限、公開設定の悪用

GuardDutyセキュリティ監視システムの要件

1. 基本的な監視要件

• GuardDuty検出器を有効化し、セキュリティ脅威の検出を行う
• 検出結果の公開頻度を6時間ごとに設定
• すべての重要度の検出結果を監視対象とする

2. データソース監視要件

• S3ログの監視を有効化
• EC2インスタンスのマルウェア保護(EBSボリュームスキャン)を有効化

3. 通知要件

• GuardDutyの検出結果をSNSトピック「guardduty-finding」に送信
• 検出結果をメールアドレス「xxxxx@xxxxx.com」に通知
• Slack通知の準備

4. イベント検知要件

• GuardDutyのすべての検出結果を検知するEventBridgeルールを設定
• GuardDuty検出結果のアーカイブ操作(ArchiveFindings、UpdateFindingsFeedback)も検知

5. 権限要件

• EventBridgeからSNSトピックへの発行権限を持つIAMロールを設定
• 適切な権限分離とセキュリティポリシーの適用

6. 運用要件

• 検出結果の管理(アーカイブ操作の追跡)
• 検出結果の通知システムの構築
• セキュリティイベントへの迅速な対応体制の整備

シーケンス図

Terraform設計

main.tf
# GuardDuty検出器の設定
resource "aws_guardduty_detector" "xxxxx" {
  enable                       = true
  finding_publishing_frequency = "SIX_HOURS"

  datasources {
    kubernetes {
      audit_logs {
        enable = false
      }
    }
    malware_protection {
      scan_ec2_instance_with_findings {
        ebs_volumes {
          enable = true
        }
      }
    }
    s3_logs {
      enable = true
    }
  }
}

# EventBridgeからSNSへの権限を持つIAMロール
resource "aws_iam_role" "eventbridge_to_sns" {
  name = "eventbridge_to_sns"

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

# SNSトピックへの発行権限を定義するポリシードキュメント
data "aws_iam_policy_document" "publish_sns" {
  statement {
    sid    = "AllowPublishToGuarddutyFindingTopic"
    effect = "Allow"
    actions = [
      "sns:Publish"
    ]
    resources = [
      "arn:aws:sns:ap-northeast-1:${data.aws_caller_identity.current.account_id}:guardduty-finding"
    ]
  }
}

# SNS発行権限のIAMポリシー
resource "aws_iam_policy" "publish_sns" {
  name   = "EventBridgePublishGuarddutyFinding"
  policy = data.aws_iam_policy_document.publish_sns.json
}

# IAMポリシーをロールにアタッチ
resource "aws_iam_policy_attachment" "attach" {
  name       = "attach-eb-sns"
  roles      = [aws_iam_role.eventbridge_to_sns.name]
  policy_arn = aws_iam_policy.publish_sns.arn
}

# GuardDuty検出結果通知用SNSトピック
resource "aws_sns_topic" "guardduty_finding" {
  name         = "guardduty-finding"
  display_name = "put-slack"
  fifo_topic   = false
  tags = {
    Name = "guardduty-finding"
  }
}

# SNSトピックのメール通知設定
resource "aws_sns_topic_subscription" "email" {
  endpoint                        = "xxxxx@xxxxx.com"
  protocol                        = "email"
  topic_arn                       = aws_sns_topic.guardduty_finding.arn
  confirmation_timeout_in_minutes = 1
  endpoint_auto_confirms          = false
  raw_message_delivery            = false
}

# GuardDutyアーカイブAPI呼び出しを検知するルール
resource "aws_cloudwatch_event_rule" "gdu_finding_archive_api" {
  name           = "gdu-finding-archive-api"
  description    = "Notify when a GuardDuty finding is archived (API call)"
  event_bus_name = "default"
  state          = "ENABLED"

  event_pattern = jsonencode({
    source      = ["aws.guardduty"]
    detail-type = ["AWS API Call via CloudTrail"]
    detail = {
      eventSource = ["guardduty.amazonaws.com"]
      eventName   = ["ArchiveFindings", "UpdateFindingsFeedback"]
    }
  })
}

# GuardDutyの全検出結果を受け取るルール
resource "aws_cloudwatch_event_rule" "guardduty_finding" {
  name           = "guardduty-finding"
  description    = "GuardDutyのすべての検出結果(全重要度)をSNSトピックに送信"
  event_bus_name = "default"
  state          = "ENABLED"

  event_pattern = jsonencode({
    source      = ["aws.guardduty"]
    detail-type = ["GuardDuty Finding"]
  })
}

# アーカイブ検知をSNSトピックに送るターゲット
resource "aws_cloudwatch_event_target" "guardduty_archive_to_sns" {
  rule      = aws_cloudwatch_event_rule.gdu_finding_archive_api.name
  target_id = "guardduty-archive-notification"
  arn       = "arn:aws:sns:ap-northeast-1:${data.aws_caller_identity.current.account_id}:guardduty-finding"
  role_arn  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge_to_sns"
}

# 全検出結果をSNSトピックに送るターゲット
resource "aws_cloudwatch_event_target" "guardduty_finding" {
  rule      = aws_cloudwatch_event_rule.guardduty_finding.name
  target_id = "guardduty-finding"
  arn       = "arn:aws:sns:ap-northeast-1:${data.aws_caller_identity.current.account_id}:guardduty-finding"
  role_arn  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge_to_sns"
}

検知アラート

Discussion