ENECHANGE Developer Blog

ENECHANGE開発者ブログ

CloudTrailとCloudWatchLogsの統合

概要

CloudTrailはCloudWatchLogsと統合できるため、CloudWatchを利用して改ざんや不正アクセスに関連する疑わしいイベントを検出できます。

検知条件の策定

セキュリティと運用の観点から合理的かつ包括的なアプローチを取るためにターゲットは下記としました。

  • CloudTrail AWS全体の監査基盤であり、これ自体の設定変更を監視することは、AWSリソース全体の監視に直結します。

  • S3 重要なデータの保管場所であり、機密データの改ざんやアクセス制御の変更を監視することでデータの保護を強化できます。 闇雲に指定しても意味ないので、下記要件を1つでも満たすものを対象とした。

    • 機密性の高いデータを含む
    • コンプライアンス要件がある
    • ビジネスクリティカルなデータを含む
    • アプリケーションの設定ファイル、バックアップデータ、重要なレポートなど
    • 外部公開されている(存在しない認識)
  • Lambda 全Functionを対象としました。 サーバーレスアーキテクチャの中心として、アプリケーションロジックを保護するための監視が重要です。

CloudTrail

CloudTrailに適用する検知条件

1. CloudTrailの無効化(StopLogging / DeleteTrail)

  • 説明: 攻撃者がAWSアカウントに不正アクセスした場合、最初に監査ログを無効化しようとする可能性があります。これにより、後続の不正アクション(例: リソース削除、データアクセスなど)が監視されなくなります。
  • リスク: StopLoggingDeleteTrail APIが実行されると、CloudTrailはログ記録を停止します。不正アクセスや改ざんの追跡が困難になります。
  • 対策: これらのイベントをCloudWatch Logsで監視し、検知した際にすぐに対応できるようにする。

2. ログの保存先S3バケット変更(UpdateTrail)

  • 説明: CloudTrailのログ保存先バケットが変更されると、誤って設定されたバケットに保存され、不正アクセスの痕跡が削除・改ざんされる可能性があります。
  • リスク: 不正なバケットポリシーの場合、攻撃者はログを削除または改ざんする可能性があります。
  • 対策: UpdateTrail イベントを監視し、保存先バケットの変更を検知した場合にアラートを発生させる。

3. CloudTrailのロギング無効化(StopLogging)

  • 説明: StopLogging APIで特定のCloudTrailがログ記録を停止される可能性があります。これは、悪意あるユーザーが操作の痕跡を隠すために行われることがあります。
  • リスク: ログ記録が停止されると、セキュリティ監査に必要なデータが失われ、不正アクセスが検出できなくなります。
  • 対策: StopLogging イベントを監視し、すぐにCloudTrailの再開や調査を実施する。

S3

S3に適用する検知条件 説明は省略

1. オブジェクト削除 (DeleteObject, DeleteObjects)

2. オブジェクト変更 (PutObject, CopyObject)

3. バケットポリシーや設定の変更 (PutBucketPolicy, PutBucketAcl, PutBucketVersioning)

4. バケット削除 (DeleteBucket)

Lambda

Lambda関数に適用する検知条件

1. Lambda関数の無効化や削除(DeleteFunction / UpdateFunctionConfiguration)

説明: 攻撃者が不正アクセスを行った場合、Lambda関数を無効化または削除することで、ビジネスロジックを停止させる可能性があります。 リスク: DeleteFunction や UpdateFunctionConfiguration APIが実行されると、Lambda関数が無効化され、重要なビジネスロジックや処理が停止する恐れがあります。 対策: これらのAPI呼び出しをCloudWatch Logsで監視し、検知した際にアラートを発生させます。

2. Lambda関数の設定変更(UpdateFunctionCode / UpdateFunctionConfiguration)

説明: Lambda関数のコードや設定が変更されると、不正なコードが実行され、機密データが漏洩する可能性があります。 リスク: 関数のコードが改ざんされることで、悪意のある動作が実行されたり、設定変更によってセキュリティポリシーが緩和される可能性があります。 対策: UpdateFunctionCode や UpdateFunctionConfiguration イベントを監視し、関数コードや設定変更が発生した場合に通知を行います。

3. Lambda関数の呼び出し(InvokeFunction)

説明: Lambda関数が不正に呼び出されることで、機密データの流出や不正な処理が行われる可能性があります。 リスク: InvokeFunction APIが不正に呼び出され、ビジネスクリティカルなデータにアクセスされたり、不正な処理が実行される可能性があります。 対策: Lambda関数の呼び出しログをCloudWatch Logsで監視し、異常な呼び出しが検知された場合にアラートを発生させます。

Terraform構成

  • chatbotとsnsはModuleにしています。
cloudtrail
├── cloudtrail.tf
├── cloudwatch_log.tf
├── cloudwatch_metric_log.tf
├── iam_role.tf
├── locals.tf
├── main.tf
└── module.tf

1 directory, 7 files

設定例

locals {
  important_s3_buckets = [
    "arn:aws:s3:::xxxxxxxx/",
    "arn:aws:s3:::xxxxxxxx/",
    "arn:aws:s3:::xxxxxxxx/",
    "arn:aws:s3:::xxxxxxxx/",
  ]
}

# CloudTrailがCloudWatchLogsにログを書き込むためのIAMロール
resource "aws_iam_role" "cloudtrail_cloudwatch_role" {
  name = "cloudtrail-cloudwatch-logs-role"

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

# CloudTrailがCloudWatchLogsにログを送信するためのポリシー
resource "aws_iam_role_policy" "cloudtrail_cloudwatch_policy" {
  role = aws_iam_role.cloudtrail_cloudwatch_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ],
        Resource = "*"
      }
    ]
  })
}

resource "aws_cloudwatch_log_group" "cloudtrail_s3_log_group" {
  name              = "/aws/cloudtrail/cloud-trail-s3-data-events"
  retention_in_days = 365
}

# s3の設定変更やログの停止イベントの監視
resource "aws_cloudwatch_log_metric_filter" "cloudtrail_s3_change" {
  name           = "S3ChangeFilter"
  log_group_name = aws_cloudwatch_log_group.cloudtrail_s3_log_group.name
  pattern        = "{ ($.eventName = DeleteObject) || ($.eventName = DeleteBucket) }"

  metric_transformation {
    name      = "S3ChangeEvents"
    namespace = "S3Metrics"
    value     = "1"
  }
}
resource "aws_cloudwatch_metric_alarm" "cloudtrail_s3_change_alarm" {
  alarm_name          = "CloudTrail-S3-ChangeAlarm"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = aws_cloudwatch_log_metric_filter.cloudtrail_s3_change.metric_transformation[0].name
  namespace           = aws_cloudwatch_log_metric_filter.cloudtrail_s3_change.metric_transformation[0].namespace
  period              = "300"
  statistic           = "Sum"

  alarm_actions = [
    module.aws_chatbot_slack.sns_topic_arn
  ]
  ok_actions = [
    module.aws_chatbot_slack.sns_topic_arn
  ]
}

resource "aws_cloudtrail" "s3_data_events" {
  name                          = "cloud-trail-s3-data-events"
  s3_bucket_name                = <s3bucket-name>
  s3_key_prefix                 = "CloudTrail-S3/" # S3用のフォルダ
  is_multi_region_trail         = true
  enable_log_file_validation    = true
  include_global_service_events = true // グローバルサービスの管理イベント

  # CloudWatchLogs設定
  cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail_s3_log_group.arn}:*"
  cloud_watch_logs_role_arn  = aws_iam_role.cloudtrail_cloudwatch_role.arn

  event_selector {
    read_write_type           = "All"
    include_management_events = true // 管理イベント

    data_resource {
      type   = "AWS::S3::Object"
      values = local.important_s3_buckets
    }
  }
}

module "aws_chatbot_slack" {
  source             = "git@github.com:xxxxxx/terraform-modules.git//chatbot-slack?ref=x.x.x"
  configuration_name = "cloudtrail-change-chatbot-configuration"
  slack_channel_id   = "xxxxxxx"
  slack_team_id      = "xxxxxxx" // #slack channel
  sns_topic_name     = "custom-cloudtrail-change-notifications"
  logging_level      = "ERROR"
  iam_role_name      = "cloudtrail-change"
}