Linux上のBGP FlowspecとExaBGPでDDoS攻撃を緩和:ルーティング層でのパケットフィルタリング自動化

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

ファイアウォールが大容量トラフィックに圧倒されるとき

前四半期、私たちのプライマリAPIクラスターが、公開されていたDNS(ポート53)およびCLDAP(ポート389)リゾルバを悪用した猛烈なUDPリフレクション攻撃を受けました。わずか15秒足らずで、インバウンドトラフィックは通常時の2.4 Gbpsから48 Gbpsへと跳ね上がり、ピーク時には毎秒約3,600万パケット(Mpps)に達しました。アップストリームのトランジット回線である100 GbEポートには十分な帯域の空きがありましたが、Linuxエッジサーバーは瞬く間にダウンしてしまいました。

ボトルネックとなったのは帯域幅ではありませんでした。真の致命傷となったのは、パケットレート(PPS)の膨大さです。受信パケットごとにLinux netfilterフレームワークがステートフルなコネクショントラッキングテーブルを探索せざるを得ませんでした。数秒のうちにksoftirqdがすべてのCPUコアを100%に張り付かせ、壊滅的なリングバッファのパケットドロップを引き起こしました。正当なTLSハンドシェイクは停止し、フリート全体のヘルスチェックが相次いで失敗、ステータスページは赤色に染まりました。

従来の復旧プレイブックでは、状況を悪化させるだけでした。

  • リモートトリガーブラックホール(RTBH: Remotely Triggered Black Hole):no-exportやトランジットプロバイダーのブラックホールコミュニティ(例: 65535:666)を付与した/32ルートを広報すれば、フラッド攻撃を破棄できます。しかし残念ながら、そのIPアドレス宛ての正当な顧客トラフィックもすべて遮断されてしまいます。これでは攻撃者の目的を難なく達成させてしまいます。
  • ファイアウォールへのハードウェア増強:より大規模なファイアウォールアプライアンスの購入や、クラスタノードのスペック強化は運用予算を圧迫します。さらに悪いことに、ステートフルアプライアンスであっても、SYNフラッドやUDPフラッドによって接続ステートテーブルが枯渇すれば、結局はダウンしてしまいます。

根本原因:サーバーのステートフル処理オーバーヘッド vs ルーターのシリコン

Linuxソフトウェアファイアウォールは、ホストのネットワーキングスタック深部でパケットを検査します。XDP(eBPF)のようなファストパスバイパス技術を用いたとしても、ラインカードやToRスイッチのアップリンクが生のワイヤスピードフラッドで埋め尽くされてしまえば対処は困難です。

一方、エッジルーターやコアルーターの処理方法は根本的に異なります。Juniper MXシリーズ、Cisco ASR 9000、Arista 7280Rといったプラットフォームは、専用のTCAM(Ternary Content-Addressable Memory)とカスタムASICを備えています。これらのASICはパケットヘッダーをワイヤレートで同時に評価可能です。毎秒5,000万パケットをフィルタリングしたとしても、追加のCPUサイクル消費はゼロです。

長年の運用上の課題は、その管理手法にありました。深夜3時のインシデント発生時に、エンジニアがコアルーターへ手動でアクセスリストを入力するわけにはいきません。また、従来の標準BGPによる広報はネットワークの到達性プレフィックスのみを交換するため、レイヤー4のポート、プロトコル、パケットサイズを指定できず、ここでも役に立ちません。テレメトリと異常検知はLinuxサーバー側で動作しているにもかかわらず、フラッドを阻止できるハードウェアはアップストリーム側に存在するのです。

各アプローチの比較検討

ホストレベルでの検知とアップストリームのルーティングハードウェアを連携させる手法は、主に以下の3つに集約されます。

1. ユニキャストBGP RTBH(RFC 3882)

RTBHは、対象IP宛てのトラフィックをNullインターフェースへ向けます。設定自体はいくつかのBGPコミュニティタグを付与するだけで完了します。しかし、宛先が/32単位という粗い粒度しか指定できないのが欠点です。単純なDNSアンプ攻撃によって、マルチテナントのHTTPSエンドポイント全体がオフラインとなり、100%の巻き添え被害を受けることになります。

2. アウトオブバンド自動化(NETCONF / RESTCONF / SSH)

自動化スクリプトからSSHセッションを開いたり、NETCONFペイロードを送信したりして、オンデマンドでルーターのACLを生成する手法です。機能はするものの、マルチベンダー環境では脆さが露呈します。ベンダーごとに構文が異なり、コミットロックによって緊急更新がブロックされる可能性や、攻撃の最中に未処理のAPIエラーが発生してフィルタリングが中途半端に適用されるリスクがあります。

3. BGP Flowspec(RFC 8955 / RFC 5575)

BGP Flowspec(AFI 1、SAFI 133)は、ファイアウォールルールを標準的なBGPルーティングアップデートとして扱うことで、粒度の問題を解決します。送信元/宛先サブネット、レイヤー4プロトコル、ポート範囲、ICMPコード、パケット長などの複雑なマッチ条件を、破棄、レートリミット、VRFリダイレクトなどの直接的なアクションと組み合わせて配布できます。ルールは既存のBGPピアリングを経由し、ミリ秒単位でネットワークトポロジ全体へ展開されます。

ExaBGPを用いた自動Flowspecコントローラーの構築

ExaBGPは、LinuxアプリケーションとBGP対応ネットワーク機器の橋渡しをするソフトウェアエンジンとして機能します。Linuxカーネルのルーティングテーブルを変更することはありません。その代わり、標準入力やFIFOからのプレーンテキストコマンドをBGPアップデートに変換し、エッジルーターへ直接送信します。

ステップ1: ExaBGPのインストール

監視用またはトラフィック分析用のサーバーにExaBGPをデプロイします。Ubuntu 22.04 LTSやDebian 12には安定版パッケージが提供されているほか、pipから最新リリースを取得することも可能です。

sudo apt-get update
sudo apt-get install -y python3-pip exabgp
# またはpip経由で直接インストール
pip3 install exabgp

ステップ2: 動的ルール注入用の名前付きパイプを設定

名前付きパイプ(FIFO)を使用することで、バックグラウンドの検知スクリプトからセッションを切断することなく、実行中のExaBGPプロセスへ即座にルールを送信できます。

sudo mkdir -p /etc/exabgp/run
sudo mkfifo /etc/exabgp/run/exabgp.in
sudo mkfifo /etc/exabgp/run/exabgp.out
sudo chmod 600 /etc/exabgp/run/exabgp.*

ステップ3: ピアリング設定の定義

以下の設定を/etc/exabgp/exabgp.confとして保存します。この構成では、Linuxコントローラー(192.0.2.10、AS 65001)がアップストリームルーター(192.0.2.1、AS 65001)と内部BGPセッションを確立します。

process inject-flowspec {
    run /usr/bin/cat /etc/exabgp/run/exabgp.in;
    encoder text;
}

neighbor 192.0.2.1 {
    router-id 192.0.2.10;
    local-address 192.0.2.10;
    local-as 65001;
    peer-as 65001;

    family {
        ipv4 flow;
    }

    api {
        processes [ inject-flowspec ];
    }
}

ステップ4: BGPセッションの確立

アップストリームルーター側でFlowspecのアドレスファミリ(Cisco IOS-XRのaddress-family ipv4 flow-specやJuniper Junosのfamily inet flowなど)が許可されていることを確認してください。ExaBGPをフォアグラウンドで起動し、セッションが正常に初期化されることを確認します。

exabgp /etc/exabgp/exabgp.conf

BGP OPENのネゴシエーションと、ステータスがESTABLISHEDへ遷移することを確認してください。

ステップ5: 標的型緩和ルールの注入

自動監視システムが、WebクラスターのIPアドレス198.51.100.50を標的とした25 MppsのDNSアンプ攻撃を検知したとします。対象の攻撃ベクトルのみをドロップする破棄ルールをFIFOへパイプすることで、攻撃トラフィックだけを排除できます。

echo "announce flow route destination 198.51.100.50/32 source-port [ =53 ] protocol udp discard" > /etc/exabgp/run/exabgp.in

ルーターは1秒未満でアップデートを受信します。ASICが即座に対象ホスト宛ての送信元ポート53のUDPパケットをマッチングし、ハードウェア側で破棄します。一方で、ポート443の正常なHTTPSトラフィックは影響を受けることなく通過し続けます。

ステップ6: 的確なレートリミットの自動化

突発的なアクセス集中と分散型フラッド攻撃の判別が難しい場合、パケットを完全にドロップすることが常に最善の初動とは限りません。そのような場合は、疑わしいトラフィックを帯域制限できます。RFC 8955では、レートリミットをバイト/秒単位で定義しています。例えば、125,000バイト/秒は1 Mbpsの帯域制限を適用します。

以下は、FastNetMon、Prometheus Alertmanager、またはカスタムsFlowコレクターなどのアラートと連携するPythonデーモンの実装例です。

#!/usr/bin/env python3
import sys

def inject_flowspec_ratelimit(victim_ip: str, port: int, rate_bps: int):
    pipe_path = "/etc/exabgp/run/exabgp.in"
    # rate_bps はバイト/秒単位: 125,000 bytes/s ≒ 1 Mbps
    rule = (
        f"announce flow route destination {victim_ip}/32 "
        f"port [ ={port} ] protocol tcp "
        f"rate-limit {rate_bps}\n"
    )
    try:
        with open(pipe_path, "w") as fifo:
            fifo.write(rule)
            fifo.flush()
        print(f"[+] {victim_ip}:{port} に {rate_bps} B/s のレートリミットを適用しました")
    except BrokenPipeError:
        print("[-] エラー: ExaBGP FIFO リスナーがアクティブではありません", file=sys.stderr)

if __name__ == "__main__":
    # ポート80を標的とする異常トラフィックを1 Mbpsに制限
    inject_flowspec_ratelimit("198.51.100.50", 80, 125000)

テレメトリによりトラフィックレベルが安定したことが確認できたら、ルートを撤回してTCAMスロットを解放します。

echo "withdraw flow route destination 198.51.100.50/32 port [ =80 ] protocol tcp" > /etc/exabgp/run/exabgp.in

ルーティングハードウェアは、マルチギガビット単位の無駄なトラフィックをワイヤレートで破棄するために専用設計されています。ExaBGPを通じてFlowspecフィルターを展開することで、サーバーにかかる大量のパケットフィルタリング負荷を排除し、インフラストラクチャに到達する前の境界線で攻撃トラフィックを阻止できます。

Share: