
eBPF LSMベースの強制アクセス制御とジェイラー
Linux向けのeBPFベースの強制アクセス制御。
このプロジェクトはクローズドソースのBpfJailerの完全な書き直しであり、 完全に実験的なものです。内部のBpfJailerが書かれた当時は利用できなかった bpf arenaのような新しい機能を活用しています。問題は想定されており、 バグバウンティの対象外であり、セキュリティ上の発見とは見なされません。 適切に評価された後、内部のクローズドソース版を置き換える予定です。
BpfJailerはeBPF LSMプログラムを使用して、プロセスをポッドと呼ばれるジェイルに配置し、
それぞれがTOMLポリシーのロールにバインドされます。ポッドはforkとexecを
通じて継承されます。オプションのポリシー機能:
killとptrace — シグナル送信やアタッチが可能な対象のロール/ポッド。bpf — あるロールが開くことができる他のロールのeBPFマップとプログラム、
またはbpf(2)を呼び出せるかどうか。keyring — あるロールが証明書を追加できる他のロールのfs-verityキーリング、
またはキーリングに書き込めるかどうか。拒否とライフサイクルイベントはピン留めされたリングバッファに書き込まれます。bpfjlog
は人間が読めるBPF診断と構造化イベントを出力し、ライブポリシー置換をまたいで
リングバッファを追跡します。
バイナリはuser.bpfj.policy.exec xattrを通じてロールを要求でき、exec時に
そのロールに登録されます。実行中のプロセスも直接登録でき、非特権プロセスは
bpfjsrv/bpfjclientを通じて自身を登録できます。
| ディレクトリ | バイナリ | 目的 |
|---|---|---|
bpfj/ | コアライブラリとBPFプログラム: ジェイラー、エンフォーサー、ポリシーパーサー、libbpf C++ヘルパー。 | |
ctl/ | bpfjctl | ジェイラーのアタッチ、リロード、検査、デタッチ、およびプロセスの登録のための汎用ツール。 |
cmd/ | bpfjcmd | 引数、およびオプションでポリシーをコンパイル時に組み込んだbpfjctl。argvを無視するため、静的にリンクして単一ユニットとしてfs-verity署名できます。 |
srv/ | bpfjsrv | それを許可するロールに非特権呼び出し元を登録する、ソケット起動型サーバー。 |
client/ | bpfjclient | bpfjsrv用の最小限のクライアントで、libbpfやBPFツールチェーンへの依存がありません。 |
log/ | bpfjlog | ピン留めされた診断および構造化イベントリングバッファのコンシューマー。 |
tests/ | bpfjtest | テストスイート。 |
CONFIG_BPF_LSM=yおよびlsm=ブートパラメータに
bpf)。BpfJailerは6.16以降でのみテストされており、古いカーネルは
サポートされていません。bpftool、およびC++20コンパイラ。clang 20
と21はarenaメモリからの__builtin_memcpyに対してaddr_space_castを出力しないため、
それらがビルドするジェイラーはベリファイアに拒否されます。CONFIG_BTRFS_FS=y。BPFプログラムは
vmlinux.hからbtrfs inodeタイプを読み取り、Makefileは実行中のカーネルのBTFから
これを生成します。protoc。DebianとUbuntuではlibprotobuf-devおよび
protobuf-compilerとして提供されています。openssl、fsverity、setfattr、およびMakefileに
記載されている静的アーカイブ(STATIC=1)。pkg-configがlibbpfを見つけられない場合はLIBBPF_CFLAGS / LIBBPF_LIBSを設定してください。
すべてのビルドでLIBARENAをlibarenaのチェックアウト先に指定してください:
以下の各 make でも LIBARENA(Requirements を参照)が必要であり、コマンドラインで指定するか環境にエクスポートしてください。
make # build/bpfjctl
make STATIC=1 # bpfjctl with no shared object dependencies
make client # build/bpfjclient, no BPF toolchain needed
make log # build/bpfjlog
make signing-key # generate a development signing key and certificate
make signed SIGNING_KEY=... SIGNING_CERT=... # static, fs-verity signed bpfjctl
make srv SIGNING_KEY=... SIGNING_CERT=... # static, signed bpfjsrv
make cmd SIGNING_KEY=... SIGNING_CERT=... \
CMD_ARGS="replace-compiled" CMD_POLICY=policy.toml CMD_ROLE=bpfjailer
make clean
すべての出力は build/ の下に生成されます。別の場所にビルドするには BUILD= を設定してください。例えば make BUILD=build-asan SANITIZE=address,undefined のようにします。
make test
テストは root として実行する必要があります。各テストがマウント名前空間を作成し、bpffs をマウントするためです。make test は実行ユーザーとしてビルドし、sudo の下でテストバイナリのみを実行します。
テストはデフォルトで直列実行されます。並行して BPF LSM をデタッチすると、影響を受けるカーネルがパニックする可能性があるためです。特定のケースには make test TEST_ARGS=Suite.Test を使用し、使い捨ての VM 内でのみ -j N または BPFJTEST_JOBS=N を指定してください。
sudo bpfjctl check policy.toml # parse a policy and report what it holds
sudo bpfjctl attach policy.toml # load and pin the jailer
sudo bpfjctl replace policy.toml # reload without releasing jailed tasks
sudo bpfjctl wrap ROLE POD_ID -- CMD # run CMD in a new pod
sudo bpfjctl enroll ROLE POD_ID PID [NAME=VALUE...] # enroll with variables
sudo bpfjctl show PID # pods a process is in
sudo bpfjctl list # every pod and its processes
sudo bpfjctl detach # unpin and unload
プログラムはデフォルトで /sys/fs/bpf/bpfj-pins にピン留めされます。これを変更するには
--bpffs-path と --pin-dir を使用します。プログラムは detach が実行されるまで
ロードされたままになります。
--drop-cap と非 root の --uid を指定せずに bpfjctl wrap を実行すると、
コマンドが自身を jail から削除できる状態のままになります。bpfjctl wrap --help を参照してください。
jailer がアタッチされている間に sudo build/bpfjlog を実行して観察します。BPF
診断情報は stderr に、構造化イベントは stdout に書き込まれます。ロガーは
replace が新しいピン留めマップのセットを差し替えると自動的に再接続します。
replace はアクティブな jailer の隣に完全な2つ目の jailer をロードし、ポッドの
メンバーシップ、変数、追跡対象のリソース所有権を移行した後、ピンツリーを
アトミックにスワップします。ハンドオフ中は両方のツリーがアタッチされたままで、
フォークと登録は移行と協調して行われ、所有権の変更はジャーナルに記録され
リプレイされます。永続化されたレイアウトのバージョンに互換性がない場合や、
状態を安全にコピーできない場合、置換はフェイルクローズします。
base-role = "floor" # optional: enroll every process on the host
vars = ["vm_uuid"] # known variable names
[certs]
corp-ca = "MIIDXTCCAkWgAwIBAgIJAK..." # PEM or base64 DER certificate
[roles.floor]
any = true # open tracking-only base role
[roles.webserver]
enforce-binary-certs = ["corp-ca"] # execs must be signed by one of these
kill-roles = ["floor"] # may signal its own pod, plus these roles
ptrace-pod = true # its own pod only
proc-roles = ["floor"] # may open proc files for these roles
bpf-pod = true # only BPF objects from its own pod
lkm-any = false # deny module and kexec loading
mq-sysv-pod = true # only SysV queues from its own pod
mq-posix-pod = true # only POSIX queues from its own pod
shm-sysv-pod = true # only SysV SHM from its own pod
shm-posix-pod = true # only POSIX SHM from its own pod
keyring-own = true # only its own role's keyring
[[roles.webserver.mq-posix-pattern]]
name = "/service-${vm_uuid}-*"
allow = true
[[roles.webserver.shm-posix-pattern]]
name = "/service-${vm_uuid}-*"
allow = true
[[roles.webserver.exec-paths]]
path = "/usr/bin/webserver"
allow = true
permissions = ["exec"]
[[roles.webserver.exec-paths]]
path = "/usr/lib"
allow = true
permissions = ["shared-object"]
[[roles.webserver.paths]] # cached path policy
path = "/"
allow = false
[[roles.webserver.paths]]
path = "/usr"
allow = true
access = "read-only"
[[roles.webserver.paths]]
path = "/etc"
allow = true
access = "read-only"
[[roles.webserver.paths]]
path = "/srv/web"
allow = true
access = "read-write"
[[roles.webserver.unix-bind]] # pathname bind rule
path = "/run/webserver"
allow = true
[[roles.webserver.unix-connect]]
name = "@control-${vm_uuid}"
allow = true
[[roles.webserver.unix-dgram]]
path = "/dev/log"
allow = true
[[roles.webserver.mount]] # destination and permitted filesystem types
path = "/srv/data"
allow = true
filesystems = ["ext4", "xfs"]
[[roles.webserver.mount]]
path = "/run/webserver"
allow = true
filesystems = ["any"] # every filesystem type at this destination
[[roles.webserver.umount]]
path = "/"
allow = false
[[roles.webserver.umount]]
path = "/srv/data"
allow = true
[roles.sandbox]
unpriv-enroll = true # every unspecified operation remains denied
override-stacked = true # answers alone, ignoring roles stacked below
ほとんどの操作ゲートは、ロールに対応するオプションがない場合に拒否されます。*-pod オプションは同じ pod からのリソースを許可し、*-roles は指定された所有者ロールを追加し、*-any はその操作を完全に開放します。keyring-own はロールスコープの対応物です。fs-verity キーリングは pod ではなくロールに属するためです。enroll-roles は bpfjsrv が追加できる唯一のロールを指定します。これがない場合、bpfjsrv 経由の登録は拒否されます。Unix パス名、マウント、アンマウントの各操作は、対応するオプションがないか、どのパスにも一致しない場合に拒否されます。抽象 Unix ソケット名はオプトインのフィルタのままであるため、未設定または一致しない抽象名は許可されます。
完全に開放された proc オプションは proc-any で、他の所有権ファミリーと同じ *-any の順序に従います。