Windows Server のイベントログから「監査の失敗(Audit Failure)」のみ S3 に集めてみた

2022.07.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、大前です。

Windows Server のイベントログを CloudWatch Logs 経由で S3 に集約する機会があったのですが、Windows Server の Security イベントに記録される 監査の失敗(Audit Failure) のみを S3 に貯める方法を調査し、実際に試してみましたのでブログに残していきます。

やりたいこと

Windows Server の "Security" に記録されるイベントのうち、キーワードとして 監査の失敗(Audit Failure) が含まれるイベントのみ S3 に集めるような構成を考えます。

今回試した構成

今回は下記の構成で 監査の失敗(Audit Failure) が含まれるイベントのみを S3 に集めてみました。 CloudWatch Agent を利用して "Security" イベントを全て CloudWatch Logs に出力し、CloudWatch Logs のサブスクリプションフィルタで「Audit Failure」が含まれるイベントのみを Kinesis Firehose を通して S3 に出力します。


ちなみに、今年初めのアップデートで CloudWatch Agent 側で正規表現に基づいたフィルタリングができるようになっていますが、現状 Windows Server のイベントログには対応していない様です。(試した内容は後述します)

やってみた

OS ログ出力・収集に必要なサービス展開

Windows Server のイベントログ保存先となる S3 バケットを用意しておきます。今回は syslog-{アカウントID} でバケットを作成しました。


以下の CloudFormation テンプレートを利用し、ログ出力や収集に必要なサービスを展開します。


【クリックで展開されます】os-log-windows-event.yml
AWSTemplateFormatVersion: "2010-09-09"
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  LogGroupPrefixWindows:
    Type: "String"
    Default: "windows-syslog"
  RetentionDays:
    Type: "String"
    Default: 7
  S3BucketName:
    Type: "String"
  FilterPattern:
    Type: "String"
    Default: "\"<Keyword>Audit Failure</Keyword>\""

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  # 取得する syslog を定義する SSM Parameter
  ## Windows
  SSMParameterWindowsLogs:
    Type: "AWS::SSM::Parameter"
    Properties:
      Name: !Sub "AmazonCloudWatch-Windows-syslog-${AWS::Region}"
      Description: "SSM Parameter for Windows to export syslog."
      DataType: "text"
      Tier: "Standard"
      Type: "String"
      Value: !Sub |-
        {
          "agent": {
            "run_as_user": "root"
          },
          "logs":
            {
              "logs_collected":
                {
                  "windows_events":
                    {
                      "collect_list":
                        [
                          {
                            "event_format": "xml",
                            "event_levels": [
                              "INFORMATION",
                              "WARNING",
                              "ERROR",
                              "CRITICAL"
                            ],
                            "event_name": "Security",
                            "log_group_name": "${LogGroupPrefixWindows}-${AWS::Region}-Security",
                            "log_stream_name": "{instance_id}",
                            "retention_in_days": ${RetentionDays}
                          }
                        ]
                    }
                }
            }
        }

  # Kinesis Firehose
  DeliveryStreamWindows:
    Type: "AWS::KinesisFirehose::DeliveryStream"
    Properties:
      DeliveryStreamName: !Sub deliverystream-${LogGroupPrefixWindows}-${AWS::AccountId}-${AWS::Region}
      DeliveryStreamType: "DirectPut"
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      S3DestinationConfiguration:
        BucketARN: !Sub "arn:aws:s3:::${S3BucketName}"
        BufferingHints:
          SizeInMBs: 5
          IntervalInSeconds: 300
        CloudWatchLoggingOptions:
          Enabled: true
          LogGroupName: !Sub /aws/kinesisfirehose/deliverystream-${LogGroupPrefixWindows}
          LogStreamName: "S3Delivery"
        CompressionFormat: "GZIP"
        Prefix: !Sub AWSLogs/${AWS::AccountId}/syslog/Windows/${AWS::Region}/
        RoleARN: !Sub ${FirehoseRole.Arn}

  # 各種 syslog の格納先となる CloudWatch ロググループ & Kinesis Firehose へのサブスクリプション
  ## Windows - Security
  CloudWatchLogGroupWindowsSecurity:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "${LogGroupPrefixWindows}-${AWS::Region}-Security"
      RetentionInDays: !Ref RetentionDays
  SubscriptionFilterWindowsSecurity:
    Type: "AWS::Logs::SubscriptionFilter"
    Properties:
      RoleArn: !GetAtt CloudWatchLogGroupRole.Arn
      LogGroupName: !Ref CloudWatchLogGroupWindowsSecurity
      FilterPattern: !Ref FilterPattern
      DestinationArn: !GetAtt DeliveryStreamWindows.Arn
  ## Windows - Kinesis Firehose エラーログ
  CloudWatchLogGroupWindowsFirehoseError:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub /aws/kinesisfirehose/deliverystream-${LogGroupPrefixWindows}-${AWS::AccountId}-${AWS::Region}
      RetentionInDays: !Ref RetentionDays
  CloudWatchLogStreamDestinationWindowsFirehoseError:
    Type: "AWS::Logs::LogStream"
    Properties:
      LogGroupName: !Ref CloudWatchLogGroupWindowsFirehoseError
      LogStreamName: "DestinationDelivery"
  CloudWatchLogStreamBackupWindowsFirehoseError:
    Type: "AWS::Logs::LogStream"
    Properties:
      LogGroupName: !Ref CloudWatchLogGroupWindowsFirehoseError
      LogStreamName: "BackupDelivery"

  # IAM ロール
  ## CloudWatch ロググループのサブスクリプション用 IAM Role
  CloudWatchLogGroupRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub CWLogGroupRole-${LogGroupPrefixWindows}-${AWS::AccountId}-${AWS::Region}
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action: "sts:AssumeRole"
            Effect: "Allow"
            Principal:
              Service: !Sub logs.${AWS::Region}.amazonaws.com
      Policies:
        - PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Action:
                  - "firehose:*"
                Effect: "Allow"
                Resource: !Sub "arn:aws:firehose:${AWS::Region}:${AWS::AccountId}:*"
              - Action:
                  - "iam:PassRole"
                Effect: Allow
                Resource:
                  - !Sub arn:aws:iam::${AWS::AccountId}:role/CloudWatchLogGroupRole-${LogGroupPrefixWindows}
          PolicyName: cloudwatch_logs_delivery_role_policy
  ## Firehose 用 IAM Role
  FirehoseRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub FirehoseRole-${LogGroupPrefixWindows}-${AWS::AccountId}-${AWS::Region}
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action: "sts:AssumeRole"
            Effect: "Allow"
            Principal:
              Service: "firehose.amazonaws.com"
      Policies:
        - PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Action:
                  - "s3:AbortMultipartUpload"
                  - "s3:GetBucketLocation"
                  - "s3:GetObject"
                  - "s3:ListBucket"
                  - "s3:ListBucketMultipartUploads"
                  - "s3:PutObject"
                Effect: "Allow"
                Resource:
                  - !Sub "arn:aws:s3:::${S3BucketName}"
                  - !Sub "arn:aws:s3:::${S3BucketName}/*"
              - Action: "logs:PutLogEvents"
                Effect: "Allow"
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/deliverystream-${LogGroupPrefixWindows}:log-stream:*"
          PolicyName: firehose_delivery_role_policy


上記 CloudFormation テンプレートをもとにスタックを作成すると、以下のリソースが作成されます。

  • EC2 からのログ出力先となる CloudWatch Logs Group
  • CloudWatch Logs から S3 にログを吐き出すための Kinesis Firehose
  • CloudWatch Agent の設定を保持する SSM Parameters
  • 各種 IAM Role
    • CloudWatch Logs サブスクリプションフィルター用
    • Kinesis Firehose 用


SSM Parameters として作成する CloudWatch Agent の設定ファイルの定義は以下となっています。event_name で "Security" イベントを指定し、event_levels で取得したいイベントのレベルを指定しています。(今回は検証のため多くのログが取れるようにしています)

{
    "agent": {
    "run_as_user": "root"
    },
    "logs":
    {
        "logs_collected":
        {
            "windows_events":
            {
                "collect_list":
                [
                    {
                    "event_format": "xml",
                    "event_levels": [
                        "INFORMATION",
                        "WARNING",
                        "ERROR",
                        "CRITICAL"
                    ],
                    "event_name": "Security",
                    "log_group_name": "${LogGroupPrefixWindows}-${AWS::Region}-Security",
                    "log_stream_name": "{instance_id}",
                    "retention_in_days": ${RetentionDays}
                    }
                ]
            }
        }
    }
}


また、監査の失敗(Audit Failure) イベントは <Keyword> 配下に Audit Failure の文字列が含まれているため、<Keyword>Audit Failure</Keyword> をサブスクリプションフィルターのフィルターパターンとして指定しています。(ダブルクォーテーション含めた指定が必要です)


Windows Server から OS ログ出力してみる

続いて、Windows Server で EC2 を立ち上げ、OS ログ出力設定を行います。今回は Windows Server 2019 で実施しました。SSM Run Command で CloudWatch Agent のインストールや起動を実施するので、IAM Role や SSM サービス利用の前提条件を満たしておく必要がありますのでご注意ください。

参考 : Systems Manager の前提条件 - AWS Systems Manager

EC2 を立ち上げたら、以下コマンドを CloudShell でそれぞれ実行します。インスタンス ID やリージョンは適宜置き換えてから実行してください。

# CloudWatch Agent インストール
aws ssm send-command \
    --document-name "AWS-ConfigureAWSPackage" \
    --instance-ids "<インスタンスID>" \
    --parameters '{"action":["Install"],"installationType":["Uninstall and reinstall"],"name":["AmazonCloudWatchAgent"],"version":["latest"]}'


# CloudWatch Agent 起動
aws ssm send-command \
    --document-name "AmazonCloudWatch-ManageAgent" \
    --instance-ids "<インスタンスID>" \
    --parameters '{"action":["configure"],"mode":["ec2"],"optionalConfigurationLocation":["AmazonCloudWatch-Windows-syslog-<リージョン>"],"optionalConfigurationSource":["ssm"],"optionalOpenTelemetryCollectorConfigurationSource":["ssm"],"optionalRestart":["yes"]}'


上記コマンドを実行すると、CloudFormation で作成された SSM Parameters をもとに CloudWatch Agent が起動されるため、下記のようにログストリームが生成されていれば OK です。


CloudWatch Logs と S3 に出力されたログを確認する

上で CloudWatch Agent の設定ファイルの説明をした通り、CloudWatch Logs には監査の成功/失敗に関わらず Security イベントのログが出力されるため、多くのログが出力されていることが確認できます。


一方で、サブスクリプションフィルターのルールに基づいて 監査の失敗(Audit Failure) イベントのみ S3 にログが転送されるため、S3 側のログは絞られていることが確認できます。(ログの中身を確認すると意図した通り「監査の失敗」イベントのみログに含まれていることが確認できました)


これにて、Windows Server のイベントログのうち、監査の失敗(Audit Failure) イベントのみを S3 に集めることができました。

(おまけ) CloudWatch Agent のフィルタリング機能を Windows イベントログに使いたかった

結論としては先述の通り非対応ではあるのですが、「監査の失敗(Audit Failure) イベントのみを取得する」の実現を検討した際に CloudWatch Agent のフィルタリング機能が使えるかどうかも試していたため、せっかくなので記載しておきたいと思います。

SSM Parameters として、以下の CloudWatch Agent 設定ファイルを用意しました。filters パラメータで "Audit Failure" が含まれる文字列を指定しようとしています。

{
    "agent": {
    "run_as_user": "root"
    },
    "logs":
    {
        "logs_collected":
        {
            "windows_events":
            {
                "collect_list":
                [
                    {
                    "event_format": "xml",
                    "event_levels": [
                        "INFORMATION",
                        "WARNING",
                        "ERROR",
                        "CRITICAL"
                    ],
                    "event_name": "Security",
                    "log_group_name": "${LogGroupPrefixWindows}-${AWS::Region}-Security",
                    "log_stream_name": "{instance_id}",
                    "retention_in_days": ${RetentionDays},
                    "filters": [
                        {
                        "type": "include",
                        "expression": "Audit Failure"
                        }
                    ]
                    }
                ]
            }
        }
    }
}


上記の CloudWatch 設定ファイルをもとに CloudWatch Agent を起動しようとすると、下記のように「filters プロパティは対応してないで」的なエラーが発生します。

PS C:\Users\Administrator> & "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c ssm:AmazonCloudWatch-Windows-syslog-ap-northeast-1
****** processing amazon-cloudwatch-agent ******
I! Trying to detect region from ec2
2022/07/13 06:39:31 D! [EC2] Found active network interface
Region: ap-northeast-1
credsConfig: map[]
Successfully fetched the config and saved in C:\ProgramData\Amazon\AmazonCloudWatchAgent\Configs\ssm_AmazonCloudWatch-Windows-syslog-ap-northeast-1.tmp
Start configuration validation...
2022/07/13 06:39:31 Reading json config file path: C:\ProgramData\Amazon\AmazonCloudWatchAgent\Configs\ssm_AmazonCloudWatch-Windows-syslog-ap-northeast-1.tmp ...
2022/07/13 06:39:31 E! Invalid Json input schema.
2022/07/13 06:39:31 E! Invalid Json input schema.
2022/07/13 06:39:31 Under path : /logs/logs_collected/windows_events/collect_list/0 | Error : Additional property filters is not allowed
2022/07/13 06:39:31 Configuration validation first phase failed. Agent version: 1.0. Verify the JSON input is only using features supported by this version.
PS C:\Users\Administrator>


CloudWatch Agent の設定ファイルに関するドキュメントをよく見ると、filters パラメータは logs > logs_collected > files > collect_list にのみ記載がされており、Windoes イベントログを取得する際に利用する logs > logs_collected > windows_events > collect_list にはパラメータとして記載がないため、現状非対応であるようです。

ドキュメントを確認しながら試していた際には同じ collect_list 配下であったので Windows イベントログも対応しているものだと思い込んでいましたが、実際に試して非対応であることが確認できてよかったと思います。

おわりに

Windows Server のイベントログから 監査の失敗(Audit Failure) のみを S3 に集めてみました。

また、おまけで記載した通り、CloudWatch Agent 側のフィルタリング機能は現状 Windows Server のイベントログには非対応であるようなので、注意しましょう。コスト的な観点で考えると、CloudWatch Agent 側でフィルタリングできた方が嬉しいはずなので、アップデートを待ちたいところです。


以上、AWS 事業本部の大前でした。

参考