
eBPF LSM القائم على التحكم الإلزامي بالوصول وjailer
تحكم وصول إلزامي (Mandatory Access Control) مبني على eBPF لنظام Linux.
هذا المشروع إعادة كتابة كاملة لـ BpfJailer مغلق المصدر وهو تجريبي بالكامل. يستفيد من ميزات أحدث مثل bpf arena لم تكن متاحة عندما كُتب BpfJailer الداخلي. الأخطاء متوقعة وليست مؤهلة للحصول على مكافأة الأخطاء (bug bounty) أو تُعتبر نتائج أمنية. بمجرد تقييمه بشكل صحيح سيحل محل النسخة الداخلية مغلقة المصدر.
يستخدم 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. لا يُصدر clang 20 و21 أي addr_space_cast لـ __builtin_memcpy من ذاكرة arena، لذا يرفض المدقق (verifier) الـ jailer الذي يبنيانه.CONFIG_BTRFS_FS=y بدلاً من وحدة نمطية. تقرأ برامج BPF أنواع inode الخاصة بـ btrfs من vmlinux.h، الذي يولّده Makefile من BTF الخاص بالنواة قيد التشغيل.protoc، اللذان توفرهما Debian وUbuntu باسم libprotobuf-dev وprotobuf-compiler.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 إلا داخل جهاز افتراضي قابل للتخلص منه.
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.
bpfjctl wrap بدون --drop-cap ومع --uid غير جذري يترك الأمر
قادرًا على إزالة نفسه من السجن. راجع bpfjctl wrap --help.
شغّل sudo build/bpfjlog أثناء ارتباط الـ jailer لمراقبته. تُكتب
تشخيصات BPF إلى stderr والأحداث المهيكلة إلى stdout. يعيد المسجّل
الاتصال تلقائيًا عندما يستبدل replace مجموعة جديدة من الخرائط المثبّتة.
يحمّل replace jailer ثانيًا كاملًا بجانب النشط، ويرحّل عضوية الـ pod
والمتغيرات وملكية الموارد المتتبَّعة، ثم يبدّل شجرتي التثبيت ذريًا. تبقى
الشجرتان مرتبطتين أثناء التسليم، وتُنسَّق عمليات fork والتسجيل مع الترحيل،
وتُسجَّل تغييرات الملكية وتُعاد. يفشل الاستبدال مغلقًا إذا كانت إصدارات
التخطيط المحفوظة غير متوافقة أو تعذّر نسخ الحالة بأمان.
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