Linux環境におけるZeekを用いたネットワークセキュリティ監視:リアルタイムプロトコル解析とメタデータ抽出

1. 現実の課題:ネットワークの死角とストレージの枯渇

基幹インフラ全体のセキュリティ監査中、不審なアウトバウンドビーコンが低優先度のNetFlowアラートを発信しました。15分ごとに、内部ホストがTCPポート443経由で未分類のIP(198.51.100.45)へデータを送信していたのです。境界ファイアウォールには、送信元、宛先、ポート、バイト数という5タプルのみが記録されていました。それだけしか分からず、要求されたSNIドメインやネゴシエーションされたTLS暗号スイート、交換されたペイロードヘッダーはまったく見えませんでした。

調査のために1 Gbpsのアップリンクをミラーリングし、tcpdumpを起動しました。しかし、その結果は運用の混乱を招くだけでした。わずか48時間で、センサーには30個のローテーションファイルにわたって合計620 GBの生.pcapファイルが蓄積されたのです。

たった1つの4 GBのPCAPファイルを開くだけで8分を要し、32 GB RAMを搭載したワークステーション上のWiresharkがフリーズしました。分割された複数ファイルから1つのDNSクエリを探し出すためだけに、Bashスクリプト作成に午後丸一日を費やしてしまいました。インシデント発生時、アナリストには即座の回答が求められます。単一のホスト名を特定するためだけに、数ギガバイトものPCAPファイルの展開に何時間も浪費する猶予はありません。

2. 原因分析:従来のパケット解析が破綻する理由

可視性のボトルネックが生じた原因は、すべてのトラフィックデータを一律に扱っていたことにあります。従来の監視手法は、パケットストレージ、プロトコルの詳細度、アラートのコンテキストに関して根本的な誤認を抱えています。

  • 無差別なペイロード保存:生のパケットキャプチャを実行すると、4K動画ストリームから大容量ファイルバックアップに至るまですべてを収集してしまいます。最新のエンタープライズトラフィックの85%以上はTLS 1.3で暗号化されています。セッションキーのない暗号化アプリケーションバイト列を保存することは、ディスクI/Oを浪費し、SANストレージ費用を高騰させるだけで、調査上のメリットは皆無です。
  • アプリケーション状態の不可視性:ステートフルファイアウォールはTCPハンドシェイクやシーケンス番号を追跡しますが、再構築されたプロトコルのセマンティクスは無視します。攻撃者がポート443経由で独自のCobalt Strikeビーコンを運用していたとしても、レイヤー4の監視ではポート番号が一致しているというだけで標準的なHTTPSとして処理されてしまいます。
  • コンテキストを欠いたアラート:従来のNIDSエンジンは、パケットを静的な正規表現とマッチングさせ、単一のアラート文字列を出力します。アラートが発生しても、前後のコンテキストを手作業で引き出す必要があります。進行中の侵害インシデントと害のないスキャナーを判別するには、直前のDNS名前解決、TLS証明書チェーン、接続継続時間などの情報が欠かせません。

3. ソリューションの比較:フルPCAP vs シグネチャ型IDS vs Zeek

運用コストと調査精度のバランスを取るため、1 Gbpsのコアアップリンク環境で3つのパッシブネットワーク監視アーキテクチャのベンチマーク検証を実施しました。

監視手法 ストレージ消費量(1 Gbpsリンク、平均約350 Mbps) クエリ速度 プロトコルのコンテキスト 運用管理負荷
フルPCAP(tcpdump / daemonlogger) 膨大(約3.5 TB / 日) 数分〜数時間(分割ファイルに対するBPFスキャン) 生バイト列、再構築が必要 高;頻繁なローテーション削除スクリプトが不可欠
シグネチャ型NIDS(Snort / Suricata) わずか(アラートログで約500 MB / 日) 高速(SIEMでインデックス化) 乏しい;ルール合致時のみメタデータを取得 高;誤検知抑制のための継続的なルール調整が必要
メタデータ抽出(Zeek) コンパクト(15〜30 GB / 日) CLIツール(zeek-cut、ripgrep)で1秒未満 高度でステートフルな解析(HTTP、DNS、TLS、SSH、SMB) 低;自動ログローテーションと予測可能な保持期間

フルパケットキャプチャにも、特定のプロトコルデバッグといった用途での存在意義はあります。また、シグネチャエンジンは既知の悪意あるパターンが出現したタイミングを通知してくれます。一方、Zeekは異なるアプローチをとります。自動化されたプロトコル監査員のように動作し、大容量のペイロード本文を破棄しながら、すべてのプロトコルイベントを解析してコンパクトな構造化テキストログに記録します。調査の深度を損なうことなく、ストレージ消費量を約95%削減できます。

4. 最適なアプローチ:リアルタイムメタデータ抽出のためのLinux上へのZeek導入

最も洗練された構成は、Debian 12またはUbuntu 22.04 LTSを実行する専用センサーノードにZeekを配置することです。ホストはスイッチのSPANポートまたはパッシブTAPからトラフィックを直接取得します。500 Mbpsから1 Gbpsの持続的なトラフィックがある環境でも、4コアCPU、16 GB RAM、NVMeログバッファを備えた適度なスペックのホストがあれば、パケットドロップなしで取り込み負荷を処理できます。

ステップ1:システムの準備とインターフェースの設定

セカンダリ物理インターフェース(例:eth1やenp3s0)をトラフィックキャプチャ専用に割り当てます。このインターフェースからすべてのIP割り当てを削除し、ルーティング不可能かつネットワーク上で不可視の状態にします。次に、ネットワークインターフェースカード(NIC)のハードウェアオフロードを無効化します。GRO(Generic Receive Offload)やLROなどのオフロード機能が有効なままだと、NICドライバがカーネルに渡す前にパケットを結合してしまい、ZeekのTCPストリーム再構築エンジンが正常に機能しなくなります。

# IPアドレスなしでプロミスキャスモードにてインターフェースを起動
sudo ip link set dev eth1 up
sudo ip link set dev eth1 promisc on

# パケットの正確な境界を維持するため、TCP/UDPハードウェアオフロードを無効化
sudo ethtool -K eth1 rx off tx off gso off gro off tso off

サーバーの再起動後もこれらのNIC設定を維持するには、/etc/networkd-dispatcher/routable.d/50-sniffer.shにスクリプトを配置するか、対象インターフェース用のカスタムsystemdサービスを作成します。

ステップ2:OpenSUSE Build ServiceからのZeekのインストール

各ディストリビューションの標準パッケージリポジトリには、古いバージョンのZeekが含まれていることがよくあります。公式のOpenSUSE Build Serviceリポジトリを使用して、最新の長期サポート(LTS)リリースを取得します。

# 鍵管理に必要な前提パッケージをインストール
sudo apt-get update && sudo apt-get install -y curl gnupg2 lsb-release

# OpenSUSEの署名鍵をインポート
curl -fsSL https://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/Release.key | \
  gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security_zeek.gpg > /dev/null

# 公式リポジトリソースを追加
echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/ /' | \
  sudo tee /etc/apt/sources.list.d/security:zeek.list

# パッケージリストを更新し、Zeek LTSをインストール
sudo apt-get update
sudo apt-get install -y zeek-lts

# デフォルトプロファイルにZeekのバイナリディレクトリを追加
echo 'export PATH="$PATH:/opt/zeek/bin"' >> ~/.bashrc
source ~/.bashrc

ステップ3:ローカルサブネットとスニッフィングインターフェースの設定

/opt/zeek/etc/に移動し、コア検出パラメータを設定します。

/opt/zeek/etc/networks.cfgを編集して、RFC 1918サブネットとDMZの境界を定義します。Zeekはこれらの定義を送信元および宛先IPと照合し、接続の方向性を正確にタグ付けします。

10.0.0.0/8          Corporate_LAN
172.16.10.0/24      Production_DMZ
192.168.50.0/24     Management_VLAN

次に、/opt/zeek/etc/node.cfgでZeekをスニッフィング用NICにバインドします。単一ホストのセンサー構成の場合、スタンドアロン設定セクションを更新します。

[zeek]
type=standalone
host=localhost
interface=eth1

ステップ4:zeekctlによるエンジンの管理

Zeekには、設定の検証、プロセス制御、ワーカー管理を行うための専用管理ラッパーzeekctlが含まれています。

# 設定構文を検証
sudo /opt/zeek/bin/zeekctl check

# 設定を反映し、バックグラウンドのキャプチャワーカーを起動
sudo /opt/zeek/bin/zeekctl deploy

# スタンドアロンインスタンスの稼働状態を確認
sudo /opt/zeek/bin/zeekctl status

ステータスがrunningと返されれば、Zeekはeth1からのトラフィック解析を正常に開始しています。

ステップ5:zeek-cutによる抽出メタデータのクエリ

Zeekは構造化ログを/opt/zeek/logs/current/にストリーミング出力します。デフォルトでは、明示的なフィールドヘッダーを持つタブ区切り(TSV)形式のテーブルですが、ElasticsearchやOpenSearchに直接パイプする場合はJSON出力に切り替えることも可能です。

監視対象となる主なログストリームは以下の通りです。

  • conn.log:すべてのフローの包括的なレコード。セッション継続時間、IPアドレス、ポート、プロトコルフラグ、合計転送バイト数を追跡します。
  • dns.log:DNSクエリと応答の完全なレコード。リソースレコードタイプ、クエリ名、解決されたアドレス、DNSサーバーのRCODEを含みます。
  • ssl.log:詳細なTLSネゴシエーションメトリクス。Server Name Indication(SNI)文字列、検証済み証明書サブジェクト、発行者CN、暗号スイートの選択内容を記録します。
  • http.log:暗号化されていないHTTPリクエスト。リクエストURI、HTTPメソッド、ホストヘッダー、ステータスコード、User-Agent文字列を記録します。

GUIツールを使わずに、zeek-cutを使用してCLIからアクティブなトラフィックを直接クエリできます。

# クエリ数の多い上位10件の外部ドメイン名を抽出
zeek-cut query answers < /opt/zeek/logs/current/dns.log | \
  sort | uniq -c | sort -nr | head -n 10

# TLSハンドシェイクから不審な外部SNIドメインとレガシー暗号スイートを探索
zeek-cut id.orig_h id.resp_h server_name cipher < /opt/zeek/logs/current/ssl.log | \
  grep -v "yourdomain.com" | head -n 15

# 異常に長時間継続しているTCP接続を特定(継続時間(秒)でソート)
zeek-cut id.orig_h id.resp_h duration proto service < /opt/zeek/logs/current/conn.log | \
  awk '$3 > 3600' | sort -k3 -nr | head -n 10

ステップ6:カスタムイベント駆動型検出スクリプトの作成

Zeekはプログラマブルなネットワーク解析言語でもあります。そのイベント駆動型アーキテクチャにより、プロトコルのステートマシンが変化するたびに実行される特定のロジックを記述できます。例えば、ポリシーでTLS 1.0および1.1が禁止されている場合、非準拠のハンドシェイクをnotice.logに直接フラグ付けできます。

/opt/zeek/share/zeek/site/detect-insecure-tls.zeekを作成します。

@load base/protocols/ssl
@load base/frameworks/notice

module SecurityMonitoring;

export {
    redef enum Notice::Type += {
        Insecure_TLS_Version
    };
}

event ssl_established(c: connection)
    {
    if ( ! c?$ssl )
        return;

    # ハンドシェイクがレガシーなTLSバージョンで確立された場合に通知を発行
    if ( c$ssl?$version && (c$ssl$version == "TLSv10" || c$ssl$version == "TLSv11") )
        {
        NOTICE([$note=Insecure_TLS_Version,
                $msg=fmt("安全でないTLSバージョンが検出されました: %s (%s -> %s, SNI: %s)", 
                         c$ssl$version, c$id$orig_h, c$id$resp_h, 
                         c$ssl?$server_name ? c$ssl$server_name : "<なし>"),
                $conn=c]);
        }
    }

ローカルサイト設定にインクルード行を追加して、スクリプトを有効化します。

echo '@load detect-insecure-tls.zeek' | sudo tee -a /opt/zeek/share/zeek/site/local.zeek
sudo /opt/zeek/bin/zeekctl deploy

内部クライアントが非推奨のハンドシェイクをトリガーすると、Zeekは/opt/zeek/logs/current/notice.logに構造化エントリを書き込みます。このレコードには、クライアントIP、宛先サーバー、ネゴシエーションされたバージョン、リクエストされたSNIホスト名が含まれます。

LinuxにZeekを導入することで、SOCはストレージのオーバーヘッドを予測可能な範囲に抑えながら、高精度なプロトコルインスペクションを実現できます。実用的な運用メタデータと生のペイロードバイト列を分離することにより、アナリストは何ギガバイトものPCAPファイルをかき分ける作業から解放され、数秒でリアルタイムのトランザクション状態を調査できるようになります。

Share: