LinuxにおけるSuricataの導入と最適化:実践で培ったIDS/IPS構築ノウハウ

Network tutorial - IT technology blog
Network tutorial - IT technology blog

深夜2時のアラートとIDS/IPS選定の課題

午前2時14分、PagerDutyのアラートが鳴り響く。Nginxゲートウェイクラスタがボットネットから毎秒35万リクエスト(350,000 req/s)の猛攻を受け、CPU使用率は98%に急上昇、メモリは逼迫し、接続が次々と切断されていく。与えられた猶予はわずか5分。決済手続き中の正当な顧客トラフィックを巻き込むことなく、悪意あるIPからの攻撃だけを遮断しなければならない。高負荷に耐えうるNetwork IDS/IPSエンジンの真価が問われる瞬間だ。

現在、Linux環境における代表的な選択肢として以下の3つが挙げられる:

  • Snort(2.x系): 長年の実績を持つ定番だが、シングルスレッド(single-thread)アーキテクチャの制約を抱える。トラフィックが1.5 Gbpsを超えると、パケット検査を担うCPUコアがボトルネックとなり、深刻なパケットドロップが発生する。Snort 3ではマルチスレッドに対応したものの、エコシステムやプラグインの成熟度には依然として課題が残る。
  • Zeek(旧Bro): フォレンジック用途におけるアプリケーション層のメタデータ抽出では圧倒的な性能を誇る。しかし、メモリ消費量が極めて大きく(本番環境では16〜32 GBのRAMを容易に消費する)、リアルタイムでのパケット遮断(インラインIPS)を想定した設計にはなっていない。
  • Suricata: 当初からネイティブなマルチスレッド(multi-threading)アーキテクチャを採用。32〜64コアのサーバーでもスムーズにスケールし、ハードウェアアクセラレーション、HTTP/2・TLS・DNSのディープパースに対応し、Snortルールセットとの完全な互換性を備えている。

実負荷環境におけるアーキテクチャの比較分析

本番環境に導入するツールを選定する際、プレゼンスライドに並ぶ機能一覧よりも、実環境におけるパケットキャプチャ性能こそが重要となる。

1. マルチコアCPUへのスケーラビリティ

SuricataはAF_PACKETやPF_RINGを介してパケットの負荷を効率的に分散する。NICのRXリングバッファから各CPUコアに独立した処理スレッドが割り当てられる。一方、Snort 2.xでマルチコアを活用しようとすると、8〜16個のプロセスを個別に立ち上げ、複雑なバランサーを組み合わせる必要があり、ステートフルトラッキングが破綻しやすい。

2. レイテンシの増大とインラインIPS稼働時の通信断リスク

SPAN/ミラーポート経由でパケットをキャプチャするパッシブモード(IDS)は極めて安全だ。エンジンが高負荷に陥ってもログがドロップするだけで、サービス自体への影響はない。しかし、IPS(NFQUEUEまたはAF_PACKET IPSによるインラインモード)へと移行すると話は一変する。すべてのパケットがSuricataの検査を通過してからでなければ外部へ送出されないからだ。

ルールセットが肥大化していると、RTTレイテンシが一瞬にして2msから80msへと跳ね上がる。過去に痛感した苦い教訓として、トラフィックピークの20時台に4.8%のパケットドロップが発生した事例がある。その際、CPUやRAMのメトリクスは50%未満で正常値(グリーン)を示していた。だが、suricata --dump-counters を実行して初めて、リングバッファのオーバーフローにより capture.kernel_drops が数百万パケット規模に達していた事実に気づかされた。

Linuxサーバーにおける実践的な導入戦略

運用初日から即座にパケット遮断モードを有効にしてはならない。まずは最初の1〜2週間、AF_PACKET IDS(パッシブモード)で稼働させることが肝要だ。この期間でネットワークのベースラインを測定し、内部トラフィックのホワイトリストを整備し、誤検知(false positive)を徹底的に排除するようルールセットを調整する。ドロップ率が0.01%未満で安定稼働することを確認してから、インラインIPSモードへと切り替えるべきである。

Ubuntu ServerにおけるSuricataのインストールと設定手順

ステップ1:公式PPAから最新バージョンをインストール

Ubuntuのデフォルトリポジトリに含まれるSuricataは古い場合が多い(v6.xの多くはサポートが終了している)。OISFの公式リポジトリから最新のv7.xを導入しよう:

# 公式PPAの追加とパッケージリストの更新
sudo add-apt-repository ppa:oisf/suricata-stable -y
sudo apt update

# SuricataおよびJSONログ抽出用jqのインストール
sudo apt install suricata jq -y

ハードウェア支援関連のビルドフラグを確認する:

suricata --build-info

ステップ2:ネットワークハードウェアオフロードの無効化

現代のNIC(ネットワークカード)は、CPU負荷を軽減するために複数の小さなパケットを64KBの巨大パケットに自動結合する機能(LRO/GRO)を備えている。しかし、この動作によりIDSエンジンは標準MTUである1500バイト単位でペイロードのシグネチャを正確に検査できなくなる。メインのNICが eth0 の場合、直ちにこれらの機能を無効化する:

sudo ethtool -K eth0 rx off tx off gso off gro off

ステップ3:suricata.yamlのパラメータ最適化

メイン設定ファイルを開く:

sudo nano /etc/suricata/suricata.yaml

ネットワーク設定およびバッファ関連のブロックを編集する:

vars:
  address-groups:
    HOME_NET: "[192.168.1.0/24,10.0.0.0/8]"
    EXTERNAL_NET: "!$HOME_NET"

default-log-dir: /var/log/suricata

af-packet:
  - interface: eth0
    threads: auto          # CPUコア数に応じてスレッドを自動割り当て
    cluster-id: 99
    cluster-type: cluster_flow # 同一フロートラフィックが同一コアで処理されるよう保証
    defrag: yes
    use-mmap: yes
    tpacket-v3: yes
    ring-size: 8192        # バースト時のパケットドロップを防ぐため8192に拡張

ステップ4:suricata-updateによるルールセットの同期

アーカイブファイルを手動でダウンロードする代わりに、suricata-update コマンドを使用すれば、日々更新されるEmerging Threats(ET Open)ルールセットを自動取得できる:

# 最新のルールデータベースを更新
sudo suricata-update

# マージされたルールファイルの確認
ls -lh /var/lib/suricata/rules/suricata.rules

ステップ5:実践的なカスタムルールの作成

実際にアラートが発報されるのを確認するまでは、システムが正常に防御していると過信してはならない。カスタムルールファイルを新規作成・編集する:

sudo nano /var/lib/suricata/rules/local.rules

ICMPパケットの検知と、HTTP層での機密ファイル探索行為を検知する2つのルールを追加する:

alert icmp any any -> $HOME_NET any (msg:"SECURITY SCAN: PING test detect"; sid:1000001; rev:1;)
alert http any any -> $HOME_NET any (msg:"EXPLOIT ATTEMPT: LFI /etc/passwd in URI"; flow:established,to_server; http.uri; content:"/etc/passwd"; nocase; sid:1000002; rev:1;)

suricata.yaml の読み込み対象リストに local.rules を追加する:

rule-files:
  - suricata.rules
  - local.rules

ステップ6:構文チェックとサービスの有効化

デーモンを再起動する前に、設定ファイルを綿密に検証する:

sudo suricata -T -c /etc/suricata/suricata.yaml -v

Configuration provided was successfully parsed と表示されたら、サービスを起動・有効化する:

sudo systemctl restart suricata
sudo systemctl enable suricata

別ターミナルを開き、簡易ログをリアルタイムで監視する:

sudo tail -f /var/log/suricata/fast.log

クライアントマシンから疑似攻撃リクエストを送信する:

curl -s -A "AttackScanner/1.0" "http://<IP_SERVER>/?file=/etc/passwd"

わずか1ミリ秒後、fast.log に以下のようなアラートが記録される:

[**] [1:1000002:1] EXPLOIT ATTEMPT: LFI /etc/passwd in URI [**] [Priority: 1] {TCP} 10.0.0.15:48922 -> 10.0.0.5:80

詳細な eve.json ログから jq を使ってアラートを抽出する:

sudo tail -f /var/log/suricata/eve.json | jq 'select(.event_type=="alert") | {src: .src_ip, alert: .alert.signature, uri: .http.url}'

ステップ7:Linux NFQUEUEによるインラインIPSへの移行

ルールの精度に十分な確信が持てたら、Suricataを能動的なファイアウォールとして動作させ、攻撃を即座に遮断するよう構成できる。

ポート80宛てのトラフィックをカーネルのキュー番号0へルーティングする:

sudo iptables -I INPUT -p tcp --dport 80 -j NFQUEUE --queue-num 0

local.rules 内のアクションを alert から drop に変更する:

drop http any any -> $HOME_NET any (msg:"BLOCKED: LFI /etc/passwd"; flow:established,to_server; http.uri; content:"/etc/passwd"; nocase; sid:1000003; rev:1;)

NFQUEUEリスニングフラグを付与してSuricataを実行する:

sudo suricata -c /etc/suricata/suricata.yaml -q 0 -D

これにより、シグネチャに抵触するリクエストはすべてカーネルレベルで即座にパケット破棄される。クライアント側にはConnection ResetまたはTimeoutが返され、バックエンドのWebサーバーはCPUリソースを一切消費することなく攻撃を無力化できる。

Share: