Sansan Builders Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

全社統合ログ基盤を構築して得た知見

こんにちは。CSIRT の吉山です。

私は2020年の4月にセキュリティエンジニアとして新卒入社し、現在は主にログ基盤(SIEM)の構築・運用やインシデント対応などの業務に取り組んでいます。

今回はそのログ基盤構築や運用、その他検証で得た知見などについて紹介します。

まず初めにログ基盤の技術的な概要についてここで簡単に触れておきます。

ちなみに基盤構築の背景などについては、以前に同じく CSIRTの松田が記事にしているのでこちらもぜひ一読いただければと思います!

buildersbox.corp-sansan.com

構成

基盤は AWS 上で構築しており、Amazon OpenSearch Service (以下、OpenSearch。旧 AWS Elasticsearch Service) をメインに構築しています。

基盤全体を見ると構成としては以下のような状態です。

f:id:yopiyama:20211011161554p:plain
ログ基盤の構成図

ログの流れとしては

  1. S3 へのログの収集
  2. S3 へのオブジェクト配置(PUT イベント)で ETL 用 Lambda (es-loader) をキック
  3. es-loader から OpenSearch へログをポスト

という具合になっています。

ログ基盤へ取り込みたいログはすべて S3 へ保存しています。S3 から OpenSearch までは es-loader が一括して行っているため、S3 に保存さえすれば勝手にログがインデックスされます。

ログの集め方

構成でも触れたとおり、ログ基盤へ取り込むログはすべて1度 S3 を経由します。 S3 を選んだ理由としては主に以下の3点があります。

  1. コスト
    • 保存料金が安価で長期保存にも使える
    • Amazon S3 Glacier などを使うとさらにコスト削減できそう
  2. レプリケーション機能を使う事で、自動で S3 から S3 へオブジェクトを転送する仕組みが作れる
  3. es-loader が S3 のイベント通知に対応している
    • S3 へオブジェクトが PUT された、というようなイベントをきっかけに es-loader が動き PUT されたオブジェクトをロードする

S3 へログを保存する際には以下の 3通りの手法があります。できるだけ 2の方法にできることが望ましいですが、ログを吐き出すシステム側の対応状況によっては fluentd などを使いログを集めます。

  1. Fetch 用のスクリプトや fluentd などを使ってログを S3 へ保存
    • API を使ってログを取得するタイプのログや syslog にしか対応していないログを取得する際に使用
  2. ログの出力先を S3 に設定
    • 主に AWS サービスでこの方法を選択
  3. S3 のレプリケーション機能を使い他のバケットから転送
    • 他の AWS アカウントで収集しているログを集める際にレプリケーションを使用

下図はログの収集から OpenSearch までの経路を簡単に示したものです。他のプロダクトなどのログは 3 の手法のレプリケーション、社内系のログは 1 や 2 の手法で収集しています。加えてログを配置するバケットはそれぞれの送信元毎に分けています。

f:id:yopiyama:20211011195317p:plain

OpenSearch にログがポストされる際には es-loader によって @log_s3bucket@log_s3key といったメタデータに該当するフィールドが追加されます。送信元のプロダクト毎に保存バケットを変えるなどすると、 @log_s3bucket フィールドを確認することでログの送信元の判断をすることができます。特定プロダクトのログのみで串刺し検索する、後述の権限設定を行う際に閲覧制限が掛けやすい、といったメリットが得られます。

ログの取り込みスクリプト(es-loader) について

es-loader は AWS が OSS として公開している SIEM ソリューションの一部です。es-loader は python で記述されています。機能や特徴の一部を上げます。

  • ログの正規化
    • 様々な AWS サービスのログに対応しています
    • 独自のログに対しても正規化の対応ルールを定義することで簡単に対応できます
      • 設定ファイルのサンプル
      • 全社統合ログ基盤では Gsuite や Okta, EDR を初めとするいくつものログ種への対応を行っており、今後これを本家の es-loader 側にも PR する予定です
  • GEO Location の付与やログの値の追加・加工・削除などの処理
    • 各種ログの設定に 1行設定を加えるだけで GEO Location の付与が可能です
    • 他にもログの追加加工のスクリプトを追加することができるため、必要に応じて値の追加・加工・削除ができます
  • SQS との連携によるログの分割処理やエラー
    • SQS を使用することでエラーが発生したログを DLQ へ落とし後から再処理、といった事が容易に行えます
    • ログの分割処理にも対応しているため、一ファイルあたりの行数が非常に多い場合にも難なく処理可能です

他にも様々な特徴があるため是非、触ってみてください。

github.com

ユーザーの管理と権限設定

ログ基盤のユーザー管理には Cognito を使用しています。Cognito のユーザープール側でユーザーとユーザーの所属するグループを管理し、どのユーザーにどのようなロールを割り当てるかの対応を決めています。

権限管理については AWS OpenSearch に提供されている FGAC という機能を利用しています。FGAC は Fine-grained access control の略で、これを有効化することで IAM ベースで以下のようなアクセス制御を行う事ができます。

  • クラスターレベルのセキュリティ
    • OpenSearch のクラスター自体に対するリクエストの制御ができます
  • インデックスレベルのセキュリティ
    • インデックスの作成や検索、読み書き・削除、などをインデックス単位で制御ができます
    • 下図の場合は、特定のロールに対して log-aws-* のような Index のマッピングの確認や read は許可する、という設定を入れています
  • ドキュメントレベルのセキュリティ
    • 特定の値を持ったドキュメントのみの閲覧を許可、といった具合にドキュメント単位で制御ができます
    • ログ基盤では、 @log_s3bucket が sansan からのログを格納しているバケットの場合は Sansan プロダクトのメンバーのみ閲覧が可能、といった制御を行っています
  • フィールドレベルのセキュリティ
    • フィールドの可視性の制御ができます
    • IP やユーザー ID などよりセンシティブな情報の入るフィールドはマスクし弱い権限のユーザーには見せない、といった事ができます

f:id:yopiyama:20211005192634p:plain
ロールの権限設定画面 例

アラーティング

OpenSearch の一機能である Alerting を使い、アラートの設置を行っています。

検索対象の Index やどのフィールドがどのような値になったら通知するか、といった項目を設定することで簡単にアラートを作成することができます。通知先は Slack Webhook や Amazon SNS、Chime などなどの中から選べます。

また、通知の文面はある程度カスタマイズする事ができるため、Slack の構文を駆使して見やすい通知にすることができます。

f:id:yopiyama:20211005192747p:plain
実際に Slack へ送っているアラートの表示例

コスト

現在ログ基盤には大凡 1.5 TB/day のペースでログを取り込んでいます。これだけのログを取り込もうとすると、一般的な SIEM 製品では非常に高コストになります。

現在運用している AWS OpenSearch をベースとしたログ基盤では、比較的低コストで運用できています。加えてログの取り込みについても必要最小限とせず、取り込めるログはできるだけ取り込む方針にすることで、各ログを網羅的に検索できるようにしています。

ちなみに、コストの掛かっているポイントとしては AWS OpenSearch のインスタンス利用料金が特に大きいです。そのためデータノードやマスターノードについては RI を購入することでコスト削減を図っています。

f:id:yopiyama:20211005192835p:plain
日毎の OpenSearch コスト

扱うログ量が増えるにつれて Lambda についても起動回数や時間が増えるため、無視できない程度のコストは掛かっています。ですが、es-loader 側の処理速度向上などのアップデートなどもあって、コストや運用時の利便性を考えるとバランスの良い状態になっています。

基盤の負荷

今まで行ってきたチューニングと後述の検証で得た基盤負荷軽減の知見を始めにまとめて置きます。

  1. シャード毎の容量を適切に区切りノード毎に均等に配置されるようにする
  2. データノードはメモリを優先したものを選択し、メモリ 64 GB 以上に
    1. データノードについてはメモリを優先する必要あり、後述の検証結果を参照
  3. データノードは一台あたりの性能より台数を増やすことを優先
    1. 一つ上のグレードのインスタンスを選択するより同じインスタンスを増やす方がコスパが良い
    2. ログも分散されることになるため、ノード一台あたりの負荷も軽減される
  4. Ultra Warm ノードを活用し、容量が多いログを優先的に移行
    1. 通常のデータノード側で扱うログ量を減らすことで負荷が軽減される
    2. Ultra Warm ノードでも、1度キャッシュに乗ればかなり高速に検索できる
    3. RI が無いためコストはかかるものの、コスパはとても良い

検証について

現在 1.5TB/day のログを処理しているログ基盤ですが、構築当初の計画では 300GB/day のログ取り込みを想定していました。想定の 5倍以上のログを取り込んでいたため、当然基盤音負荷は問題となり、ピークタイムに CPU 負荷が 98% 近くになる事もざらにありました。 これだけ高負荷な状態が定常化すると、大きめのクエリを投げる事も憚れる、収集しているが負荷でインデックスできず検索できない、などせっかくのログが無駄になるような事が起きてきます。

このような状態を改善するため、各種ログや設定のチューニングとログ基盤に使用するインスタンス変更の検証を行いました。ここでは主にインスタンス変更について触れていきたいと思います。

f:id:yopiyama:20211005200510p:plain
検証前の基盤負荷

検証前と検証対象は以下の通りです。

  • 検証前
    • データノード
      • r5.2xlarge x9台
    • マスターノード
      • c5.xlarge x3台
  • 検証パターン
    • データノード
      • c6g.2xlarge x9台
      • c6g.4xlarge x9台
      • r6g.2xlarge x9~15台
    • マスターノード
      • c6g.xlarge x3台

検証結果としては以下の表のようになりました。(!検証結果については必ずこうなるものではなくログ量や各種設定によっても変化するもでのあるため参考程度に留めてください)

検証パターン CPU 負荷 メモリ負荷 検索速度やインデックスの速度 備考
c6g.2xlarge x9台 〇(?) × 一見 CPU 負荷/メモリ負荷が見かけ優秀だが、Indexing の異常な遅延が確認された
c6g.4xlarge x9台 〇(?) × c6g.2xlarge よりは優秀であるものの、Indexing 性能が悪かった
r6g.2xlarge x9台 既存の r5.2xlarge と比較すると CPU 負荷は変化少ないが、メモリ負荷は大幅に改善(80%前後 → 20-30%)
r6g.2xlarge x12台 CPU 負荷がピークタイムでも 60% を下回るように
r6g.2xlarge x15台 CPU 負荷 40% 台を下回るように、取り込むログ量を増やしても 60% を下回る

検証した結果、c 系はなしで r 系を維持、Graviton2 系のインスタンスの優秀さも相まって 2xlarge クラスでも台数を増やせば十分な性能が得られるという結論になりました。

c 系のインスタンスについて負荷は優秀に見えましたが、Indexing が異常に遅延する事が確認されました。

f:id:yopiyama:20211005192920p:plain
c6g.2xlarge での Writing thread pool

AWS のドキュメントによると

By default, Amazon ES uses 50% of an instance’s RAM for the JVM heap, up to a heap size of 32 GiB.

docs.aws.amazon.com

とあります。今回検証した r 系インスタンスのメモリは 64GB ありましたが、今回検証した c 系インスタンスはどちらもそれ以下であるため JVM heap の容量が足りていなさすぎたため、異常な性能悪化が発生したものと思われます。

c 系インスタンスでメモリ 64GB 以上の c6g.8xlarge などを検証することも考えはしましたが、コストが単純に倍以上になる点や vCPU : 32 というのは明らかにオーバースペックに感じられたため、メモリ特化の r 系インスタンスを選択することにしました。

r 系についてはどれも優秀でしたが、検証時の段階で取り込めていないログがある、かつ今後も取り込むログ量が増大する予定もあるため、拡張性を優先して r6g.2xlarge x15 台となりました。

f:id:yopiyama:20211005200823p:plain
検証後の負荷。スパイクする事はあるものの全体的に改善

課題

2点今抱えている課題感についても触れておきます。

  • Ultrawarm node の RI がなくてコストが少々つらい

これはどうしようもない事ではありますが、今のところ OpenSearch の Ultra Warm ノードに RI は出ていません。そのため、オンデマンドで課金せざるを得ないのですが、このオンデマンド料金が地味に全体のコストにも響きます。

今後の RI 追加など AWS 側のアップデートに期待です。

  • Dashboard や Visualization、Index Pattern などの管理が難しい

ログ基盤では multi-tenancy 機能 を使い、ユーザー毎にテナントを発行し閲覧・使用できる Dashboard や Visualization、Index Pattern などのオブジェクトを制御しています。 複数のテナントに同じオブジェクトを置いているような場合には毎回各テナントへオブジェクトを配置する必要があり、非常に手間になります。加えて、更新漏れや改変していたオブジェクトを誤って上書きし改変部分が消えるなどのトラブルも発生しやすいです。

幸い、オブジェクトの配置や更新などを行う API は公開されているため、各テナントへオブジェクトの差分などを考慮しつつオブジェクトを配置するスクリプトを作成しています。 ですが、これも非常に手間かつややこしいため、OpenSearch 側のアップデートでもオブジェクトの管理を円滑にする機能が追加されることを期待しています。

最後に

この記事では SIEM on Amazon OpenSearch Service や OpenSearch を使い構築・運用しているログ基盤について紹介しました。 この基盤を構築して以来、インシデント対応や問い合わせ対応、その他各種事象の調査などに対して活用しています。様々なログに対して横串で素早く検索を掛けることができるため、対応のスピード感や精度も上がったように感じます。 メリットもある一方、少なくないコストが掛かっている点の改善や基盤自体にまだまだブラッシュアップしたい部分もあります。そのため今後は先述の課題の解決や基盤全体のコストカット、基盤の利便性向上などを目標に取り組んでいきます。

また今回のログ分析基盤の取り組みについて、11/5開催のSansan Builders Stage 2021でも発表するので、ご興味のある方はぜひ!

jp.corp-sansan.com

© Sansan, Inc.