eBPFトラフィ���クリダイレクト — カーネルでの宛先すり替え
Keploy、Cilium、Istioがアプリコード修正なしに���ラフィックを制御する原理
まずeBPFとは
eBPF(extended Berkeley Packet Filter)はLinuxカーネル内でユーザーが書いたプログラムを実行できる技術だ。カーネルコードを直接修正せずにカーネルの動作を拡張できる。
ネットワークパケット処理、syscall追跡、セキュリティポリシー適用に使われる。核心は「カーネル内で動く」こと。ユーザースペースより遥かにオーバーヘッドが少ない。
connect() syscallとeBPFフック
アプリがDBに接続する時に内部で起きること:
1. アプリコード:db.connect("postgres://localhost:5432")
2. ラ���ブラリ(pg、psycopg2):socket() → connect() syscall呼び出し
3. カーネル:TCP SYN → SYN-ACK → ACK(3ウェイハンドシェイ��)
4. PostgreSQLワイヤプロトコル通��開始
eBPFは2〜3の間にフックをかける。connect() syscallが実行される時点で宛先アドレスを確認し、必要なら別のアドレスにすり替える。
アプリから見ればpostgres:5432に接続した。実際のTCPパケットは127.0.0.1:16789(プロキシ)に行く。アプリはこの事実を知らない。
eBPFプログラムタイプ別フックポイント
SockOps(BPF_PROG_TYPE_SOCK_OPS) — TCPイベント(接続確立、切断等)を監視。cgroupに付着して特定コンテナのTCPイベントだけ選別可能。
connect4/connect6(BPF_CGROUP_INET4_CONNECT) — connect() syscall進入時点で宛先IP:Portを直接修正。Keployが使うコアフックがこれ。
TC(Traffic Control) — ネットワークインターフェースに付着。パケット単位でフィルタリング/リダイレクト。CiliumがPod間トラフィック制御に使用。
XDP(eXpress Data Path) — NICでパケットがカーネルネットワークスタックに入る前に処理。最速だが制約も大きい。
自分で確認する方法
# 1. connect() syscall追跡
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_connect {
printf("%s[%d] → connect()\n", comm, pid);
}'
# 2. TCP接続先の確認
sudo bpftrace -e 'kprobe:tcp_v4_connect {
printf("%s → %s:%d\n", comm,
ntop(((struct sockaddr_in *)arg1)->sin_addr.s_addr),
ntohs(((struct sockaddr_in *)arg1)->sin_port));
}'
Keployを起動した状態でアプリを実行すると、connect()の宛先がpostgres:5432��はなく127.0.0.1:プロキシポートに変わるのが見える。
Keployで簡単テスト
curl -O -L https://keploy.io/install.sh && source install.sh
keploy record -c "docker compose up" --container-name my-app
keploy test -c "docker compose up" --container-name my-app
macOSではDocker Desktop上でのみ動作。eBPFはLinuxカーネル機能なので。
動作フロー
eBPFプログラムがカーネルにロード — connect4フックでconnect() syscallを傍受
アプリがconnect(postgres:5432)呼び出し → eBPFが宛先をプロキシ(127.0.0.1:proxyPort)に修正
プロキシが本来の宛先に中継しながら録画(録画)または録画済み応答を返却(再生)
アプリは宛先変更を知らない — カーネルレベル透過リダイレクト