کتاب "BPF برائے لینکس مانیٹرنگ"

کتاب "BPF برائے لینکس مانیٹرنگ"ہیلو، خبر کے رہنے والوں! BPF ورچوئل مشین لینکس کرنل کے سب سے اہم اجزاء میں سے ایک ہے۔ اس کا صحیح استعمال سسٹم انجینئرز کو خامیاں تلاش کرنے اور انتہائی پیچیدہ مسائل کو حل کرنے کی اجازت دے گا۔ آپ سیکھیں گے کہ ایسے پروگرام کیسے لکھیں جو دانا کے رویے کی نگرانی اور اس میں ترمیم کرتے ہیں، دانا میں ہونے والے واقعات کی نگرانی کے لیے کوڈ کو محفوظ طریقے سے کیسے نافذ کیا جائے، اور بہت کچھ۔ David Calavera اور Lorenzo Fontana BPF کی طاقت کو غیر مقفل کرنے میں آپ کی مدد کریں گے۔ کارکردگی کی اصلاح، نیٹ ورکنگ، سیکورٹی کے بارے میں اپنے علم کو وسعت دیں۔ - لینکس کرنل کے رویے کی نگرانی اور اس میں ترمیم کرنے کے لیے BPF کا استعمال کریں۔ - دانا کو دوبارہ کمپائل کیے یا سسٹم کو ریبوٹ کیے بغیر کرنل ایونٹس کی محفوظ طریقے سے نگرانی کرنے کے لیے کوڈ کو انجیکشن کریں۔ - C، Go یا Python میں آسان کوڈ کی مثالیں استعمال کریں۔ - BPF پروگرام لائف سائیکل کے مالک ہو کر کنٹرول حاصل کریں۔

لینکس کرنل سیکورٹی، اس کی خصوصیات اور Seccomp

BPF استحکام، سیکورٹی، یا رفتار کی قربانی کے بغیر دانا کو بڑھانے کا ایک طاقتور طریقہ فراہم کرتا ہے۔ اس وجہ سے، کرنل ڈویلپرز نے سوچا کہ BPF پروگراموں کے ذریعے سپورٹ کردہ Seccomp فلٹرز کو لاگو کر کے Seccomp میں عمل کی تنہائی کو بہتر بنانے کے لیے اس کی استعداد کو استعمال کرنا اچھا خیال ہو گا، جسے Seccomp BPF بھی کہا جاتا ہے۔ اس باب میں ہم وضاحت کریں گے کہ Seccomp کیا ہے اور اسے کیسے استعمال کیا جاتا ہے۔ پھر آپ سیکھیں گے کہ BPF پروگراموں کا استعمال کرتے ہوئے Seccomp فلٹرز کیسے لکھتے ہیں۔ اس کے بعد، ہم بلٹ ان BPF ہکس کو دیکھیں گے جو لینکس سیکیورٹی ماڈیولز کے لیے کرنل میں شامل ہیں۔

لینکس سیکیورٹی ماڈیولز (ایل ایس ایم) ایک ایسا فریم ورک ہے جو فنکشنز کا ایک سیٹ فراہم کرتا ہے جو مختلف سیکیورٹی ماڈلز کو معیاری انداز میں نافذ کرنے کے لیے استعمال کیا جا سکتا ہے۔ LSM براہ راست کرنل سورس ٹری میں استعمال کیا جا سکتا ہے، جیسے Apparmor، SELinux اور Tomoyo۔

آئیے لینکس کی صلاحیتوں پر بات کرتے ہوئے شروع کرتے ہیں۔

صلاحیتوں

لینکس کی صلاحیتوں کا نچوڑ یہ ہے کہ آپ کو کسی خاص کام کو انجام دینے کے لیے ایک غیر مراعات یافتہ عمل کی اجازت دینے کی ضرورت ہے، لیکن اس مقصد کے لیے سوڈ استعمال کیے بغیر، یا دوسری صورت میں اس عمل کو مراعات یافتہ بنانا، حملے کے امکان کو کم کرنا اور عمل کو کچھ کام انجام دینے کی اجازت دینا۔ مثال کے طور پر، اگر آپ کی ایپلیکیشن کو مراعات یافتہ پورٹ کھولنے کی ضرورت ہے، تو 80 بولیں، عمل کو روٹ کے طور پر چلانے کے بجائے، آپ اسے CAP_NET_BIND_SERVICE کی صلاحیت دے سکتے ہیں۔

main.go نام کے ایک گو پروگرام پر غور کریں:

package main
import (
            "net/http"
            "log"
)
func main() {
     log.Fatalf("%v", http.ListenAndServe(":80", nil))
}

یہ پروگرام پورٹ 80 پر ایک HTTP سرور پیش کرتا ہے (یہ ایک مراعات یافتہ بندرگاہ ہے)۔ عام طور پر ہم اسے تالیف کے فوراً بعد چلاتے ہیں:

$ go build -o capabilities main.go
$ ./capabilities

تاہم، چونکہ ہم روٹ مراعات نہیں دے رہے ہیں، اس لیے یہ کوڈ پورٹ کو بائنڈنگ کرتے وقت ایک غلطی پھینک دے گا:

2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1

capsh (شیل مینیجر) ایک ایسا ٹول ہے جو صلاحیتوں کے مخصوص سیٹ کے ساتھ شیل چلاتا ہے۔

اس صورت میں، جیسا کہ پہلے ہی بتایا جا چکا ہے، مکمل روٹ رائٹس دینے کے بجائے، آپ پروگرام میں پہلے سے موجود تمام چیزوں کے ساتھ cap_net_bind_service کی اہلیت فراہم کر کے مراعات یافتہ پورٹ بائنڈنگ کو فعال کر سکتے ہیں۔ ایسا کرنے کے لیے، ہم اپنے پروگرام کو capsh میں بند کر سکتے ہیں:

# capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' 
   --keep=1 --user="nobody" 
   --addamb=cap_net_bind_service -- -c "./capabilities"

آئیے اس ٹیم کو تھوڑا سمجھیں۔

  • capsh - کیپش کو شیل کے طور پر استعمال کریں۔
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - چونکہ ہمیں صارف کو تبدیل کرنے کی ضرورت ہے (ہم روٹ کے طور پر نہیں چلنا چاہتے ہیں)، ہم cap_net_bind_service اور اصل میں صارف ID کو تبدیل کرنے کی صلاحیت کی وضاحت کریں گے۔ root to nobody، یعنی cap_setuid اور cap_setgid۔
  • —keep=1 — ہم روٹ اکاؤنٹ سے سوئچ کرتے وقت انسٹال شدہ صلاحیتوں کو برقرار رکھنا چاہتے ہیں۔
  • صارف = "کوئی نہیں" - پروگرام چلانے والا آخری صارف کوئی نہیں ہوگا۔
  • —addamb=cap_net_bind_service — روٹ موڈ سے سوئچ کرنے کے بعد متعلقہ صلاحیتوں کی کلیئرنگ سیٹ کریں۔
  • - -c "./capabilities" - بس پروگرام چلائیں۔

منسلک صلاحیتیں ایک خاص قسم کی صلاحیتیں ہیں جو بچوں کے پروگراموں کو وراثت میں ملتی ہیں جب موجودہ پروگرام انہیں execve() کا استعمال کرتے ہوئے انجام دیتا ہے۔ صرف وہی صلاحیتیں جن سے وابستہ ہونے کی اجازت ہے، یا دوسرے لفظوں میں، ماحولیاتی صلاحیتوں کے طور پر، وراثت میں مل سکتی ہے۔

آپ شاید سوچ رہے ہوں گے کہ --caps آپشن میں صلاحیت بتانے کے بعد +eip کا کیا مطلب ہے۔ ان جھنڈوں کا استعمال اس قابلیت کا تعین کرنے کے لیے کیا جاتا ہے:

چالو ہونا ضروری ہے (p)؛

-استعمال کے لیے دستیاب (e)؛

بچوں کے عمل سے وراثت میں مل سکتا ہے (i)۔

چونکہ ہم cap_net_bind_service استعمال کرنا چاہتے ہیں، ہمیں ای فلیگ کے ساتھ ایسا کرنے کی ضرورت ہے۔ پھر ہم کمانڈ میں شیل شروع کریں گے۔ یہ صلاحیتیں بائنری چلائے گا اور ہمیں اسے i پرچم کے ساتھ نشان زد کرنے کی ضرورت ہے۔ آخر میں، ہم چاہتے ہیں کہ خصوصیت کو فعال کیا جائے (ہم نے یہ UID تبدیل کیے بغیر کیا) p کے ساتھ۔ یہ cap_net_bind_service+eip کی طرح لگتا ہے۔

آپ ss کا استعمال کرکے نتیجہ دیکھ سکتے ہیں۔ آئیے صفحہ پر فٹ ہونے کے لیے آؤٹ پٹ کو تھوڑا سا چھوٹا کرتے ہیں، لیکن یہ 0 کے علاوہ متعلقہ پورٹ اور یوزر آئی ڈی دکھائے گا، اس صورت میں 65:

# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0

اس مثال میں ہم نے capsh کا استعمال کیا، لیکن آپ libcap کا استعمال کرتے ہوئے ایک شیل لکھ سکتے ہیں۔ مزید معلومات کے لیے man 3 libcap دیکھیں۔

پروگرام لکھتے وقت، اکثر ڈویلپر ان تمام خصوصیات کو پہلے سے نہیں جانتا ہے جن کی پروگرام کو چلانے کے وقت کی ضرورت ہوتی ہے۔ مزید یہ کہ یہ خصوصیات نئے ورژن میں تبدیل ہو سکتی ہیں۔

اپنے پروگرام کی صلاحیتوں کو بہتر طور پر سمجھنے کے لیے، ہم BCC قابل ٹول لے سکتے ہیں، جو cap_capable کرنل فنکشن کے لیے kprobe سیٹ کرتا ہے:

/usr/share/bcc/tools/capable
TIME      UID  PID   TID   COMM               CAP    NAME           AUDIT
10:12:53 0 424     424     systemd-udevd 12 CAP_NET_ADMIN         1
10:12:57 0 1103   1101   timesync        25 CAP_SYS_TIME         1
10:12:57 0 19545 19545 capabilities       10 CAP_NET_BIND_SERVICE 1

ہم cap_capable کرنل فنکشن میں ون لائنر kprobe کے ساتھ bpftrace کا استعمال کرکے ایک ہی چیز حاصل کرسکتے ہیں:

bpftrace -e 
   'kprobe:cap_capable {
      time("%H:%M:%S ");
      printf("%-6d %-6d %-16s %-4d %dn", uid, pid, comm, arg2, arg3);
    }' 
    | grep -i capabilities

اگر ہمارے پروگرام کی صلاحیتیں kprobe کے بعد فعال ہو جائیں تو یہ درج ذیل کی طرح کچھ آؤٹ پٹ کرے گا۔

12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 10 1

پانچواں کالم وہ صلاحیتیں ہیں جن کی عمل کو ضرورت ہے، اور چونکہ اس آؤٹ پٹ میں غیر آڈٹ کے واقعات شامل ہیں، اس لیے ہم تمام غیر آڈٹ چیک دیکھتے ہیں اور آخر میں آڈٹ کے جھنڈے کے ساتھ مطلوبہ صلاحیت (آؤٹ پٹ میں آخری) 1 پر سیٹ ہوتی ہے۔ جس میں ہم دلچسپی رکھتے ہیں وہ ہے CAP_NET_BIND_SERVICE، اسے فائل میں کرنل سورس کوڈ میں ایک مستقل کے طور پر بیان کیا گیا ہے include/uapi/linux/ability.h شناخت کنندہ 10 کے ساتھ:

/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">

کنٹینرز جیسے runC یا Docker کے لیے رن ٹائم کے وقت صلاحیتوں کو فعال کیا جاتا ہے تاکہ وہ غیر مراعات یافتہ موڈ میں چل سکیں، لیکن انہیں صرف ان صلاحیتوں کی اجازت دی جاتی ہے جو زیادہ تر ایپلیکیشنز کو چلانے کے لیے درکار ہوتی ہیں۔ جب کسی ایپلیکیشن کو کچھ صلاحیتوں کی ضرورت ہوتی ہے، تو Docker انہیں --cap-add کا استعمال کرتے ہوئے فراہم کر سکتا ہے:

docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummy

یہ کمانڈ کنٹینر کو CAP_NET_ADMIN کی اہلیت دے گی، جس سے وہ ڈمی0 انٹرفیس کو شامل کرنے کے لیے نیٹ ورک لنک کو تشکیل دے گا۔

اگلا حصہ دکھاتا ہے کہ فلٹرنگ جیسی خصوصیات کو کیسے استعمال کیا جائے، لیکن ایک مختلف تکنیک کا استعمال کرتے ہوئے جو ہمیں پروگرام کے مطابق اپنے فلٹرز کو لاگو کرنے کی اجازت دیتی ہے۔

Seccomp

Seccomp کا مطلب Secure Computing ہے اور یہ لینکس کے کرنل میں لاگو ایک حفاظتی پرت ہے جو ڈویلپرز کو مخصوص سسٹم کالز کو فلٹر کرنے کی اجازت دیتی ہے۔ اگرچہ Seccomp کی صلاحیتوں میں لینکس سے موازنہ کیا جا سکتا ہے، لیکن کچھ سسٹم کالز کو منظم کرنے کی اس کی صلاحیت اسے ان کے مقابلے میں بہت زیادہ لچکدار بناتی ہے۔

Seccomp اور Linux کی خصوصیات باہمی طور پر خصوصی نہیں ہیں اور اکثر دونوں طریقوں سے فائدہ اٹھانے کے لیے ایک ساتھ استعمال ہوتے ہیں۔ مثال کے طور پر، آپ کسی عمل کو CAP_NET_ADMIN کی اہلیت دینا چاہیں گے لیکن اسے ساکٹ کنکشنز کو قبول کرنے کی اجازت نہیں دیں گے، اور قبول کریں اور قبول کریں 4 سسٹم کالز کو مسدود کریں۔

Seccomp فلٹرنگ کا طریقہ SECOMP_MODE_FILTER موڈ میں کام کرنے والے BPF فلٹرز پر مبنی ہے، اور سسٹم کال فلٹرنگ اسی طرح کی جاتی ہے جس طرح پیکٹ کے لیے ہوتی ہے۔

Seccomp فلٹرز prctl کا استعمال کرتے ہوئے PR_SET_SECCOMP آپریشن کے ذریعے لوڈ کیے جاتے ہیں۔ یہ فلٹرز ایک BPF پروگرام کی شکل اختیار کرتے ہیں جو کہ seccomp_data ڈھانچے کے ذریعہ پیش کردہ ہر Seccomp پیکٹ کے لئے عمل میں لایا جاتا ہے۔ اس ڈھانچے میں ریفرنس آرکیٹیکچر، سسٹم کال کے وقت پروسیسر کی ہدایات کے لیے ایک پوائنٹر، اور زیادہ سے زیادہ چھ سسٹم کال آرگیومینٹس ہیں، جنہیں uint64 کے طور پر ظاہر کیا گیا ہے۔

linux/seccomp.h فائل میں کرنل سورس کوڈ سے seccomp_data ڈھانچہ ایسا لگتا ہے:

struct seccomp_data {
int nr;
      __u32 arch;
      __u64 instruction_pointer;
      __u64 args[6];
};

جیسا کہ آپ اس ڈھانچے سے دیکھ سکتے ہیں، ہم سسٹم کال، اس کے دلائل، یا دونوں کے مجموعے سے فلٹر کر سکتے ہیں۔

ہر Seccomp پیکٹ حاصل کرنے کے بعد، فلٹر کو حتمی فیصلہ کرنے کے لیے پروسیسنگ کرنا چاہیے اور کرنل کو بتانا چاہیے کہ آگے کیا کرنا ہے۔ حتمی فیصلے کا اظہار واپسی کی قدروں میں سے ایک (اسٹیٹس کوڈز) سے ہوتا ہے۔

- SECOMP_RET_KILL_PROCESS - سسٹم کال کو فلٹر کرنے کے فوراً بعد پورے عمل کو ختم کر دیتا ہے جس کی وجہ سے عمل نہیں ہوتا ہے۔

- SECOMP_RET_KILL_THREAD - سسٹم کال کو فلٹر کرنے کے فوراً بعد موجودہ تھریڈ کو ختم کر دیتا ہے جو اس کی وجہ سے عمل میں نہیں آتا ہے۔

— SECOMP_RET_KILL — عرف SECOMP_RET_KILL_THREAD کے لیے، پسماندہ مطابقت کے لیے چھوڑ دیا گیا۔

- SECOMP_RET_TRAP - سسٹم کال ممنوع ہے، اور SIGSYS (خراب سسٹم کال) سگنل اس کام کو بھیجا جاتا ہے جو اسے کال کرتا ہے۔

- SECOMP_RET_ERRNO - سسٹم کال پر عمل نہیں ہوا ہے، اور SECOMP_RET_DATA فلٹر ریٹرن ویلیو کا کچھ حصہ صارف کی جگہ کو errno ویلیو کے طور پر منتقل کیا جاتا ہے۔ خرابی کی وجہ پر منحصر ہے، مختلف غلطی کی قدریں واپس کی جاتی ہیں۔ اگلے حصے میں غلطی کے نمبروں کی فہرست فراہم کی گئی ہے۔

- SECOMP_RET_TRACE - استعمال کرتے ہوئے ptrace ٹریسر کو مطلع کرنے کے لیے استعمال کیا جاتا ہے - PTRACE_O_TRACESECCOMP اس عمل کو دیکھنے اور کنٹرول کرنے کے لیے جب سسٹم کال کی جاتی ہے تو اسے روکنے کے لیے۔ اگر کوئی ٹریسر منسلک نہیں ہے تو، ایک خرابی واپس آ جاتی ہے، غلطی کو -ENOSYS پر سیٹ کیا جاتا ہے، اور سسٹم کال پر عمل نہیں ہوتا ہے۔

- SECOMP_RET_LOG - سسٹم کال حل ہو گئی اور لاگ ان ہو گئی۔

- SECOMP_RET_ALLOW - سسٹم کال کی اجازت ہے۔

ptrace ایک سسٹم کال ہے جو ٹریسی نامی عمل میں ٹریسنگ میکانزم کو نافذ کرنے کے لیے ہے، جس میں عمل کی نگرانی اور اسے کنٹرول کرنے کی صلاحیت ہوتی ہے۔ ٹریس پروگرام مؤثر طریقے سے عملدرآمد کو متاثر کر سکتا ہے اور ٹریسی کے میموری رجسٹر میں ترمیم کر سکتا ہے۔ Seccomp سیاق و سباق میں، ptrace استعمال کیا جاتا ہے جب SECOMP_RET_TRACE اسٹیٹس کوڈ کے ذریعہ متحرک کیا جاتا ہے، لہذا ٹریسر سسٹم کال کو عمل کرنے سے روک سکتا ہے اور اپنی منطق کو نافذ کرسکتا ہے۔

Seccomp کی غلطیاں

وقتاً فوقتاً، Seccomp کے ساتھ کام کرتے ہوئے، آپ کو مختلف خرابیوں کا سامنا کرنا پڑے گا، جن کی شناخت SECOMP_RET_ERRNO قسم کی واپسی قدر سے ہوتی ہے۔ غلطی کی اطلاع دینے کے لیے، seccomp سسٹم کال 1 کے بجائے -0 واپس آئے گی۔

درج ذیل غلطیاں ممکن ہیں:

- EACCESS - کال کرنے والے کو سسٹم کال کرنے کی اجازت نہیں ہے۔ یہ عام طور پر ہوتا ہے کیونکہ اس میں CAP_SYS_ADMIN مراعات نہیں ہیں یا no_new_privs prctl کا استعمال کرتے ہوئے سیٹ نہیں کیا گیا ہے (ہم اس کے بارے میں بعد میں بات کریں گے)؛

— EFAULT — منظور شدہ دلائل (seccomp_data ڈھانچے میں args) کا کوئی درست پتہ نہیں ہوتا ہے۔

— EINVAL — یہاں چار وجوہات ہو سکتی ہیں:

-درخواست کردہ آپریشن نامعلوم ہے یا موجودہ کنفیگریشن میں کرنل کے ذریعہ تعاون یافتہ نہیں ہے۔

-مخصوص جھنڈے درخواست کردہ آپریشن کے لیے درست نہیں ہیں۔

-آپریشن میں BPF_ABS شامل ہے، لیکن مخصوص آفسیٹ کے ساتھ مسائل ہیں، جو seccomp_data ڈھانچے کے سائز سے زیادہ ہو سکتے ہیں۔

- فلٹر کو بھیجی گئی ہدایات کی تعداد زیادہ سے زیادہ ہے۔

- ENOMEM - پروگرام کو چلانے کے لیے کافی میموری نہیں ہے۔

- EOPNOTSUPP - آپریشن نے اشارہ کیا کہ SECOMP_GET_ACTION_AVAIL کے ساتھ کارروائی دستیاب تھی، لیکن دانا دلائل میں واپسی کی حمایت نہیں کرتا ہے۔

— ESRCH — کسی دوسرے سلسلے کو ہم آہنگ کرتے وقت ایک مسئلہ پیش آیا۔

- ENOSYS - SECOMP_RET_TRACE کارروائی کے ساتھ کوئی ٹریسر منسلک نہیں ہے۔

prctl ایک سسٹم کال ہے جو یوزر اسپیس پروگرام کو کسی عمل کے مخصوص پہلوؤں کو جوڑ کر (سیٹ اور حاصل کرنے) کی اجازت دیتا ہے، جیسے بائٹ اینڈیننس، تھریڈ کے نام، محفوظ کمپیوٹیشن موڈ (Seccomp)، مراعات، پرف ایونٹس وغیرہ۔

Seccomp آپ کو سینڈ باکس ٹیکنالوجی کی طرح لگ سکتا ہے، لیکن ایسا نہیں ہے۔ Seccomp ایک ایسی افادیت ہے جو صارفین کو ایک سینڈ باکس میکانزم تیار کرنے کی اجازت دیتی ہے۔ اب دیکھتے ہیں کہ کس طرح یوزر انٹرایکشن پروگرام ایک فلٹر کا استعمال کرتے ہوئے بنائے جاتے ہیں جسے براہ راست Seccomp سسٹم کال کہتے ہیں۔

BPF Seccomp فلٹر کی مثال

یہاں ہم دکھائیں گے کہ پہلے زیر بحث آنے والی دو کارروائیوں کو یکجا کرنے کا طریقہ، یعنی:

- ہم ایک Seccomp BPF پروگرام لکھیں گے، جو کیے گئے فیصلوں کی بنیاد پر مختلف ریٹرن کوڈز کے ساتھ فلٹر کے طور پر استعمال کیا جائے گا۔

prctl کا استعمال کرتے ہوئے فلٹر لوڈ کریں۔

پہلے آپ کو معیاری لائبریری اور لینکس کرنل سے ہیڈر کی ضرورت ہے:

#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>

اس مثال کو آزمانے سے پہلے، ہمیں یہ یقینی بنانا چاہیے کہ دانا CONFIG_SECCOMP اور CONFIG_SECCOMP_FILTER سیٹ y کے ساتھ مرتب کیا گیا ہے۔ کام کرنے والی مشین پر آپ اسے اس طرح چیک کر سکتے ہیں:

cat /proc/config.gz| zcat | grep -i CONFIG_SECCOMP

باقی کوڈ دو حصوں کا install_filter فنکشن ہے۔ پہلا حصہ BPF فلٹرنگ ہدایات کی ہماری فہرست پر مشتمل ہے:

static int install_filter(int nr, int arch, int error) {
  struct sock_filter filter[] = {
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
  };

ہدایات linux/filter.h فائل میں بیان کردہ BPF_STMT اور BPF_JUMP میکروز کا استعمال کرتے ہوئے ترتیب دی گئی ہیں۔
آئیے ہدایات کے ذریعے چلتے ہیں۔

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - سسٹم BPF_LD سے BPF_W لفظ کی شکل میں لوڈ اور جمع ہوتا ہے، پیکٹ ڈیٹا ایک مقررہ آفسیٹ BPF_ABS پر واقع ہوتا ہے۔

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - BPF_JEQ کا استعمال کرتے ہوئے چیک کرتا ہے کہ آیا جمع کنسٹنٹ BPF_K میں آرکیٹیکچر ویلیو آرچ کے برابر ہے۔ اگر ایسا ہے تو، اگلی ہدایات پر آفسیٹ 0 پر چھلانگ لگاتا ہے، بصورت دیگر ایک غلطی پھینکنے کے لیے آفسیٹ 3 (اس معاملے میں) پر چھلانگ لگاتا ہے کیونکہ آرک مماثل نہیں ہوتا ہے۔

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - BPF_LD سے BPF_W لفظ کی شکل میں لوڈ اور جمع ہوتا ہے، جو BPF_ABS کے مقررہ آفسیٹ میں موجود سسٹم کال نمبر ہے۔

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — سسٹم کال نمبر کا موازنہ nr متغیر کی قدر سے کرتا ہے۔ اگر وہ برابر ہیں، تو اگلی ہدایات پر جائیں اور سسٹم کال کو غیر فعال کر دیں، بصورت دیگر SECOMP_RET_ALLOW کے ساتھ سسٹم کال کی اجازت دیتا ہے۔

- BPF_STMT(BPF_RET + BPF_K, SECOMP_RET_ERRNO | (غلطی اور SECOMP_RET_DATA)) - پروگرام کو BPF_RET کے ساتھ ختم کرتا ہے اور اس کے نتیجے میں ایرر متغیر سے نمبر کے ساتھ ایک غلطی SECOMP_RET_ERRNO پیدا ہوتی ہے۔

- BPF_STMT(BPF_RET + BPF_K, SECOMP_RET_ALLOW) - پروگرام کو BPF_RET کے ساتھ ختم کرتا ہے اور SECCOMP_RET_ALLOW کا استعمال کرتے ہوئے سسٹم کال کو انجام دینے کی اجازت دیتا ہے۔

SECOMP CBPF ہے۔
آپ سوچ رہے ہوں گے کہ مرتب کردہ ELF آبجیکٹ یا JIT مرتب کردہ C پروگرام کے بجائے ہدایات کی فہرست کیوں استعمال کی جاتی ہے۔

اس کی دو وجوہات ہیں۔

• سب سے پہلے، Seccomp cBPF (کلاسک BPF) استعمال کرتا ہے نہ کہ eBPF، جس کا مطلب ہے: اس میں کوئی رجسٹر نہیں ہے، لیکن آخری حساب کے نتائج کو ذخیرہ کرنے کے لیے صرف ایک جمع کرنے والا، جیسا کہ مثال میں دیکھا جا سکتا ہے۔

• دوسرا، Seccomp براہ راست BPF ہدایات کی ایک صف کی طرف اشارہ قبول کرتا ہے اور کچھ نہیں۔ ہم نے جو میکرو استعمال کیے ہیں وہ ان ہدایات کو پروگرامر کے موافق طریقے سے بیان کرنے میں مدد کرتے ہیں۔

اگر آپ کو اس اسمبلی کو سمجھنے میں مزید مدد کی ضرورت ہے تو، سیوڈوکوڈ پر غور کریں جو ایک ہی کام کرتا ہے:

if (arch != AUDIT_ARCH_X86_64) {
    return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
    return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;

socket_filter ڈھانچے میں فلٹر کوڈ کی وضاحت کرنے کے بعد، آپ کو ایک sock_fprog کی وضاحت کرنے کی ضرورت ہے جس میں کوڈ اور فلٹر کی لمبائی کا حساب لگایا جائے۔ اس ڈیٹا ڈھانچے کو بعد میں چلانے کے عمل کا اعلان کرنے کی دلیل کے طور پر ضرورت ہے:

struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};

install_filter فنکشن میں صرف ایک کام باقی ہے - پروگرام کو خود لوڈ کریں! ایسا کرنے کے لیے، ہم prctl استعمال کرتے ہیں، محفوظ کمپیوٹنگ موڈ میں داخل ہونے کے لیے PR_SET_SECCOMP کو اختیار کے طور پر لیتے ہیں۔ پھر ہم موڈ کو SECOMP_MODE_FILTER کا استعمال کرتے ہوئے فلٹر کو لوڈ کرنے کے لیے کہتے ہیں، جو sock_fprog قسم کے پروگ متغیر میں موجود ہے:

  if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
  }
  return 0;
}

آخر میں، ہم اپنے install_filter فنکشن کو استعمال کر سکتے ہیں، لیکن اس سے پہلے ہمیں prctl کو موجودہ عمل کے لیے PR_SET_NO_NEW_PRIVS سیٹ کرنے کے لیے استعمال کرنے کی ضرورت ہے اور اس طرح اس صورت حال سے بچنا چاہیے جہاں بچوں کے عمل کو ان کے والدین سے زیادہ مراعات حاصل ہوں۔ اس کے ساتھ، ہم روٹ رائٹس کے بغیر install_filter فنکشن میں درج ذیل prctl کال کر سکتے ہیں۔

اب ہم install_filter فنکشن کو کال کر سکتے ہیں۔ آئیے X86-64 فن تعمیر سے متعلق تمام رائٹ سسٹم کالز کو بلاک کردیں اور صرف ایک اجازت دیں جو تمام کوششوں کو روکتی ہے۔ فلٹر انسٹال کرنے کے بعد، ہم پہلی دلیل کا استعمال کرتے ہوئے عملدرآمد جاری رکھتے ہیں:

int main(int argc, char const *argv[]) {
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
   perror("prctl(NO_NEW_PRIVS)");
   return 1;
  }
   install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);
  return system(argv[1]);
 }

آو شروع کریں. اپنے پروگرام کو مرتب کرنے کے لیے ہم clang یا gcc استعمال کر سکتے ہیں، کسی بھی طرح سے یہ صرف main.c فائل کو خصوصی اختیارات کے بغیر مرتب کر رہا ہے:

clang main.c -o filter-write

جیسا کہ نوٹ کیا گیا ہے، ہم نے پروگرام میں تمام اندراجات کو بلاک کر دیا ہے۔ اس کی جانچ کرنے کے ل you آپ کو ایک پروگرام کی ضرورت ہے جو کچھ آؤٹ پٹ کرتا ہے - ls ایک اچھے امیدوار کی طرح لگتا ہے۔ وہ عام طور پر اس طرح برتاؤ کرتی ہے:

ls -la
total 36
drwxr-xr-x 2 fntlnz users 4096 Apr 28 21:09 .
drwxr-xr-x 4 fntlnz users 4096 Apr 26 13:01 ..
-rwxr-xr-x 1 fntlnz users 16800 Apr 28 21:09 filter-write
-rw-r--r-- 1 fntlnz users 19 Apr 28 21:09 .gitignore
-rw-r--r-- 1 fntlnz users 1282 Apr 28 21:08 main.c

کمال ہے! ہمارے ریپر پروگرام کا استعمال کرتے ہوئے ایسا لگتا ہے: ہم صرف اس پروگرام کو پاس کرتے ہیں جسے ہم پہلی دلیل کے طور پر جانچنا چاہتے ہیں:

./filter-write "ls -la"

جب عملدرآمد کیا جاتا ہے، تو یہ پروگرام مکمل طور پر خالی آؤٹ پٹ پیدا کرتا ہے۔ تاہم، ہم یہ دیکھنے کے لیے strace کا استعمال کر سکتے ہیں کہ کیا ہو رہا ہے:

strace -f ./filter-write "ls -la"

کام کا نتیجہ بہت چھوٹا ہے، لیکن اس کا متعلقہ حصہ ظاہر کرتا ہے کہ ریکارڈز EPERM کی خرابی کے ساتھ مسدود ہیں - وہی جو ہم نے ترتیب دیا تھا۔ اس کا مطلب یہ ہے کہ پروگرام کچھ بھی آؤٹ پٹ نہیں کرتا ہے کیونکہ یہ رائٹ سسٹم کال تک رسائی حاصل نہیں کرسکتا ہے۔

[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "write error", 11) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "n", 1) = -1 EPERM (Operation not permitted)

اب آپ سمجھ گئے ہیں کہ Seccomp BPF کیسے کام کرتا ہے اور آپ اس کے ساتھ کیا کر سکتے ہیں اس کا اچھا اندازہ ہے۔ لیکن کیا آپ سی بی پی ایف کے بجائے ای بی پی ایف کے ساتھ اپنی پوری طاقت کو بروئے کار لانے کے لیے وہی چیز حاصل کرنا پسند نہیں کریں گے؟

eBPF پروگراموں کے بارے میں سوچتے وقت، زیادہ تر لوگ سوچتے ہیں کہ وہ انہیں صرف لکھتے ہیں اور انہیں ایڈمنسٹریٹر کے مراعات کے ساتھ لوڈ کرتے ہیں۔ اگرچہ یہ بیان عام طور پر درست ہے، دانا مختلف سطحوں پر eBPF اشیاء کی حفاظت کے لیے میکانزم کا ایک سیٹ نافذ کرتا ہے۔ ان میکانزم کو BPF LSM ٹریپس کہا جاتا ہے۔

بی پی ایف ایل ایس ایم ٹریپس

نظام کے واقعات کی فن تعمیر سے آزاد نگرانی فراہم کرنے کے لیے، LSM ٹریپس کے تصور کو نافذ کرتا ہے۔ ہک کال تکنیکی طور پر سسٹم کال سے ملتی جلتی ہے، لیکن یہ سسٹم آزاد اور انفراسٹرکچر کے ساتھ مربوط ہے۔ LSM ایک نیا تصور فراہم کرتا ہے جس میں ایک تجریدی پرت مختلف فن تعمیرات پر سسٹم کالز سے نمٹنے کے دوران درپیش مسائل سے بچنے میں مدد کر سکتی ہے۔

لکھنے کے وقت، دانا کے پاس BPF پروگراموں سے وابستہ سات ہکس ہیں، اور SELinux واحد بلٹ ان LSM ہے جو ان کو لاگو کرتا ہے۔

ٹریپس کا سورس کوڈ فائل میں شامل/linux/security.h میں کرنل ٹری میں واقع ہے:

extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_alloc(struct bpf_map *map);
extern void security_bpf_map_free(struct bpf_map *map);
extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
extern void security_bpf_prog_free(struct bpf_prog_aux *aux);

ان میں سے ہر ایک کو پھانسی کے مختلف مراحل پر بلایا جائے گا:

— security_bpf — عمل درآمد شدہ BPF سسٹم کالز کی ابتدائی جانچ کرتا ہے۔

- security_bpf_map - چیک کرتا ہے جب دانا نقشے کے لیے فائل ڈسکرپٹر واپس کرتا ہے۔

- security_bpf_prog - چیک کرتا ہے جب کرنل eBPF پروگرام کے لیے فائل ڈسکرپٹر واپس کرتا ہے۔

— security_bpf_map_alloc — چیک کرتا ہے کہ آیا BPF نقشوں کے اندر سیکیورٹی فیلڈ شروع کی گئی ہے۔

- security_bpf_map_free - چیک کرتا ہے کہ آیا BPF نقشوں کے اندر سیکیورٹی فیلڈ کو صاف کیا گیا ہے۔

— security_bpf_prog_alloc — چیک کرتا ہے کہ آیا BPF پروگراموں کے اندر سیکیورٹی فیلڈ کو شروع کیا گیا ہے۔

- security_bpf_prog_free - چیک کرتا ہے کہ آیا BPF پروگراموں کے اندر سیکیورٹی فیلڈ کو صاف کیا گیا ہے۔

اب، یہ سب دیکھ کر، ہم سمجھتے ہیں: LSM BPF انٹرسیپٹرز کے پیچھے خیال یہ ہے کہ وہ ہر eBPF آبجیکٹ کو تحفظ فراہم کر سکتے ہیں، اس بات کو یقینی بناتے ہوئے کہ مناسب مراعات کے حامل افراد ہی کارڈز اور پروگراموں پر آپریشن کر سکتے ہیں۔

خلاصہ

سیکیورٹی ایسی چیز نہیں ہے جسے آپ ہر اس چیز کے لیے جو آپ محفوظ کرنا چاہتے ہیں ایک ہی سائز کے مطابق نافذ کر سکتے ہیں۔ مختلف سطحوں اور مختلف طریقوں سے سسٹمز کی حفاظت کرنے کے قابل ہونا ضروری ہے۔ یقین کریں یا نہ کریں، نظام کو محفوظ بنانے کا بہترین طریقہ مختلف مقامات سے تحفظ کی مختلف سطحوں کو منظم کرنا ہے، تاکہ ایک سطح کی حفاظت کو کم کرنے سے پورے نظام تک رسائی کی اجازت نہ ہو۔ بنیادی ڈویلپرز نے ہمیں مختلف پرتوں اور ٹچ پوائنٹس کا ایک سیٹ دے کر بہت اچھا کام کیا ہے۔ ہم امید کرتے ہیں کہ ہم نے آپ کو اچھی طرح سمجھ لیا ہے کہ پرتیں کیا ہیں اور ان کے ساتھ کام کرنے کے لیے BPF پروگراموں کو کیسے استعمال کیا جائے۔

مصنفین کے بارے میں

ڈیوڈ کیلاویرا Netlify میں CTO ہے۔ اس نے Docker سپورٹ میں کام کیا اور Runc، Go اور BCC ٹولز کے ساتھ ساتھ دیگر اوپن سورس پروجیکٹس کی ترقی میں تعاون کیا۔ ڈوکر پروجیکٹس اور ڈوکر پلگ ان ایکو سسٹم کی ترقی پر اپنے کام کے لیے جانا جاتا ہے۔ ڈیوڈ شعلہ گراف کے بارے میں بہت پرجوش ہے اور ہمیشہ کارکردگی کو بہتر بنانے کے لیے کوشاں رہتا ہے۔

لورینزو فونٹانا Sysdig میں اوپن سورس ٹیم پر کام کرتا ہے، جہاں وہ بنیادی طور پر Falco پر مرکوز ہے، جو ایک کلاؤڈ مقامی کمپیوٹنگ فاؤنڈیشن پروجیکٹ ہے جو کنٹینر رن ٹائم سیکیورٹی اور کرنل ماڈیول اور eBPF کے ذریعے بے ضابطگی کا پتہ لگاتا ہے۔ وہ ڈسٹری بیوٹڈ سسٹمز، سافٹ ویئر ڈیفائنڈ نیٹ ورکنگ، لینکس کرنل اور کارکردگی کے تجزیہ کے بارے میں پرجوش ہے۔

»کتاب کے بارے میں مزید تفصیلات یہاں پر دیکھی جا سکتی ہیں۔ پبلشر کی ویب سائٹ
» مواد کی میز
» اقتباس

Khabrozhiteley کے لیے کوپن کا استعمال کرتے ہوئے 25% ڈسکاؤنٹ - لینکس

کتاب کے کاغذی ورژن کی ادائیگی پر، ایک الیکٹرانک کتاب بذریعہ ای میل بھیجی جائے گی۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں