実践セキュリティ監視基盤構築(15): ログデータベースの設計
この記事はアドベントカレンダー実践セキュリティ監視基盤構築の15日目です。
ここまでのログ収集・保全の記事では、ログデータをCloud Storageに保存する手順を解説しました。次に、ログデータの変換・投入について説明する前に、投入先のデータベースの設計について考えてみます。
基本的にはビジネス系の大規模データを扱う際のスキーマ設計と同様ですが、セキュリティ監視の特性に合わせた設計も必要ですので、それについても触れます。
データウェアハウスの選択
これまで解説してきた通り、本アドベントカレンダーではBigQueryをデータウェアハウスとして利用します。BigQueryはストレージコストが安く、フルマネージドであるため、大量のデータを抱えつつ運用コストを抑えたいセキュリティ監視基盤の構築に適しています。
他の選択肢としては以下のようなものがあります。
- Amazon Redshift: AWSのデータウェアハウスサービスで、クラスタのリソース選択やスケールを調整できるため、柔軟な運用が可能です。ただし、データ量やクラスタサイズが大きくなるとコストに影響しやすいです。
- Amazon Athena: AWSのサーバレスなクエリサービスで、S3に保存されたデータをクエリできます。ただし、S3に保存するデータの管理は自分で行う必要があるため、運用コストが高くなりがちです。
- Snowflake: クラウドネイティブなデータウェアハウスサービスで、複数のクラウドプラットフォームに対応しやすいですが、IAM管理が独立しているため権限管理に注意が必要です。
- Elasticsearch: ログデータの検索に特化したデータベースで、高速な検索や可視化が可能ですが、インスタンスを常に起動し続ける必要があり、ストレージコストも高くなりやすいです。データウェアハウスの補助的なデータベースとして利用することがあります。
ログデータベースのスキーマ設計
セキュリティ監視に利用するログデータの特徴として、ログの大部分が外部から提供され、スキーマも提供元の特性に合わせて決定されるという点があります。BigQueryはスキーマレスなデータベースではないため、スキーマ設計が必要です。スキーマについては、正規化と管理の2つの観点から検討する必要があります。
(ポイント1) スキーマの正規化
セキュリティ監視に関連したイベントを扱う場合、ログデータは共通したフィールドを持つように思えますが、実際にはログデータの種類によってフィールドが異なることが多いため、すべてのログデータを正規化するのは困難です。具体例をいくつか挙げます。
- 送信元IPアドレス: 例えば、NATを通過した後のIPアドレスと端末に直接付与されたIPアドレスのどちらを使うべきかは文脈によって異なります。また、IPv6の場合、複数のIPアドレスが端末に付与されることもあり、1つのフィールドに収めるのは難しいです。
- イベントの主体(SubjectやPrincipal): イベントを実行した主体を表すフィールドがありますが、OSのユーザ名とユーザ番号など複数の表記方法があったり、ユーザ名とグループ名が混在していたりします。Impersonationなどの概念がある場合、イベントの主体が複数存在することもあります。
このように、同じ属性値でもログの提供元によって文脈が異なるため、完全な正規化は難しいです。したがって、少数の特定のフィールドだけを正規化するアプローチが有効です。詳しくは後述します。
(ポイント2) スキーマの管理
ログの正規化ができない場合でも、スキーマ自体は管理する必要があります。BigQueryはスキーマを持つデータベースであるため、任意のスキーマのログを取り扱う場合にはそのスキーマを把握してログを取り込むことが重要です。これは以下の理由からです。
- クエリのためのスキーマ把握: BigQueryは効率的に検索するために目的のフィールドのみにアクセスするようにクエリを最適化します。そのため、クエリを書く際にはどのフィールドにどのようなデータが入っているかを事前に把握する必要があります。
- フィールドの衝突回避: BigQueryはJSONデータとしてフィールドに任意のスキーマのデータを文字列形式で格納し、検索時にパースして利用できます。スキーマが保証されないと、同じフィールド名で別の型が混在することが起こり得ます。これを避けるためにもスキーマの管理が重要です。
- スキーマの変更: 外部サービスのログはスキーマが変更されることがあります。これを把握せずにログを取り込むと、様々なスキーマが混在し、クエリの作成やデータの分析が困難になります。
スキーマ管理は必要ですが、人間が手作業で管理するのは困難です。そのため、スキーマの管理には自動化が必要です。具体的な方法については、ログの変換・書込の記事で解説します。
スキーマの設計例
2つのポイントを踏まえたうえで、スキーマの設計例を示します。以下はセキュリティ監視のためのログデータベースのスキーマの例です。
-
id
: ログのユニークなIDです。重複排除に利用します。取得元のログにIDがある場合はそのまま利用します。 -
timestamp
: ログの発生時刻です。パーティショニングに利用します。取得元のログにタイムスタンプがある場合はそのまま利用します。 -
ingested_at
: ログが取り込まれた時刻です。現在時刻を記録します。 -
data
: ログの本体です。取得元のログの内容をそのまま格納します。このフィールドはRECORD型として、取得元のログのスキーマをそのまま格納します。
このスキーマは全体的な正規化を諦め、data
フィールドに取得元のログの内容をそのまま格納するアプローチを取っています。この data
以下のスキーマを常にアップデートし続けることで、取得元のログの変更に対応できます。
他の3つのフィールドはログ検索の基本となる範囲指定と重複排除に利用します。 timestamp
はそのまま検索対象となる時間指定に、 id
および ingested_at
は重複排除に利用します。同じ id
のログは同一ですが、例えば後から取り込まれたログは変換処理が修正されている場合があるので、 id
が同じでも ingested_at
が新しいログを優先して利用することで、最新のログを取得できます。
以下のようなクエリで重複排除と時刻指定を統一して行うことができます。
WITH
latest AS (
SELECT
id,
MAX(ingested_at) AS ingested_at
FROM
`my_project.my_dataset.event_logs_v1`
WHERE
TIMESTAMP_TRUNC(timestamp, DAY) = TIMESTAMP("2024-11-30")
GROUP BY
id )
SELECT
logs.*
FROM
`my_project.my_dataset.event_logs_v1` AS logs
INNER JOIN
latest
ON
logs.id = latest.id
AND logs.ingested_at = latest.ingested_at
WHERE
TIMESTAMP_TRUNC(timestamp, DAY) = TIMESTAMP("2024-11-30")
このように、ログデータベースのスキーマ設計はログ種別をまたいだ完全な正規化を諦め、取得元のログの内容をそのまま格納するアプローチが有効です。このアプローチはログの変更に対応しやすく、またログの内容をそのまま利用できるため、柔軟なログデータベースの構築が可能です。ただし、ログのスキーマを把握しBigQuery側のスキーマとの整合性を保つために、スキーマの管理には自動化が必要です。
プロジェクト・データセット・テーブルの設計
セキュリティ監視基盤のために利用するBigQueryのプロジェクト構成は様々ですが、一つのアプローチとして以下のような構成が考えられます。
プロジェクトの構成例
-
source
プロジェクト: セキュリティ監視基盤のパイプラインによって取得したデータを投入するためのプロジェクト -
dwh
プロジェクト: データマートを実装するためのプロジェクト
この構成はセキュリティ監視のために基盤上のパイプラインで取得したもの以外にも、利用できるログデータがある場合に効果的です。具体的には以下のようになります。
セキュリティ監視でのみ利用するログはsource
プロジェクトに投入しますが、他の目的で収集したログを利用したい場合も考えられます。その場合、データの保持をするプロジェクトとクエリをするプロジェクトを分けることで、以下のようなメリットがあります。
- セキュリティ監視に利用したいデータセットの参照を一つのプロジェクトに集約できる: 一つのプロジェクトに集約することで一覧性がよくなり、クエリの構築をする際にも便利になります。
- 予算管理をしやすい: このDWHプロジェクトはセキュリティ監視の目的にのみ利用することで、複数のデータソースを参照する場合もセキュリティ監視にかかるコストを把握しやすくなります。
- データとモデルを分離できる: データの保持とモデルの構築を分離することで、データの保持をするプロジェクトはデータの保持に特化した設計にできます。また、モデルに問題があった場合も修正が容易になります。正規化をしたい場合に便利なアプローチとなります。
dwh
プロジェクトに集約させるためにはいくつかの方法がありますが、一つは dbt のようなツールを利用する方法です。dbtはデータウェアハウスのためのモデルを管理するためのツールで、SQLを使ってデータの変換や集計を行うことができます。
注意点
このような構成は便利な反面、以下のような注意点があります。
-
データセットの地域を統一する: BigQueryのデータセットは地域を統一する必要があります。
source
プロジェクトとdwh
プロジェクトで地域が異なるとクエリやデータの結合ができなくなるため、地域を統一する必要があります。 -
コピーかビューか:
source
プロジェクトのデータをdwh
プロジェクトにコピーするか、ビューを作成するかは検討が必要です。ビューを作成する場合は手軽である反面、権限管理がsource
側に依存するという点に注意が必要です。コピーの場合は権限はdwh
プロジェクトで管理できますが、データのコピーが発生するためコストがかかります。
データセット、テーブルの設計例
データセット、テーブルについても様々な分割の仕方がありますが、一例として以下のような分割が考えられます。
-
service1
データセット: サービス1のログデータを格納するデータセット-
event_logs_v1
テーブル: サービス1のログデータを格納するテーブル -
event_logs_v2
テーブル: サービス1のログデータを格納するテーブル
-
-
service2
データセット: サービス2のログデータを格納するデータセット-
audit_logs_v1
テーブル: サービス2のログデータを格納するテーブル -
inventory_logs_v1
テーブル: サービス2のログデータを格納するテーブル
-
まずデータセットはログの発生元ごとに分割しています。これはセキュリティ監視以外の活用も考えられる場合、権限設定をしやすいように区切るためです。BigQueryは最小でテーブル単位の権限設定をできますが、ログの発生元ごとにわけることでより権限設定をしやすくなります。
テーブルについてはログのバージョンごとに分割しています。先述したスキーマの設計例ではログのスキーマは自動的に更新していくのが望ましいとしていましたが、破壊的変更が必要になる場面もあります。そのような状況のために、バージョンごとにテーブルを分割しておくことで、異なるスキーマのデータを並べて保持できるようにします。
また、ログの提供元によっては複数種類のログが提供される場合もあります。その場合はそれぞれのログの種類ごとにテーブルを分割することで、データの管理をしやすくします。
コスト圧縮のための設計
最後に、データウェアハウスの設計においてコスト圧縮を行うための設計について考えます。データウェアハウスはこのセキュリティ監視基盤のコストの大部分を占めるため、コスト圧縮は重要です。コスト圧縮のための手段はいくつかありますが、設計の段階で主に留意すべき点は以下の2つです。
Storage Billing Models (Physical or Logical)
BigQueryはデータが保存されるストレージに対しての課金モデルが2つあります。それぞれのモデルについて以下で概要を説明します。
- Physical Storage Billing: ストレージに保存されている圧縮後のデータのサイズをもとに課金額が計算されるモデルです。データの圧縮率が高い場合は、データのサイズに対して課金されるため、コストを抑えることができます。
- Logical Storage Billing: データの論理的なサイズをもとに課金額が計算されるモデルです。データの圧縮率が低い場合は、データのサイズに対して課金されるため、コストがかかりやすいです。
料金表を見るとわかる通り、Physical Storage Billingの方がLogical Storage Billingよりも高価です。しかし、データの圧縮率が高い場合はPhysical Storage Billingの方がコストを抑えることができます。この圧縮率はデータをいかに一括して書き込むかによって変わります。そのため(変更頻度の制約などはありますが)設計時だけでなく、実際に蓄積されたデータ量を見ながら調整することが重要です。
Partitioning
BigQueryはパーティショニングをサポートしており、パーティショニングを利用することでクエリの効率化やコスト削減が可能です。パーティショニングはクエリの対象を指定することで探索範囲を狭めるため、クエリの高速化とコスト削減が期待できます。
パーティショニングには様々な値を利用できますが、セキュリティ監視のユースケースではログの発生時刻を示す timestamp
を利用する、 Time-unit column partitioning が適切でしょう。 基本的には時刻を指定してクエリを行うことが多いため、パーティショニングによってクエリの効率化が期待できます。
パーティショニングはテーブルの作成時に指定する必要があります。この際注意しなければいけないのが、時刻の粒度を決めないといけないという点です。Time-unit column partitioningの場合、時間単位(hourly)、日単位(daily)、月単位(monthly)の粒度を指定する必要があります。この粒度は細かいほどクエリの効率化が期待できますが、いくつか制約・注意点があります。
- パーティションはテーブルごとに最大10,000件になります。そのため時間単位のパーティショニングを行う場合、最大で10,000時間分=約416日分のデータしか保持できません。
- パーティションのサイズは10GB以上が推奨値であり、それより小さい場合はパフォーマンスに影響します。
その他の注意事項については、公式のドキュメントを参照してください。パーティションはテーブル作成時にしか指定できないため、設計時に検討しておくことが重要です。
まとめ
セキュリティ監視基盤の構築において、ログデータベースの設計はログの特性に合わせて柔軟に行うことが重要です。今回はBigQueryをベースに紹介しましたが、他のデータウェアハウスの場合も同様の設計が求められます。
また、コスト圧縮のポイントなどもデータウェアハウスのサービスによって異なります。コストはセキュリティ監視基盤の構築において重要な要素の一つであるため、設計時にも留意することが重要です。
Discussion