مقالات معمّقة حول التكنولوجيا التي تشكّل المستقبل.

تطور sudo: من su في Unix إلى الصلاحيات الحديثة

تعرّف على تطور sudo من أمر su الأصلي في Unix إلى بدائل حديثة مثل doas وpolkit وrun0، مع أخطاء أمنية شائعة وأفضل الممارسات.

صف من المفاتيح يتدرج من الحديد الصدئ إلى الحديث الأنيق معروضة على جدار مخملي

هل تعلم حقيقة تفاجئ معظم الناس؟ عندما تكتب كلمة المرور في sudo، لا يظهر لك أي أثر على الإطلاق. لا نجوم ولا نقاط، لا شيء. يبقى المؤشر ثابتاً في مكانه. هذا القرار التصميمي اتُخذ في ثمانينيات القرن الماضي، وما زال يربك المستخدمين منذ أكثر من أربعة عقود. كثيراً ما يظن الناس أن الطرفية (Terminal) تجمّدت، فيكتبون كلمة المرور مرات عدة، أو يفترضون أن شيئاً ما تعطل. أخيراً قررت Ubuntu إظهار النجوم افتراضياً، لتنهي بذلك تقليداً استمر 46 عاماً في إدخال كلمات المرور بصمت.

هذا التغيير الصغير يكشف شيئاً أكبر: طريقة تعامل Linux مع رفع الصلاحيات (Privilege Escalation) هي مزيج من قرارات اتُخذت على مدى عقود، بعضها عبقري وبعضها مثير للتساؤل بشدة، وكلها محمّلة بثقل التوافق مع الإصدارات السابقة. فهم كيف وصلنا إلى هنا يساعدك على اتخاذ قرارات أمنية أفضل اليوم.

قبل sudo: عالم su

كانت أداة رفع الصلاحيات الأصلية في Unix هي su — اختصار لـ«substitute user». وكانت تقوم بشيء واحد فقط: تبديل جلسة الصدفة (Shell) بالكامل إلى مستخدم آخر، عادةً root. كنت تكتب su، وتدخل كلمة مرور root، وتنجز أعمال الإدارة، ثم تعود إلى حسابك العادي.

$ su
Password: [type root's password]
# whoami
root
# apt install nginx
# exit
$ whoami
normaluser

بدأت مشاكل su تتضح مع نمو أنظمة Unix وتجاوزها عدد قليل من المشغلين الموثوقين. كان كل من يحتاج صلاحيات إدارية مضطراً لمعرفة كلمة مرور root. وعند مغادرة أحد أعضاء الفريق، كان لزاماً تغيير كلمة مرور root وتوزيعها الجديدة على الباقين. ولم يكن هناك سجل تدقيق؛ فبمجرد أن يصبح أحدهم root، تُسجَّل جميع أفعاله باسم «root» دون أي أثر يدل على من نفّذ ماذا. ثم إن su كان يمنحك صدفة root كاملة، أي أن خطأ مطبعياً واحداً قد يدمّر النظام بأكمله.

القصة الكلاسيكية المرعبة: مسؤول أراد كتابة rm -rf /tmp/old_files لكنه ضغط Enter بالخطأ بعد كتابة rm -rf /. مع صدفة root كاملة، لا شيء يمنع هذا الأمر من التنفيذ. لم يكن su يفرّق بين «هذا الشخص يحتاج إلى إعادة تشغيل nginx» و«هذا الشخص يحتاج إلى وصول غير مقيّد إلى النظام كله».

كيف حلّ sudo مشكلة كلمة مرور root

وُلد sudo («superuser do») عام 1980 في جامعة SUNY Buffalo على يد Bob Coggeshall وCliff Spencer. كانت الفكرة الجوهرية بسيطة لكنها غيّرت كل شيء: بدلاً من مشاركة كلمة مرور root، يُتاح للمستخدمين الأفراد تنفيذ أوامر محددة بصلاحيات root باستخدام كلمة مرورهم الخاصة. ويتحكم مدير النظام في من يستطيع تنفيذ ماذا عبر ملف إعدادات.

حلّ هذا النهج عدة مشاكل في آن واحد:

  • لا توجد كلمة مرور root مشتركة. يتحقق كل مستخدم بهويته الخاصة. وعند مغادرة أحدهم، تُزيل صلاحية sudo عنه، دون الحاجة إلى تغيير أي كلمة مرور.
  • صلاحيات دقيقة. يمكنك السماح لمطور بإعادة تشغيل خدمة معينة دون منحه صلاحيات root كاملة.
  • سجل تدقيق. يُسجَّل كل أمر sudo مع اسم المستخدم الذي نفّذه، وما الذي نفّذه، ووقت التنفيذ.
  • رفع مؤقت للصلاحيات. بدلاً من صدفة root مفتوحة، ينفّذ sudo أمراً واحداً بصلاحيات مرتفعة ثم يعود إلى الوضع العادي.
# Run a single command as root
$ sudo apt install nginx
[sudo] password for priya:
# That's YOUR password, not root's
# The audit trail in /var/log/auth.log:
# Mar 20 14:23:01 web-03 sudo: priya : TTY=pts/0 ;
#   PWD=/home/priya ; USER=root ; COMMAND=/usr/bin/apt install nginx

ملف sudoers: قوي وخطير

تُحفظ إعدادات sudo في /etc/sudoers، وصياغته من أكثر الأشياء إرباكاً في إدارة Linux فعلاً. خطأ صياغي واحد يكفي لإقصائك عن sudo تماماً، ولهذا السبب وُجد الأمر visudo الذي يتحقق من صحة الملف قبل الحفظ.

# /etc/sudoers syntax:
# WHO  WHERE = (AS_WHOM) WHAT
# Let priya run anything as root on any host
priya ALL=(ALL:ALL) ALL
# Let the 'webdev' group restart nginx only
%webdev ALL=(root) /usr/bin/systemctl restart nginx,\
/usr/bin/systemctl reload nginx
# Let deploy user run deploys without a password
deploy ALL=(root) NOPASSWD: /opt/deploy/run.sh
# DANGEROUS: This looks restrictive but isn't
bob ALL=(root) /usr/bin/vim
# Bob can now run: sudo vim, then :!bash to get a root shell

المثال الأخير — منح شخص صلاحية sudo على vim — ثغرة أمنية يقع فيها الناس باستمرار. يستطيع Vim (وكثير من البرامج الأخرى) تشغيل أوامر صدفة. فإذا استطاع مستخدم تنفيذ sudo vim، فهو عملياً يملك صلاحية root غير مقيّدة. وينطبق الأمر نفسه على less وman وawk وfind وpython وعشرات الأوامر الأخرى. يحتفظ مشروع GTFOBins بقائمة شاملة بالملفات التنفيذية التي يمكن استخدامها للهروب من الصدفات المقيّدة.

سلوكيات sudo الغريبة وثغرات الأمان

تراكمت لدى sudo خلال تاريخه الذي تجاوز 40 عاماً سلوكيات غريبة فعلاً. وفهم هذه التفاصيل مهم للأمان:

  • تخزين بيانات الاعتماد مؤقتاً (Credential Caching). بعد إدخال كلمة مرورك، يخزنها sudo مؤقتاً لمدة 15 دقيقة افتراضياً. وخلال هذه الفترة، يُنفَّذ أي أمر sudo دون مصادقة. فإذا تركت طرفية مفتوحة دون قفل، يستطيع أي شخص تنفيذ أوامر sudo لمدة تصل إلى 15 دقيقة. شغّل sudo -k لمسح الذاكرة المؤقتة فوراً.
  • إعداد tty_tickets. افتراضياً، تكون ذاكرة sudo المؤقتة مرتبطة بكل طرفية على حدة. لكن في بعض الأنظمة، تؤدي المصادقة في طرفية واحدة إلى فتح sudo في جميع الطرفيات. تحقق من إعدادات Defaults لديك.
  • متغيرات البيئة. يُنقّي sudo معظم متغيرات البيئة، لكن ليس جميعها. فمتغيرات LD_PRELOAD وLD_LIBRARY_PATH تُحذف، لكن إذا أُسيء إعداد env_keep، يستطيع المهاجم حقن مسارات مكتبات خبيثة. وكان هذا أساس عدة ثغرات حقيقية لرفع الصلاحيات.
  • فخ NOPASSWD. إعداد NOPASSWD مريح للأتمتة، لكنه خطير إذا طُبّق على نطاق واسع. فتطبيق مخترق يعمل بحساب يملك صلاحية sudo بخيار NOPASSWD يملك عملياً صلاحية root، دون كلمة مرور تحتاج إلى كسرها.
# Security hardening for sudoers:
# Require password every time (disable caching)
Defaults timestamp_timeout=0
# Or reduce cache to 1 minute
Defaults timestamp_timeout=1
# Ensure credential cache is per-terminal
Defaults tty_tickets
# Log all sudo I/O (records full terminal sessions)
Defaults log_input, log_output
Defaults iolog_dir=/var/log/sudo-io
# Require root password instead of user password
# (prevents compromised user accounts from sudo-ing)
Defaults rootpw
# Show asterisks when typing password
Defaults pwfeedback

بدائل حديثة: doas وpolkit وrun0

أفرزت تعقيدات sudo عدة بدائل، لكل منها فلسفة مختلفة.

doas: sudo دون التعقيد

أُنشئ doas في OpenBSD («dedicated OpenBSD application subexecutor») على يد Ted Unangst عام 2015، تحديداً لأن sudo أصبح معقداً أكثر من اللازم بحيث يصعب تدقيقه. وتتكون إعداداته عادةً من سطرين أو ثلاثة فقط:

# /etc/doas.conf — the entire configuration
permit persist priya
permit nopass deploy as root cmd /opt/deploy/run.sh
# That's it. Compare this to a typical sudoers file.
# 'persist' is like sudo's credential caching
# 'nopass' is like NOPASSWD

يبلغ كود doas المصدري نحو 2,500 سطر من لغة C، بينما يتجاوز كود sudo 150,000 سطر. وفي الأنظمة التي لا تحتاج ميزات sudo المتقدمة (مثل مطابقة معاملات الأوامر أو التكامل مع LDAP)، يصبح doas أبسط بكثير في الإعداد والتدقيق والتأمين. أستخدمه على خوادمي الشخصية منذ سنوات، ولم أشتق إلى sudo ولو مرة واحدة.

polkit: صلاحيات دقيقة لسطح المكتب

يتبنى PolicyKit (polkit) نهجاً مختلفاً تماماً. فبدلاً من تغليف أوامر فردية، يعرّف إجراءات يمكن للتطبيقات طلبها. وعندما يحتاج تطبيق سطح مكتب إلى تعديل إعدادات الشبكة، يطلب الإذن من polkit، فيقرر وفق قواعده إما المنح أو الرفض أو مطالبة المستخدم بالتأكيد.

وهذه هي الطريقة التي تتيح لسطح مكتب Linux تركيب فلاش USB دون كلمة مرور، بينما يتطلب تثبيت البرامج مصادقة. والدقة هنا على مستوى الإجراء وليس مستوى الأمر، وهذا يتوافق أفضل مع طريقة تفكير مستخدمي سطح المكتب في الصلاحيات.

run0: إعادة التفكير الجذرية من systemd

أحدث الوافدين هو run0 من systemd، ويعمل بطريقة مختلفة جوهرياً عن sudo. فبدلاً من تشغيل أمر بصلاحيات root من جلستك الحالية (وهذا يتطلب بتات setuid ويخلق مشاكل أمنية)، يطلب run0 من مدير الخدمات تشغيل خدمة جديدة تعمل بصلاحيات root. ولا تكتسب جلستك للمستخدم أي صلاحيات مرتفعة أبداً، فالعملية المميزة منفصلة تماماً.

يتفادى هذا النهج فئات كاملة من ثغرات sudo: لا يوجد ملف تنفيذي بـ setuid، ولا تخزين مؤقت لبيانات الاعتماد، ولا سطح لحقن متغيرات البيئة. ومقابل ذلك، يتطلب النظام systemd، مما يجعله غير مناسب لأنظمة BSD وإعدادات Linux الخفيفة. لكن على خوادم systemd، يُعد على الأرجح الخيار الأكثر أماناً المتاح.

توصيات عملية

بعد سنوات من إدارة خوادم Linux والتحقيق في حوادث رفع الصلاحيات، إليك ما أوصي به فعلاً:

  1. عطّل تسجيل دخول root تماماً. استخدم sudo أو doas لكل شيء. لا ينبغي أن توجد كلمة مرور root مشتركة.
  2. استخدم المجموعات لا المستخدمين الأفراد في sudoers. أدر الصلاحيات عبر عضوية المجموعات. وعند مغادرة أحدهم، أزله من المجموعة.
  3. لا تمنح أبداً صلاحية sudo للمفسّرات أو المحررات. لا sudo vim ولا sudo python ولا sudo less. وإذا احتجت إلى تحرير ملف مملوك لـ root، فاستخدم sudoedit بدلاً من ذلك.
  4. قلّل قواعد NOPASSWD. استخدمها للعمليات المؤتمتة فقط، ولأوامر محددة بمسارات كاملة.
  5. فعّل تسجيل أوامر sudo. سجّل على الأقل جميع أوامر sudo. وفي الأنظمة الحساسة، فعّل تسجيل كامل للمدخلات والمخرجات.
  6. فكّر في doas للأنظمة الأبسط. إذا لم تكن بحاجة إلى تكامل LDAP أو قواعد مطابقة معقدة، فإن doas أسهل في الإعداد الصحيح.
  7. أعد تدوير ذاكرة بيانات الاعتماد المؤقتة بصرامة. قلّل timestamp_timeout إلى 1-5 دقائق، أو عطّل التخزين المؤقت تماماً على خوادم الإنتاج.

أفضل آلية لرفع الصلاحيات هي التي تمنح الحد الأدنى من الوصول اللازم، ولأقل مدة لازمة، مع سجل تدقيق كامل. وما عدا ذلك فهو تنازل.

لن يختفي sudo في أي وقت قريب، فهو متجذر بعمق في السكربتات والأتمتة والوثائق والعادات اليومية. لكن مشهد البدائل أصبح أكثر صحة من أي وقت مضى. سواء تمسكت بـ sudo (مع تحصينه بشكل صحيح)، أو انتقلت إلى doas (للبساطة)، أو اعتمدت run0 (للأنظمة الحساسة أمنياً)، فالمهم هو فهم ما تفعله أداة رفع الصلاحيات فعلاً، لأن الفجوة بين ما يظنه الناس أن sudo يفعله وما يفعله فعلاً هي المكان الذي تعيش فيه معظم الحوادث الأمنية.