- パスベースのポリシー判定をinode単位でキャッシュし、カーネルCPUコストを約90%削減
- キャッシュのキーはマウント名前空間ID・マウントID・inode番号の3つで構成
- ハードリンクなど複数パスが同一inodeを共有する場合はキャッシュを使わず正確性を優先
何が報告されたのか
eBPFを使ったセキュリティエージェントを開発する著者が、プロファイリングの結果をもとに性能改善の事例をブログで報告しました。コードはすでにGitHubでオープンソース化されており、記事の内容はそのリポジトリに対応しています。
このエージェントのポリシーはパスベースで、eBPFのLSMフック(カーネルのセキュリティ判定に処理を差し込む仕組み)を使ってファイルオープン時に発火します。処理の流れは、パスを再構築し、親ディレクトリのdentries(ディレクトリエントリのカーネル内部表現)をたどりながら、対象ファイルや祖先ディレクトリに一致するポリシーがあるかを確認するというものです。
プロファイリングで分かったのは、コストの大半は許可・拒否の適用そのものではなく、どのポリシーが該当するかの特定にあるということでした。同じファイルや同じサブツリーを繰り返し開く場合、この判定を毎回やり直すことになり、たとえばPostgresが/var/lib/postgres配下のファイルを繰り返し読むようなケースでは無駄が大きくなります。記事ではこの非効率な経路を「スローパス」と呼んでいます。
キャッシュの設計
対策はメモ化、つまり判定結果をキャッシュして再利用するというものです。ただしeBPFのマップに格納できる形にする必要があり、dentriesはポインタであるためそのままではマップに入りません。ポインタの内容を構造体に詰める案も検討されましたが、キーとしては重すぎると判断されています。
そこで採用されたのがinodeベースのキャッシュです。キーはマウント名前空間ID、マウントID、inode番号の3つのフィールドで構成されます。inode番号は特定のマウントツリー内でのみ一意であり、複数のマウントツリーを対象にするポリシーでは番号が重複し得るため、マウントIDとマウント名前空間IDを組み合わせて別のマウントや名前空間のキャッシュを誤って使わないようにしています。
キャッシュの値はaccess_indexとキャッシュ状態の2つです。ポリシーは空間効率のためにビットマスクとして保持されており、access_indexは該当するパスポリシーのビット位置を指します。マップの種類はLRUハッシュで、最大エントリ数は10000とされています。キャッシュにヒットすればその結果でファイルオープンを判定し、ミスした場合のみスローパスを実行して結果を格納する流れです。
性能への影響
ベンチマークでは、同じファイルを200,000回オープンして性能を計測したと報告されています。キャッシュなしでは28 billion(280億)だったカーネルサイクル数が、キャッシュありでは3.03 billion(30.3億)になりました。記事ではこれを約90%の削減と表現しています。
キャッシュがない場合、tail_call_security_checkがスタックに89.2%、is_restricted_filepathが81.9%、path_check_callbackが63.7%の割合で出現していました。キャッシュを入れると、is_restricted_filepathとpath_check_callbackはそれぞれ約0.02%まで縮小し、グラフ上では事実上見えなくなると説明されています。
計測にはperfのcycles:kイベントが使われており、これはファイルオープン時のカーネル側CPUコストを測るものです。初回のルックアップ以降はパス走査のコストがほぼ消える、というのが著者の説明です。
エッジケースと注意点
考慮すべき例外として、複数のパスが1つのinodeを共有するケースがあります。最も分かりやすい例はハードリンクで、2つの異なるパスが同じinodeを指すことになります。パスベースのポリシーでは結果が変わってしまうため、キャッシュ性能よりも正確な結果が重要だと著者は述べています。
対処は完全な解決というより回避策です。inodeはリンク数(i_nlink)を保持しており、これを読み取って1より大きい場合はそのキャッシュエントリを使わずスローパスに戻す実装になっています。キャッシュのカバレッジはその分失われますが、正確なキャッシュの方が重要だというトレードオフとして説明されています。
もう1つの特徴は、キャッシュがエージェント内部で完結している点です。ユーザーが定義するポリシーを変更しなくても高速化の恩恵が受けられると著者は述べています。
| 項目 | キャッシュなし | キャッシュあり |
|---|---|---|
| カーネルサイクル数 | 28 billion | 3.03 billion |
| is_restricted_filepathの出現割合 | 81.9% | 約0.02% |
| path_check_callbackの出現割合 | 63.7% | 約0.02% |
In our benchmark tests, we opened the same file 200,000 times to analyze performance; the cache reduced kernel cycles from 28 billion to 3.03 billion.
ベンチマークテストでは、性能を分析するために同じファイルを20万回オープンしました。キャッシュによってカーネルサイクル数は280億から30.3億に減少しました。
今後の見通し
今回の数値は「同じファイルを200,000回オープンする」という単一のベンチマークシナリオに基づくものであり、多様なワークロードでも同程度の効果が出るかは今後の検証材料次第とみられます。特に、LRUハッシュの最大エントリ数が10000に固定されているため、扱うファイル数が多い環境でキャッシュのヒット率がどこまで保たれるかが論点になりそうです。また、リンク数が1より大きいinodeをキャッシュ対象から外す設計により、ハードリンクが多い環境では削減効果が小さくなる可能性があります。公開リポジトリ上でヒット率やスキップ率の統計がどこまで取れるかが分かれば、実運用での判断材料になると考えられます。
日本の開発者・IT企業にとっての意味
カーネル空間でセキュリティ判定を行うエージェントは、機能面だけでなくオーバーヘッドの小ささが導入可否を左右します。今回の事例は、ポリシー適用そのものより「どのポリシーが該当するか」の特定にコストが偏っていたという計測結果と、それをinode単位のメモ化で解消した手順を示しており、eBPFプログラムを書く際の設計判断として参考になります。特に、eBPFマップにポインタを格納できないという制約から、キーをマウント名前空間ID・マウントID・inode番号の組み合わせに落とし込む考え方は、ファイル単位のキャッシュを自作する場合にそのまま応用できます。また、ハードリンクのように正確性が崩れる条件ではキャッシュを使わないという割り切りも、性能と正しさのトレードオフをどう線引きするかの実例です。実装がオープンソース化されているため、自社のワークロードで追試しやすい点も実務上の利点といえます。
気になる点
- このキャッシュは自分の環境でも試せますか
- コードはGitHubのbomfather/agentリポジトリで公開されています。記事ではキャッシュがエージェント内部で完結し、ユーザーのポリシー定義を変えなくても高速化すると説明されています。ただし対応するカーネルバージョンやライセンス条件については記事中に記載がないため、導入可否はリポジトリ側の情報を確認する必要があります。
- ハードリンクがあると性能はどうなりますか
- inodeのリンク数(i_nlink)が1より大きい場合、そのエントリはキャッシュに使わずスローパスに戻す実装です。正確性を優先した割り切りであり、キャッシュのカバレッジはその分小さくなります。ただし、どの程度ヒット率が落ちるかの具体的な数値は記事には示されていません。
- ポリシーの書き方を変える必要はありますか
- 不要とされています。キャッシュはエージェント内部の実装であり、利用者が定義するパスベースのポリシーを変更しなくても高速化の効果が得られると著者は説明しています。ポリシーはビットマスクとして保持され、キャッシュはそのビット位置を記録する形で連携しています。
用語解説
- eBPF
- カーネル内で安全にプログラムを実行する仕組み。Linuxの観測やセキュリティ制御に広く使われる。
- LSMフック
- Linux Security Modulesが提供する、ファイルオープンなどの処理にセキュリティ判定を差し込むための接続点。
- dentry
- ディレクトリエントリの略。カーネルがファイル名とinodeの対応を管理する内部構造で、ポインタとして扱われる。
- inode
- ファイルの実体を表すカーネル内部の構造。番号は特定のマウントツリー内でのみ一意となる。
- メモ化
- 同じ入力に対する計算結果を保存して再利用する最適化手法。今回はファイルオープン時のポリシー判定結果を保存する。
- LRUハッシュ
- 使用頻度の低い要素から順に追い出す方式のハッシュマップ。eBPFマップの一種として利用できる。
出典
Dropping eBPF CPU Cost by About 90% with Memoization (Not AI Gen)
AI導入も顧問も、お任せください
何から手をつけるか、どこまでAIに任せるか。自社の業務に合わせて整理し、導入から運用まで伴走します。相談だけでも構いません。
AI導入について相談する