
تنفيذ آمن للكود
يدير CodeJail تنفيذ الكود غير الموثوق في صناديق رمل آمنة. وهو مصمم بشكل أساسي لتنفيذ بايثون، ولكن يمكن استخدامه أيضًا للغات أخرى.
يتم فرض الأمان باستخدام AppArmor. إذا كان نظام التشغيل لديك لا يدعم AppArmor، أو إذا لم يتم تعريف ملف تعريف AppArmor وتهيئته بشكل صحيح، فلن يحمي CodeJail عملية التنفيذ.
تم تصميم CodeJail ليكون قابلاً للتهيئة، وسيُهيئ نفسه تلقائيًا لتنفيذ بايثون إذا قمت بتثبيته بشكل صحيح.
يتكون صندوق الرمل في CodeJail من عدة أجزاء:
#) بيئة صندوق الرمل. بالنسبة لإعداد بايثون، ستكون هذه هي بايثون والحزم الأساسية المرتبطة بها كبيئة افتراضية (virtualenv). يُشار إلى ذلك في جميع أنحاء هذا المستند بالرمز . هذه البيئة للقراءة فقط، ويتم مشاركتها عبر جميع نسخ صناديق الرمل.
يمكن للكود المعزول أيضًا الوصول إلى مكتبات نظام التشغيل بالقدر الذي يسمح به ملف تعريف AppArmor.
#) دليل تنفيذ صندوق الرمل. هذا دليل مؤقت للقراءة فقط باسم مثل /tmp/codejail-XXXXXXXX يحتوي على الكود المقدَّم (./jailed_code)، وملفات إضافية اختيارية، ودليل مؤقت قابل للكتابة (./tmp) يمكن للكود المقدَّم استخدامه كمساحة عمل مؤقتة.
الكود المقدَّم عادةً هو الكود الذي يقدمه الطالب ليتم اختباره على الخادم، والملفات الإضافية عادةً ما تكون python_lib.zip تحتوي على مكتبات تقييم أو أدوات مساعدة.
لتشغيل CodeJail، يلزم وجود حسابي مستخدم. الحساب الأول هو الحساب الرئيسي الذي يتم بموجبه تشغيل الكود، وله صلاحية إنشاء صناديق الرمل. سيُشار إليه بالرمز <SANDBOX_CALLER>. الحساب الثاني هو الحساب الذي يتم بموجبه تشغيل صندوق الرمل. عادةً ما يكون هذا الحساب هو حساب sandbox.
هذه المكتبة مُختبرة حاليًا للعمل مع الإصدارات التالية
بايثون:
أوبونتو:
(لاحظ أن إصدار بايثون المستخدم داخل صندوق الرمل قد يختلف عن الإصدار المستخدم للمكتبة نفسها.)
توضح هذه التعليمات كيفية تهيئة نظام التشغيل لديك بحيث يمكن لـ CodeJail تنفيذ كود بايثون بأمان. ومع ذلك، من الممكن أيضًا تعيين codejail.safe_exec.ALWAYS_BE_UNSAFE = True وتنفيذ كود بايثون المقدَّم مباشرة على الجهاز، دون أي أمان على الإطلاق. قد يكون هذا مناسبًا لأجهزة المطورين الذين لا يهتمون بالأمان، ويسمح باختبار التكامل مع واجهة برمجة تطبيقات CodeJail. ومع ذلك، يجب عدم استخدامه إذا كان أي إدخال يأتي من مصادر غير موثوقة. لا تستخدم هذا الخيار في أنظمة الإنتاج.
لتأمين تنفيذ بايثون، ستحتاج إلى إنشاء بيئة افتراضية جديدة (virtualenv). هذا يعني أنه سيكون لديك اثنتان: البيئة الافتراضية الرئيسية لمشروعك، والبيئة الجديدة لكود بايثون المعزول.
اختر مكانًا للبيئة الافتراضية الجديدة، وسمِّه . سيتم اكتشافها واستخدامها تلقائيًا إذا وضعتها بجوار بيئتك الافتراضية الحالية تمامًا، ولكن مع إلحاق -sandbox. لذا إذا كانت بيئتك الافتراضية الحالية في /home/chris/ve/myproj، فاجعل هي /home/chris/ve/myproj-sandbox.
المستخدم الذي يشغّل نظام إدارة التعلّم (LMS) هو <SANDBOX_CALLER>، على سبيل المثال، أنت على جهاز التطوير الخاص بك، أو www-data على خادم.
تفاصيل أخرى هنا تعتمد على تهيئتك:
أنشئ البيئة الافتراضية الجديدة، باستخدام --copies بحيث يكون هناك ملف تنفيذي مميز لبايثون يمكن تقييده::
$ sudo python3.12 -m venv --copies
افتراضيًا، ستقوم البيئة الافتراضية فقط بإنشاء ارتباط رمزي (symlink) إلى بايثون النظام، وقد تمنع تهيئة AppArmor الافتراضية على بعض أنظمة التشغيل تطبيق العزل على ذلك.
(اختياري) إذا كانت لديك حزم معينة تريد إتاحتها لكودك المعزول، فقم بتثبيتها عن طريق تفعيل البيئة الافتراضية لصندوق الرمل، واستخدام pip لتثبيتها::
$ /bin/pip install -r requirements/sandbox.txt
أضف مستخدم صندوق الرمل::
$ sudo addgroup sandbox $ sudo adduser --disabled-login sandbox --ingroup sandbox
اسمح لخادم الويب بتشغيل بايثون المعزول كمستخدم sandbox. أنشئ الملف /etc/sudoers.d/01-sandbox::
$ sudo visudo -f /etc/sudoers.d/01-sandbox
<SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/bin/python <SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/usr/bin/find <SANDBOX_CALLER> ALL=(ALL) NOPASSWD:/usr/bin/pkill
(لاحظ أن ملف find الثنائي يمكنه تشغيل أي كود، لذا فإن هذا ليس ملف sudoers آمنًا لأغراض غير متعلقة بـ CodeJail.)
حرّر ملف تعريف AppArmor. هذا ملف نصي يحدد القيود المفروضة على ملف بايثون القابل للتنفيذ في صندوق الرمل. يجب أن يكون الملف في /etc/apparmor.d ويجب تسميته بناءً على الملف القابل للتنفيذ، مع استبدال الشرطات المائلة بنقاط. على سبيل المثال، إذا كان بايثون المعزول لديك في /home/chris/ve/myproj-sandbox/bin/python، فيجب أن يكون ملف تعريف AppArmor الخاص بك هو /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python.
انظر نموذج ملف التعريف في apparmor-profiles/. ملف التعريف ليتوافق مع موقع صندوق الرمل لديك.
إذا كان CodeJail لديك مهيأً بشكل صحيح لاستخدام safe_exec، فجرّب هذه الأوامر في طرفية بايثون الخاصة بك::
import codejail.jail_code
codejail.jail_code.configure('python', '<SANDENV>/bin/python', user='sandbox')
import codejail.safe_exec
jailed_globals = {}
codejail.safe_exec.safe_exec("output=open('/etc/passwd').read()", jailed_globals)
print(jailed_globals) # should be unreachable if codejail is working properly
يجب أن يفشل هذا مع استثناء (exception).
إذا كنت بحاجة إلى تغيير الحزم المثبتة في البيئة الافتراضية لصندوق الرمل، فستحتاج إلى تعطيل AppArmor، لأن بايثون المعزول لديك لا يمتلك صلاحيات تعديل الملفات في دليل site-packages الخاص به.
عطّل AppArmor لصندوق الرمل الخاص بك::
$ sudo apt-get install apparmor-utils # if you haven't already $ sudo aa-complain /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
ثبّت الحزم أو غيّرها بأي طريقة أخرى::
$ pip install -r requirements/sandbox.txt
أعد تفعيل AppArmor لصندوق الرمل الخاص بك::
$ sudo aa-enforce /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
لتشغيل الاختبارات، يجب عليك تنفيذ خطوات التثبيت القياسية. ثم يجب عليك تعيين متغيرات البيئة التالية::
$ export CODEJAIL_TEST_USER=<owner of sandbox (usually 'sandbox')>
$ export CODEJAIL_TEST_VENV=<SANDENV>
شغّل الاختبارات باستخدام Makefile::
$ make tests
يتم تخطي عدة اختبارات وكيل (proxy) إذا لم يتم تكوين وضع الوكيل.
CodeJail عام بما يكفي ليمكن استخدامه في مجموعة متنوعة من المشاريع لتشغيل الكود غير الموثوق. يوفر طبقتين:
يوفر jail_code.py تنفيذًا آمنًا للعمليات الفرعية. يقوم بذلك عن طريق تشغيل البرنامج في عملية فرعية يديرها AppArmor.
يوفر safe_exec.py معالجة متخصصة لتنفيذ بايثون، باستخدام jail_code لتوفير دلالات عبارة exec الخاصة ببايثون.
يشغّل CodeJail البرامج تحت AppArmor. AppArmor هي ميزة يوفرها نظام التشغيل للحد من الموارد التي يمكن للبرامج الوصول إليها. لتشغيل كود بايثون مع وصول محدود إلى الموارد، نقوم بإنشاء بيئة افتراضية جديدة، ثم نضع اسم ملف بايثون القابل للتنفيذ في ملف تعريف AppArmor، ونقيد الموارد في هذا الملف التعريفي. سينفذ CodeJail برنامج بايثون المقدم باستخدام ذلك الملف القابل للتنفيذ، وسيقوم AppArmor تلقائيًا بالحد من الموارد التي يمكنه الوصول إليها. يستخدم CodeJail أيضًا setrlimit للحد من مقدار وقت المعالجة و/أو الذاكرة المتاحة للعملية.
يأخذ codejail.jail_code برنامجًا لتشغيله، وملفات لنسخها في بيئته، ووسائط سطر الأوامر، وتدفق stdin. ينشئ دليلًا مؤقتًا، وينشئ أو ينسخ الملفات اللازمة، ويطلق عملية فرعية لتشغيل الكود، ويعيد مخرجات العملية وحالة الخروج الخاصة بها.
يحاكي codejail.safe_exec عبارة exec الخاصة ببايثون. يأخذ جزءًا من كود بايثون، ويشغله باستخدام jail_code، مع تعديل قاموس المتغيرات العامة (globals) كتأثير جانبي. يقوم safe_exec بذلك عن طريق تسلسل المتغيرات العامة من وإلى العملية الفرعية بصيغة JSON.
إذا لم يتم تكوين codejail أو AppArmor بشكل صحيح، فقد يلجأ codejail افتراضيًا إلى تشغيل الكود بشكل غير آمن (بدون عزل في صندوق رمل). إنه ليس آمنًا بشكل افتراضي. يجب على المشاريع التي تدمج codejail أن تنظر في تضمين مجموعة اختبارات وقت التشغيل التي تتحقق من العزل السليم عند بدء التشغيل قبل قبول أي مدخلات غير موثوقة.
يتم تحقيق عزل صندوق الرمل عبر تقييد AppArmor. يسهل Codejail ذلك، لكنه لا يمكنه عزل التنفيذ دون استخدام AppArmor.
لا يمكن تقييد حدود الموارد إلا باستخدام الآليات التي يوفرها rlimit في لينكس. بعض أوجه القصور الملحوظة:
بينما يمكن لـ FSIZE في rlimit تحديد حجم أي ملف واحد يمكن للعملية إنشاؤه، ويمكنه تحديد عدد الملفات المفتوحة في أي وقت واحد، إلا أنه لا يمكنه تحديد العدد الإجمالي للملفات المكتوبة، وبالتالي لا يمكنه تحديد العدد الإجمالي للبايتات المكتوبة عبر جميع الملفات. هناك تخفيف جزئي يتمثل في تقييد الحد الأقصى لوقت التنفيذ. (سيتم حذف جميع الملفات المكتوبة في صندوق الرمل في نهاية التنفيذ، على أي حال.)
يحد NPROC من قدرة العملية الحالية على إنشاء سلاسل (threads) وعمليات جديدة، ولكن عدد الاستخدام (عدد العمليات الموجودة بالفعل) هو مجموع جميع العمليات التي تحمل نفس UID، حتى في الحاويات الأخرى على نفس المضيف حيث قد يكون UID مرتبطًا باسم مستخدم مختلف. ينطبق هذا القيد أيضًا على مستخدم التطبيق بسبب كيفية تطبيق حدود rlimit. حتى إذا تم اختيار معرّفات UID بحيث لا تستخدمها برامج أخرى على المضيف، فإن عمليات صندوق الرمل المتعددة في codejail على نفس المضيف ستتقاسم مجموعة الاستخدام هذه ويمكن أن تقلل من قدرة بعضها البعض على إنشاء عمليات. في هذه الحالة، سيتعين ضبط NPROC على قيمة أعلى مما ستكون عليه لمثيل codejail واحد يعالج طلبًا واحدًا في كل مرة.
لا تتمتع صناديق الرمل بعزل قوي عن بعضها البعض. في ظل التهيئة الصحيحة، لا ينبغي أن يتمكن الكود غير الموثوق من اكتشاف عمليات تنفيذ الكود الأخرى النشطة، ولكن إذا تم انتهاك هذا الافتراض، فقد يتداخل صندوق رمل واحد نظريًا مع آخر.
يرجى عدم الإبلاغ عن الثغرات الأمنية علنًا. يرجى مراسلتنا عبر البريد الإلكتروني على [email protected].
حلّل ملفات التعريف::
$ sudo apparmor_parser --replace --warn=all --warn=no-debug-cache --Werror <APPARMOR_FILE>
أعد تفعيل البيئة الافتراضية الرئيسية لمشروعك مرة أخرى.
عطّل استخدام PAM لتعيين حدود الموارد (rlimits)::
sed -i '/pam_limits.so/d' /etc/pam.d/sudo