
eBPF LSM القائم على التحكم الإلزامي بالوصول وjailer
التحكم الإلزامي للوصول (MAC) المبني على eBPF لنظام Linux.
هذا المشروع هو إعادة كتابة كاملة لـ BpfJailer مغلق المصدر وهو تجريبي بالكامل. يستفيد من ميزات أحدث مثل bpf arena التي لم تكن متاحة عند كتابة BpfJailer الداخلي. الأخطاء متوقعة ولا تكون مؤهلة لمكافأة الأخطاء أو تُعتبر نتائج أمنية. بمجرد تقييمه بشكل صحيح سيحل محل النسخة الداخلية مغلقة المصدر.
يستخدم BpfJailer برامج eBPF LSM لوضع العمليات في سجون، تُسمى pods، كل منها مرتبط بدور من سياسة TOML. يتم توريث الـ pod عبر fork و exec. ميزات السياسة الاختيارية:
kill و ptrace — الأدوار/الـ pods المستهدفة التي يمكن الإشارة إليها أو الارتباط بها.bpf — خرائط وبرامج eBPF الخاصة بأي أدوار يمكن للدور فتحها، أو ما إذا كان يمكنه استدعاء bpf(2) على الإطلاق.keyring — حلقات مفاتيح fs-verity الخاصة بأي أدوار يمكن للدور إضافة شهادات إليها، أو ما إذا كان يمكنه كتابة حلقات المفاتيح على الإطلاق.تُكتب حالات الرفض وأحداث دورة الحياة إلى ring buffers مثبتة. يطبع bpfjlog تشخيصات BPF القابلة للقراءة البشرية والأحداث المهيكلة ويتابع ring buffers عبر استبدال سياسة حي.
يمكن لثنائي المطالبة بدور عبر xattr الخاص بـ user.bpfj.policy.exec ويتم تسجيله فيه وقت التنفيذ. يمكن أيضًا تسجيل العمليات قيد التشغيل مباشرة، ويمكن لعملية غير مميزة تسجيل نفسها عبر bpfjsrv/bpfjclient.
| الدليل | الثنائي | الغرض |
|---|---|---|
bpfj/ | المكتبة الأساسية وبرامج BPF: jailer، enforcers، محلل السياسة، مساعدات libbpf C++. | |
ctl/ | bpfjctl | أداة عامة الغرض لربط وإعادة تحميل وفحص وفصل الـ jailer، ولتسجيل العمليات. |
cmd/ | bpfjcmd | bpfjctl مع وسائطه، واختياريًا سياسته، مُجمّعة داخله. يتجاهل argv، لذا يمكن ربطه بشكل ثابت وتوقيعه بـ fs-verity كوحدة واحدة. |
srv/ | bpfjsrv | خادم مُفعّل بالمقبس يسجّل المتصلين غير المميزين في الأدوار التي تسمح بذلك. |
client/ | bpfjclient | عميل بسيط لـ bpfjsrv، بدون أي اعتماد على libbpf أو سلسلة أدوات BPF. |
log/ | bpfjlog | مستهلك لـ ring buffers التشخيصية والأحداث المهيكلة المثبتة. |
tests/ | bpfjtest | مجموعة الاختبارات. |
CONFIG_BPF_LSM=y و bpf في معامل الإقلاع lsm=). يتم اختبار BpfJailer فقط على 6.16+، ولا يتم دعم النوى الأقدم.bpftool، ومترجم C++20.openssl و fsverity و setfattr، بالإضافة إلى الأرشيفات الثابتة المدرجة في Makefile (STATIC=1).قم بتعيين LIBBPF_CFLAGS / LIBBPF_LIBS إذا لم يتمكن pkg-config من العثور على libbpf. وجّه LIBARENA إلى نسخة libarena المستخرجة في كل بناء:
كل make أدناه يحتاج أيضًا إلى LIBARENA (انظر المتطلبات)، معيّنًا على سطر الأوامر أو مُصدَّرًا في البيئة.
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، لأن كل واحد منها ينشئ mount namespace ويركّب bpffs. يبني make test كمستخدم الاستدعاء ويشغّل فقط ثنائي الاختبار تحت sudo.
تعمل الاختبارات بشكل تسلسلي افتراضيًا لأن فصل BPF LSM المتزامن يمكن أن يتسبب في panic للنوى المتأثرة. استخدم make test TEST_ARGS=Suite.Test لحالة مركزة، ولا تشترك في -j N أو BPFJTEST_JOBS=N إلا داخل VM يمكن التخلص منه.
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 USER_ID -- CMD # run CMD in a new pod
sudo bpfjctl enroll ROLE USER_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.
bpfjctl wrap بدون --drop-cap و --uid غير جذري يترك الأمر قادرًا على إزالة نفسه من السجن. انظر bpfjctl wrap --help.
شغّل sudo build/bpfjlog أثناء ربط الـ jailer لمراقبته. تُكتب تشخيصات BPF إلى stderr والأحداث المهيكلة إلى stdout. يعيد المسجّل الاتصال تلقائيًا عندما يستبدل replace مجموعة جديدة من الخرائط المثبتة.
يقوم replace بتحميل jailer ثانٍ كامل بجانب النشط، ويهاجر عضوية الـ pod والمتغيرات وملكية الموارد المتتبعة، ثم يبدّل أشجار التثبيت بشكل ذري. تبقى كلتا الشجرتين مرتبطتين أثناء التسليم، ويتم تنسيق forks والتسجيل مع الترحيل، ويتم تسجيل تغييرات الملكية وإعادة تشغيلها. يفشل الاستبدال بشكل مغلق إذا كانت إصدارات التخطيط المحفوظة غير متوافقة أو لا يمكن نسخ الحالة بأمان.
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