Snort の検知ログを GCP BigQuery へ送ってみた
プロダクトエンジニアリング部の morikawa です。Zabbix や Ansible の記事ばかり書いてましたが、最近ようやく GCP BigQuery なども触り始めたので今回は BigQuery 関連の記事にしてみました。
今回 BigQuery に送ったのは OSS の IDS/IPS ソフトウェアである Snort で検知したログになります。
要するに Snort インストールしてログを td-agent + fluent-plugin-bigquery で BigQuery に送っただけではあるんですが、ログフォーマットの解析であったり、BigQuery にバッチ読み込みでログを送る際にいろいろつまずいてドキュメントを漁って調べることも多かったので今回記事にした次第です。
Snort のインストール、BigQuery 側のアカウントやデータセットの準備、td-agent のインストールからその設定まで一通り記載してみたので、記事は長いですが再現は比較的容易かと思います。部分的に参照したい場合は以下の目次をご活用下さい。
目次
Snort バージョンについて
- Snort はバージョン 2.9.18-1 を利用しています
Snort がインストールされる環境
- Snort は CentOS7 のマシンにインストールを行います
- Snort がインストールされるマシンの以下のネットワークインターフェースに入ってくるパケットを検査対象とします。このネットワークインターフェースはネットワーク機器側でミラーポート (他ポートへの通信をコピーしてパケットを送出するポート) に接続されているものとします
- eno49
Snort セットアップ
Snort インストール
今回は Snort は 公式サイト の rpm パッケージを用います。(ファイル名に “centos8" が含まれていて CentOS8 向けのように見えますが CentOS7 でも動きました)
まず、パッケージのインストールに必要な daq パッケージ、また snortd を起動する際に必要とされる libdnet パッケージをインストールしておきます。
公式サイトから rpm パッケージをダウンロードします。
rpm コマンドでインストールします。
Snort ルールファイルの取得と展開
Snort はルールファイルを取得し、特定のディレクトリ上に展開する必要がありますが、ルールファイルを取得するにはまずユーザ登録が必要になります。
Snort – Network Intrusion Detection & Prevention Systemからユーザ登録を行います。
ユーザ登録後、 Snort Rules and IDS Software Download へアクセスし、インストールしたバージョン (今回は 2.9.18-1) に対応したルールファイル (今回の場合は snortrules-snapshot-29180.tar.gz ) をダウンロードします。
なお、ユーザ登録すると Oinkcode が発行され、公式サイトのアカウント画面で確認ができます。この Okincode を用いると、以下のようにルールファイルをコマンドラインで取得も可能になります。 Oinkcode はユーザごとに異なりますので、<Oinkcode> の箇所は置き換えてください。
ダウンロードした tar.gz ファイルは Snort をインストールしたサーバにコピーし、展開して /etc/snort/rules/ 以下にコピーします。以下が作業の例です。 (公式サイト にもやり方は記載されていますが、その通りだと手元では上手く動かなかったため、不正アクセス検知システムのSnort インストール | ex1-lab などを参考にルールファイル設置のやり方をカスタマイズしています)
ネットワークインターフェースの設定
検査対象のパケットを受け取る eno49 デバイスについて、IP アドレス無しで有効化できるよう BOOTPROTO を none に変更し、インターフェースを起動します。
次に、他ホスト宛のパケットを受け取るためにプロミスキャストモードの設定も行います。ここでは、サービス起動時に自動的に有効化できるよう、rc.local に設定を記載してそれを反映させるという方法を採っています。
まず作業前に、該当のネットワークインターフェースがパケットを受け取っていないことを確認します。
rc.local ファイルにプロミスキャストモードを有効にするコマンドを追加します。
rc.local が自動起動するよう、rc.local ファイルを実行可能にし、systemctl から自動起動の有効化と実際の起動を実施します。
rc.local サービス起動時にファイルに記載したプロミスキャストモード有効化コマンドが実行されているはずなので、これでパケットを受け取る状態になっているはずです。ネットワークインターフェースの状態を再確認してみます。
Snort 設定
snort.conf ファイルの編集
検査対象の IP アドレスの設定を行うため、/etc/snort/snort.conf を編集します。
ここで以下について修正を加えます。
- HOME_NET
- ここに記載された IP アドレスへの通信についての検査を行います。
- 以下に記載しているのは例になります。
- 管理しているシステムの IP アドレスをリストしておくことで、外向きの通信の検査が除外されるという効果も生じます。
- ここに記載された IP アドレスへの通信についての検査を行います。
- EXTERNAL_NET
- 監視対象以外のネットワークを指定します。
- WHITE_LIST_PATH, BLACK_LIST_PATH
- 以降で用意するホワイトリスト、ブラックリストファイルの置き場のパスを設定します
- dynamicdetection
- 今回はこれは利用しないためコメントアウト
差分は以下のようになります。
ホワイトリスト、ブラックリスト作成
上記設定ファイルの以下の箇所で指定されるファイルが当初は存在しないため作成する必要があります。
以下のように touch コマンドで空ファイルを作成します。
検査対象ネットワークインターフェースの設定、ログへのインターフェース名の記載
eno49デバイスを監視対象にするための設定を /etc/sysconfig/snort に記載します。また以下についても併せて設定しています。
- ログへのインターフェース名の記載
- デフォルトではインターフェース名はログに記載されません。インターフェースが一つの場合は不要ですが、複数のインターフェースに対して検査する場合、違いに意味持たせるため、PRINT_INTERFACE を有効にするのをおすすめします。
編集結果の差分は以下のようになります。
Snort 動作確認
Snort 起動
snort を起動します。
messages ログを確認してエラーが出ていないかを確認してみましょう。Commencing packet processingといったメッセージが表示されていれば問題ない模様です。
脆弱性検出の確認
snort が起動した状態でしばらく待ち、その後検知ログを確認します。ネットワークの状況によりけりですが、インターネットからのアクセスを受け付ける状況であれば何かしらの通信は検知されることになるかと思います。
GCP BigQuery 側の設定
GCP BigQuery 側の設定を行います。Google のアカウント自体はすでに存在し、利用料の支払いの準備も整っていることを前提にしています。
プロジェクトの作成
既に適切なプロジェクトの用意があれば不要ですが、ない場合にはプロジェクトの作成から始めます。
- https://console.cloud.google.com/ へアクセス
- 画面上部の「プロジェクトの選択」をクリックし、そこで表示されるウィザードで「新しいプロジェクト」をクリック
- 以下入力後に「作成」をクリックしてプロジェクトを作成
- プロジェクト名に入力した文字列は原則的にはそれをつなぎ併せてプロジェクト IDになるようです。既にIDとして使われている名称と重複した場合は数値 (おそらく内部的にIDとして使用されるもの) が自動で付与されるようです。プロダクトIDにランダムっぽい数値が含まれるのが嫌であれば既存のものと重複しないようなある程度長い名称をプロジェクト名に指定したほうが良いようです。後で変更はできないので、長く使うのであれば慎重に決めましょう。
一般的にはプロジェクト作成後には IAM にユーザ登録するなどの対応が必要ですがここでは最低限必要なものに絞って記載します。
データセット、ロール、 サービスアカウント、JSON 鍵の準備
外部から BigQuery にログを送る場合、データを格納する器となるデータセットとそこへのアクセス権をもつ認証キーが最低限必要となります。今回認証キーの形式は JSON にします。
この JSON 鍵を強い権限を持つアカウントで発行してしまえばログの受付はできますが、ログを送付するシステム側に不要な権限も与えることになるため、今回は特定のデータセットに対してテーブルの作成、ログのインポートを行うだけの必要最低限の権限を付与したサービスアカウントを用意し、 JSON 鍵を発行してみます。
データセットを作成
- https://console.cloud.google.com/bigquery へアクセス
- プロジェクトを上記で作成したものへと変更
- 画面右側の「データセットを作成」をクリック
- データセット作成画面で以下を入力して「データセットを作成」をクリック
- データセット ID (英数字とアンダースコアのみ使用可能): snort
- データのロケーション: 東京 (asia-northeast1)
- デフォルトは US 設定になっており、明示的に設定しないとログはアメリカに送られます。
- AWS のようにデフォルトロケーションを東京リージョンにするといったことはできない模様です。
- 日本国内ロケーションとしては他に 大阪 (asia-northeast2) もあります。
- デフォルトは US 設定になっており、明示的に設定しないとログはアメリカに送られます。
- デフォルトのテーブルの有効期限: 366日
- ここはお好み、ないしはログの保存ポリシー次第になります。有効期限は設定しないこともできますので IDS のログを永続的に保存したいのであれば有効期限を設定しないのが良いです。この記事では 1 年後に削除する設定を入れてみています。
- 暗号化: Google が管理する鍵
BigQuery データ挿入者 (カスタムロール) の作成
- https://console.cloud.google.com/ ヘアクセス
- メニュー「IAM と管理」→「ロール」を選択
- 「+ロールを作成」をクリック
- ロール作成画面で以下を入力
- タイトル: BigQuery データ挿入者 (カスタムロール)
- 説明: 外部システムからのデータのインポートに利用することを想定したロール。インポートの際はテーブル作成も行うことを前提としている。
- ロールのリリース段階: 一般提供
- 権限
- 以下の 6 つの権限を付与
- bigquery.datasets.get
- bigquery.tables.create
- bigquery.tables.get
- bigquery.tables.list
- bigquery.tables.update
- bigquery.tables.updateData
- 設定に関する補足
- 権限の数が数千以上と半端なく多く、普通に選択しようとしていると辛いので、「+権限を追加」をクリック後「ロールで権限をフィルタリングする」で以下の既存ロールを選択すると上記 6 つの権限を含んだ 30 個程度の権限に絞られるため楽です
- BigQuery データ編集者
- 権限の数が数千以上と半端なく多く、普通に選択しようとしていると辛いので、「+権限を追加」をクリック後「ロールで権限をフィルタリングする」で以下の既存ロールを選択すると上記 6 つの権限を含んだ 30 個程度の権限に絞られるため楽です
- 以下の 6 つの権限を付与
- 最後に「作成」をクリック
サービスアカウントの作成
サービスアカウント自体にもロールをセットする機能はありますが、試したところ データセット毎に権限設定することができなかったため、ここでは行わずデータセットと紐付けるタイミングでロールを関連付けます。
- https://console.cloud.google.com/ ヘアクセス
- メニュー「IAM と管理」→「サービスアカウント」を選択
- 「+サービスアカウントの作成」をクリック
- サービスアカウント作成ウィザードで以下を入力
- サービスアカウントの詳細
- サービスアカウント名: IDS Snort Alerts Importer
- サービスアカウントの説明: IDS (Snort利用) のアラートデータのインポートに利用。このサービスアカウントのキーは IDS サーバで使用される。
- このサービスアカウントにプロジェクトに対してのジョブ設定を許可する
- ロール: BigQuery ジョブユーザー
- ユーザーにこのサービス アカウントへのアクセスを許可
- 設定しません
- サービスアカウントの詳細
- 「完了」をクリック
データセットに対して個別にサービスアカウントに権限付与
- https://console.cloud.google.com/ ヘアクセス
- メニュー「IAM と管理」→「サービスアカウント」を選択
- 上記で作成したサービスアカウントについてメールアドレスが「メール」の欄に記載されているのでそれをコピーしておく
- https://console.cloud.google.com/bigquery ヘアクセス
- エクスプローラ内のプロジェクト、データセット選択画面で上記で作成したデータセットを選択し、→ snort を選択
- 右側の人のようなアイコンであるところの「共有データセット」をクリック
- データセットの権限画面で「メンバーを追加」の欄に上記メールアドレスをペースト
- 「ロールを選択」のプルダウンは カスタム → BigQuery データ挿入者 (カスタムロール) を選択
- 「追加」をクリック
- 最後に下部の「完了」をクリック
なお、既存設定を確認したい場合は先程と同様に「データセットの権限」画面へ移動すると、「BigQuery データ挿入者 (カスタムロール)(1 人のメンバー)」が確認できる。内容はプルダウンで確認が可能となっています。
鍵の発行
- https://console.cloud.google.com/ ヘアクセス
- メニュー「API とサービス」→「認証情報」を選択
- 「サービスアカウント」欄から上記で作成した IDS Snort Alerts Importer を選択
- 「キー」欄で「鍵を追加」をクリックし「新しい鍵を作成」をクリック
- キーのタイプとしては JSON を選択して「作成」をクリック
- 鍵となる JSON ファイルがダウンロードされるので一旦ローカルPC上に保存
このJSONファイルは権限が対象のデータセット上でのテーブル作成とログ送付に権限は絞られてはいますが機密な情報になるので、以降の作業でサーバ上に保存したら手元からは削除しましょう
以上で GCP 上での準備は完了です。
Snort サーバから td-agent を用いて GCP BigQuery へログを送付
td-agent と fluent-plugin-bigquery などのプラグインのインストール
Snort サーバに td-agent バージョン 4 をインストールします。バージョン 4 としているのは fluent-plugin-bigquery プラグインのためです。なお、仮に既に td-agent バージョン 3 以下がインストールされていてバージョン 4 に入れ替えるのが難しい場合はバージョン 4 をインストールしたログ中継サーバを用意するなどすると良いでしょう。
fluent-plugin-bigquery プラグインでログを BigQuery にインポートする場合以下の 2 つの方法を採ることができます。
- bigquery_load プラグインを使用
- 設定した間隔でログをバッチ処理でインポートします
- メリット
- この方法で BigQuery にログをインポートする場合、インポート自体には費用がかかりません
- デメリット
- 設定した間隔でログが送られるため、出力されるログを BigQuery で確認できるようになるまで時間がかかります
- 後述の理由からあまり短い間隔を設定することもできません
- 1 日のテーブルあたりの読み込みジョブの件数は 1,000 件まで、1 日のプロジェクトあたりの読み込みジョブは 50,000 件に制限されています
- Fluentd と BigQuery を使用したリアルタイムのログ分析 | Cloud アーキテクチャ センター
- ここでの「読み込むジョブの件数」はbigquery_load プラグインでインポートする回数に相当します
- 設定した間隔でログが送られるため、出力されるログを BigQuery で確認できるようになるまで時間がかかります
- bigquery_insert プラグインを使用
- ログを BigQuery に対してストリーミング入力します。
- メリット
- ログは数秒以内に BigQuery にインポートされるため、ほぼリアルタイムでログを BigQuery で確認することが可能です。
- デメリット
- ストリーミング入力自体に費用がかかります。
今回は bigquery_load プラグインで 10 分おきにログを送る例を示します。
td-agent の設定
バックアップを取って td-agent.conf を編集します。
Output descriptions に以下の設定を追記します。
Source descriptions に以下の設定を追記します。 expression 部分の正規表現は Fluentular: a Fluentd regular expression editor を利用するなどして作りました。
JSON キーファイル作成
スキーマファイル作成
td-agent サービスを起動
なお、個人的には td-agent の経験不足もあるとは思いますがだいたい何かエラーが出ます。上手く起動しない場合はすぐログを確認しましょう。
td-agent 起動後のエラーでハマったもの
時刻の形式は GCP の TIMESTAMP 型で解釈できる形式に明示的に変更が必要
Snort の日時形式は少々独特で %m/%d-%H:%M:%S というスタイルであるため、これを GCP BigQuery に送る場合に明示的に変更する必要がありました。変更せずに送ろうとすると以下のエラーが…。
なお、GCP BigQuery の TIMESTAMP 型として受け入れられる時刻形式は 公式ドキュメント Cloud Storage からの CSV データの読み込み | BigQuery | Google Cloud に例として記載されています。
- 一般的な時刻の形式
- 2018-08-19 12:11
- 2018-08-19 12:11:35
- 2018-08-19 12:11:35.22
- 2018/08/19 12:11
- 2018-07-05 12:54:00 UTC
- 2018-08-19 07:11:35.220 -05:00
- 2018-08-19T12:11:35.220Z
- Unix エポック時間
- 1534680695
- 1.534680695e11
私の場合は record_transformer の中で ruby の時刻関係の関数を呼び出して変換を行って対処しました。td-agent.conf 内の以下の部分になります。
GCP BigQuery に入れるログは JSON 形式になっている必要がある
たまたま以前に BigQuery にインポートしたログが JSON 形式のものだったので意識していなかったのですが、JSON 形式にせずに BigQuery に送ろうとすると、JSON 形式を前提とする先方からエラーを返されます。何かしらのデータ構造になってるんだし問題ないでしょうという思い込みからしばしハマりました。
こちらは以下のように record_reformer の中で to_json 関数を使って変換することで対処しました。
bigquery_load プラグイン使用時は buffer セクションに path が必須
個人的には bigquery_insert プラグインを先に触っていたので、buffer セクション内の path の記載をサボってエラーに遭遇しました。
bigquery_load の buffer セクション内の path は、出力されたログを td-agent が読み込み、これを BigQuery に送り出すまでに溜めておくためのファイルのパスを示しているものです。当初この理解が及んでおらず、またウェブサイトを漁ると path に “*" (アスタリスク) がつく例が多く、「これ、読み込む先のファイル?」と誤解して混乱してしまったのですが、アスタリスクには (おそらく他と重複が起こりにくいよう) ランダムな文字列がシステムから付与されているようでした。ログを更新しつつ、該当のパスを watch コマンドでウォッチしてみると、ファイル生成→サイズ増加→ファイル消滅 (ログが BigQuery に送られた瞬間) → ファイル再度生成、が繰り返されるのが見て取れます。
以上で一通りやることは完了になります。
GCP BigQuery でログを確認
td-agent がエラーを吐かずに動作していれば BigQuery のログ転送は成功しているとみなして問題ないかと思います (私はそこからハマったりはしませんでした)。BigQuery のコンソールを開き、対象のデータセットを見てみると、 YYYY-MM-DD 形式のテーブルが作成され、その中にログが格納されていることが確認できるかと思います。
まとめ
今回は Snort のログを BigQuery に送るという一言で言えばそれだけの内容について、Snort のインストールから地道に書き出してみました、みたいな記事にしてみました。
Snort のインストールは調べてみるとソースからコンパイルというものが多かったですが、パッケージが意外とあっさり使えたのでそんなに苦労しませんでした。
やはり GCP BigQuery にログを送るというのが簡単なようで細かいところでつまずくところが多かったです。GCP も td-agent やそのプラグインも公式ドキュメントはもちろん充実してるんですが、よく分からないまま組み合わせて使おうとしてハマるということをまだ繰り返しております。イマドキですとシステムのログは BigQuery などの外部の分析基盤に転送していつでも解析できるみたいなのが標準になりつつあるようですし、もう少しサクサクと使いこなせるようになってトライコーンのシステムのログ周りの環境もアップデートしたいですね。
ディスカッション
コメント一覧
まだ、コメントがありません