تغييرات sudo-rs في Ubuntu 26.04 وكيفية تعديل ملف sudoers
تنتقل Ubuntu 26.04 إلى sudo-rs كخيار افتراضي بدلاً من sudo التقليدي. اكتشف لماذا تتوقف قواعد wildcard عن العمل في ملف sudoers وكيف تكتب الأنماط الصحيحة لتجنب أخطاء الصلاحيات.
ما الذي يغيّره sudo-rs في Ubuntu
تأتي Ubuntu 26.04 LTS مع sudo-rs كإصدار افتراضي لـ sudo، لذا فإن أمر sudo على خادم جديد يُشغّل إعادة التنفيذ بلغة Rust بدلاً من برنامج C الأصلي. تستمر معظم ملفات sudoers في العمل كما كانت تماماً. القاعدة التي تتعطل هي تلك التي تحتوي على حرف بدل (wildcard) ضمن وسائط الأمر، لأن sudo-rs لا تطابق أنماط glob مع نص الوسائط.
بدأت Ubuntu 25.10 بهذا التغيير أولاً، واعتمدته Ubuntu 26.04 LTS. لا تتأثر Ubuntu 24.04 LTS بهذا، حيث تظل تستخدم sudo الأصلي ما لم تقم بتثبيت sudo-rs يدوياً. تظهر أهمية هذا الأمر في اللحظة التي ترقّي فيها من Ubuntu 24.04 إلى 26.04، أو عند إعداد خادم جديد على الإصدار الأحدث. إذا كنت تشغّل أيضاً الإصدارات المؤقتة، فإن كيف تختلف إصدارات LTS والإصدارات المؤقتة من Ubuntu على الخادم يوضح أي الأجهزة تواجه تغييراً كهذا أولاً.
تحقق من إصدار sudo الذي يعمل فعلياً على خادمك
لا تعتمد على رقم الإصدار في التخمين. اسأل النظام مباشرة.
sudo --version
update-alternatives --config sudo
dpkg -l 'sudo*'ثق بـ sudo --version على خادمك الخاص أكثر من أي جدول إصدارات على الإنترنت، بما في ذلك هذه الصفحة. update-alternatives --config sudo هو النصف الآخر من الإجابة: فهو يسرد كل مزود مثبت لـ /usr/bin/sudo ويحدد المختار منها. كون الحزمة مثبتة لا يعني أنها هي المختارة، لذا اقرأ التحديد، لا قائمة الحزم.
يتم توفير كلا الإصدارين ضمن الحزم أثناء الفترة الانتقالية. إصدار Rust هو sudo-rs، ويحمل الإصدار 0.2.13 في 26.04 اعتباراً من أغسطس 2026. أما الإصدار الأصلي، الذي يطوره Todd C. Miller، فيتم توفيره كحزمة sudo.ws، وتحمل برامجه لاحقة .ws: وهي sudo.ws و visudo.ws.
لماذا انتقلت Ubuntu إلى sudo-rs
يستخدم sudo صلاحية setuid root. يمكن لأي مستخدم على الخادم تشغيله، ويبدأ البرنامج بصلاحيات كاملة، لذا فإن أي ثغرة في الذاكرة ضمنه تُعد ثغرة لرفع الصلاحيات إلى root محلياً. كانت CVE-2021-3156 مثالاً دقيقاً على ذلك: فيضان في الذاكرة المؤقتة (heap buffer overflow) يمكن لأي مستخدم محلي الوصول إليه، وقد ظل موجوداً في الكود المصدري المتاح لمدة عشر سنوات تقريباً. تعالج لغة Rust هذا النوع من الأخطاء أثناء مرحلة الترجمة (compile time)، وهذا هو الجوهر الأساسي لحجة إعادة كتابة البرنامج.
السبب الثاني هو النطاق (scope)، وهو السبب الذي يؤثر على إعداداتك. جمع برنامج sudo الأصلي مجموعة ضخمة من الميزات على مدار ثلاثة عقود، وكل ميزة تعني كوداً إضافياً يعمل بصلاحيات root. ينفذ sudo-rs مجموعة فرعية من الميزات عن قصد. تم استبعاد أي ميزة اعتبرها المؤلفون ثانوية أو ضارة، لذا قد تجد أن قواعد sudoers التي عملت لسنوات غير مدعومة ببساطة. قاعدتك التي تستخدم المحارف البديلة (wildcard) هي واحدة من تلك الميزات المستبعدة.
تزيل ميزة أمان الذاكرة فئة واحدة من الأخطاء. هذا لا يجعل البرنامج خالياً من الأخطاء، وقد أصدر sudo-rs إصلاحات أمنية خاصة به منذ أن أصبح الخيار الافتراضي. قم بتحديثه وتصحيحه تماماً مثل أي برنامج آخر.
ما هي قواعد sudoers التي لا تزال تعمل
الملف هو نفس الملف. يقرأ sudo-rs ملف /etc/sudoers وملفات الإضافة في /etc/sudoers.d/، وتُدعم الإعدادات العادية التي يكتبها مدير الخادم:
deploy ALL=(ALL:ALL) ALL، وصيغ المجموعات مثل%sudo ALL=(ALL:ALL) ALL- الوسمان
NOPASSWD:وPASSWD: User_AliasوRunas_AliasوHost_AliasوCmnd_Alias- أمر مع قائمة وسائط دقيقة، على سبيل المثال
/usr/bin/systemctl restart app-api - أمر متبوع بـ
""، والذي يسمح بالأمر فقط دون أي وسائط على الإطلاق - أمر متبوع بـ
*كآخر وسيط له، والذي يسمح بأي وسائط لاحقة - مسار دليل ينتهي بـ
/، والذي يسمح بأي أمر داخل ذلك الدليل !لاستبعاد أمر من قائمة- مجموعة فرعية مفيدة من
Defaults، بما في ذلكsecure_pathوenv_keepوenv_checkوtimestamp_timeoutوpasswd_triesوeditorوumaskوtargetpwوrootpwوuse_pty
هناك إعدادان افتراضيان يتصرفان بشكل مختلف ويوقعان المستخدمين في الخطأ. لا يمكن إيقاف env_reset في sudo-rs: فهو مفعل دائماً. أما use_pty فهو مفعل افتراضياً، لذا يعمل الأمر في طرفية وهمية (pseudo-terminal) خاصة به.
لماذا توقفت قاعدة sudoers التي تستخدم أحرف البدل عن المطابقة
لا تزال أحرف البدل مسموحة في موضع واحد: اسم ملف الأمر. القاعدة %ops ALL = /sbin/fsck* لا تزال تسمح بـ sudo fsck و sudo fsck_exfat، لأن * جزء من المسار الذي تتم مطابقته مع نظام الملفات.
داخل قائمة الوسائط، لا يقبل sudo-rs سوى شكلين خاصين، وكلاهما ليس نمطاً (pattern). تعني "" عدم وجود وسائط. وتعني * في النهاية أي وسائط لاحقة. تتم مقارنة كل وسيطة أخرى كنص حرفي. لذا فإن %ops ALL = /sbin/service ntp * صحيحة، لأن ntp نص حرفي و * تأتي في النهاية. ومع ذلك، فإن قاعدة مثل هذه لا تمنحك ما كنت تقصده:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart app-*تُعد app-* نمطاً في منتصف الوسيطة. لا يقوم sudo-rs بتوسيعها، لذا لا تغطي القاعدة systemctl restart app-api ويرفض sudo تنفيذ الأمر. يخبرك أمران بالحقيقة حول أي قاعدة على خادمك: sudo -l -U deploy، عند تشغيله بصلاحيات root، يطبع ما يمكن لهذا الحساب تشغيله فعلياً، ويخبرك sudo visudo -c ما إذا كان الملف قابلاً للتحليل (parse) من الأساس. قم بتشغيلهما قبل البدء في التعديل العشوائي.
قاعدة النطاق العام (wildcard) كانت دائماً ثغرة
في إصدار sudo الأصلي، تُدمج الوسائط التي تكتبها في سلسلة نصية واحدة وتُطابق مع سلسلة وسائط القاعدة باستخدام نمط glob. يطابق نمط glob المسافات البيضاء. هذا هو الجزء الذي يغفل عنه الجميع تقريباً.
تقدم وثائق sudo-rs أوضح مثال على ذلك. القاعدة /bin/rm *.txt تسمح أيضاً بـ sudo rm -rf /home .txt، لأن الـ * الواحدة تبتلع -rf /home وتظل السلسلة المدمجة تنتهي بـ .txt. تُقرأ القاعدة على أنها "ملفات نصية فقط". لكنها تعني فعلياً "أي وسائط على الإطلاق، طالما ينتهي السطر بـ .txt".
ينطبق الشيء نفسه على مثال systemctl. نظراً لأن الوسائط تُقارن كسلسلة واحدة مدمجة، فإن النمط اللاحق يطابق أيضاً أي شيء تضيفه بعده، لذا فإن restart app-* تغطي restart app-api بالإضافة إلى أي وسائط إضافية يضيفها المستدعي. النمط الموجود داخل وسيطة يكشف الوسائط المحيطة به، والوسائط هي المكان الذي تكمن فيه قوة الأمر. ترفض sudo-rs هذا التركيب بدلاً من محاولة جعله آمناً، لأنه لا توجد صيغة عامة آمنة له.
استبدل الـ wildcard بقائمة أوامر صريحة
توجد معظم قواعد الـ wildcard لأن شخصاً ما لم يرغب في كتابة أربعة أسطر. اكتب الأسطر الأربعة.
Cmnd_Alias APP_RESTART = /usr/bin/systemctl restart app-api, /usr/bin/systemctl restart app-worker
Cmnd_Alias APP_STATUS = /usr/bin/systemctl status app-api, /usr/bin/systemctl status app-worker
deploy ALL=(root) NOPASSWD: APP_RESTART, APP_STATUSتأكد من صحة المسار. القاعدة التي تسمي /bin/systemctl على نظام يكون فيه الملف التنفيذي هو /usr/bin/systemctl لن تتطابق أبداً، وسيبدو الفشل مطابقاً لمشكلة صلاحيات. تحقق باستخدام command -v systemctl والصق ما يطبعه.
ضع القاعدة في ملف إضافي (drop-in file) خاص بها بدلاً من /etc/sudoers، حتى لا يتعارض تحديث الحزمة مع تعديلك:
sudo visudo -f /etc/sudoers.d/90-deploy
sudo visudo -c
sudo -l -U deployسمِّ الملف بدون نقطة وبدون علامة تيلدا (tilde) في نهايته. يتجاهل sudo الأصلي الملفات الموجودة في sudoers.d التي تحتوي أسماؤها على نقطة، لذا فإن 90-deploy.conf هو مثال كلاسيكي لعملية لا تؤدي إلى شيء (no-op) بصمت، والالتزام بالاتفاقية لا يكلف شيئاً.
استخدم غلافاً مملوكاً للمستخدم root عندما تصبح القائمة طويلة
عندما تكون مجموعة الصلاحيات كبيرة جداً بحيث يصعب إدراجها، انقل قرار السماح من ملف sudoers إلى برنامج صغير يمتلكه المستخدم root.
sudo tee /usr/local/sbin/app-restart >/dev/null <<'EOF'
#!/bin/sh
set -eu
case "${1:-}" in
app-api|app-worker) ;;
*) echo "app-restart: not allowed: ${1:-}" >&2; exit 1 ;;
esac
exec /usr/bin/systemctl restart "$1"
EOF
sudo chown root:root /usr/local/sbin/app-restart
sudo chmod 0755 /usr/local/sbin/app-restart
ls -l /usr/local/sbin/app-restartفي هذه الحالة، يشير ملف sudoers إلى أمر واحد فقط:
deploy ALL=(root) NOPASSWD: /usr/local/sbin/app-restart *استخدام الرمز * في النهاية مقبول هنا لأن البرنامج النصي، وليس sudo، هو من يقرر ما هو مسموح به. هذا ينطبق فقط طالما أن البرنامج النصي مملوك للمستخدم root وغير قابل للكتابة من قبل أي مستخدم آخر. إذا كان بإمكان deploy الكتابة في الملف، فيمكن لـ deploy استبدال محتوياته وتشغيل أي شيء بصلاحيات root، وهو أمر أسوأ من قاعدة wildcard التي قمت بإزالتها. تحقق من نمط الصلاحيات باستخدام ls -l، وإذا لم تكن المخرجات واضحة لك، فإن قراءة سلسلة صلاحيات drwxr-xr-x تستغرق خمس دقائق فقط لتتعلمها. تنطبق القاعدة نفسها على المجلد: يجب ألا يكون /usr/local/sbin قابلاً للكتابة من قبل الحساب أيضاً، لأن المجلد القابل للكتابة يعني إمكانية استبدال الملف بالكامل.
امنح المهمة حساباً خاصاً بدلاً من قاعدة sudo
السؤال الأفضل غالباً هو لماذا تحتاج الأوامر إلى صلاحيات root من الأساس. يمكن إدارة الخدمة التي تعمل كمستخدم خاص بها بواسطة ذلك المستخدم، ولا حاجة حينها لأي سطر في ملف sudoers. بالنسبة لوحدات systemd، يفوّض النظام هذا القرار مسبقاً إلى polkit، لذا يمكن لقاعدة واحدة تحديد وحدة واحدة ومشغّل واحد:
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "app-api.service" &&
subject.user == "deploy") {
return polkit.Result.YES;
}
});احفظ هذا الملف باسم /etc/polkit-1/rules.d/50-app-api.rules، وسيتمكن deploy من تشغيل systemctl restart app-api دون الحاجة إلى sudo نهائياً. اختبر القاعدة من السياق الفعلي الذي سيستخدمها، لأن القاعدة التي تعمل في جلسة SSH الخاصة بك يجب التأكد من عملها عبر cron قبل الاعتماد عليها. في كلتا الحالتين، يجب أن يكون الحساب الذي ينفذ العمل مخصصاً لهذا العمل فقط، وهو نفس المنطق الكامن وراء حسابات المستخدمين ذات الصلاحيات المحدودة على خادم VPS.
ما الذي يغفله sudo-rs
لم يتم تنفيذ sudo -E. استخدم Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY" لتسمية المتغيرات التي تحتاجها بدلاً من ذلك، وتذكر أن env_reset مفعّل دائماً، لذا فإن أي شيء لا يتم الاحتفاظ به سيتم مسحه.
تمت إزالة التخزين المركزي لملفات sudoers في LDAP. لم يتم تنفيذ sudoers.ldap وcvtsudoers، كما تمت إزالة حزمة sudo-ldap في الإصدار 26.04. لا تزال المصادقة عبر LDAP من خلال PAM أو SSSD تعمل بشكل طبيعي؛ فالجزء الذي يخرج عن نطاق العمل هو سياسة الوصول عبر الدليل (policy-in-a-directory).
لم يتم تنفيذ INTERCEPT، التي كانت تحاول منع الهروب إلى الصدفة (shell escapes) من أمر مسموح به. لم تكن هذه الميزة فعالة أبداً ضد مستخدم عازم على الاختراق. إذا كانت القاعدة تسمح لشخص ما بتشغيل محرر نصوص أو مترجم أوامر بصلاحيات root، فإنه يمتلك صلاحيات root بالفعل، ولا يوجد خيار في sudo يغير هذه الحقيقة.
لم يتم تنفيذ تسجيل الجلسات، لذا لا يوجد سجل للمدخلات والمخرجات (I/O log) ولا يوجد sudoreplay. تذهب السجلات إلى syslog فقط، ولا يوجد خيار logfile لإعادة توجيهها إلى مكان آخر، لذا ستصل رسائل sudo إلى المكان الذي يرسل إليه نظامك سجلات syslog حالياً.
هل يجب عليك العودة إلى sudo.ws؟
يمكنك ذلك، وخلال دورة الإصدار 26.04 تظل النسخة الأصلية متاحة ضمن الحزم لهذا السبب تحديداً.
sudo apt install sudo.ws
update-alternatives --config sudo
sudo update-alternatives --set sudo /usr/bin/sudo.wsانسخ المسارات الدقيقة من مخرجات --config بدلاً من نسخها من هذه الصفحة، لأن تلك هي القائمة التي سيقبلها نظامك. العودة إلى sudo-rs لاحقاً تعني ضبط البديل (alternative) على مسار ملف sudo-rs الثنائي الموجود في القائمة نفسها.
أبقِ جلسة SSH ثانية مفتوحة، ومسجلة للدخول، وفي حالة خمول، قبل أن تلمس أي شيء يؤثر على sudo. فملف sudoers الذي يفشل في التحليل، أو بديل يشير إلى ملف ثنائي غير مثبت، قد يتركك دون أي وسيلة لتصبح root على خادم بعيد. هذه العادة يجب أن تكون جزءاً من كل ما تفعله في الدقائق العشر الأولى على خادم VPS جديد.
تعامل مع العودة إلى النسخة السابقة كمهلة زمنية وليس كحل نهائي. فهي تمنحك أسبوعاً لإعادة كتابة القواعد بشكل صحيح، وتستحق عملية إعادة الكتابة هذه العناء بحد ذاتها، لأن كل قاعدة تحتوي على محارف بدل (wildcard) تقوم بحذفها كانت تمنح صلاحيات أكثر مما أدركه كاتبها.
FAQ
لماذا توقفت قاعدة البدل (wildcard) الخاصة بـ sudoers عن العمل في Ubuntu 26.04؟
لأن Ubuntu 26.04 LTS تعتمد sudo-rs كإصدار افتراضي لـ sudo، ولا يدعم sudo-rs مطابقة أنماط البدل داخل وسائط الأمر. يسمح النظام باستخدام البدل في اسم ملف الأمر فقط، حيث يعني "" عدم وجود وسائط، بينما يعني * الوحيد في نهاية الأمر وجود وسيط أخير. القاعدة مثل /usr/bin/systemctl restart app-* تضع نمطاً في منتصف الوسيط، لذا فهي لا تمنح أي صلاحية ويتم رفض الأمر. نفّذ sudo -l -U deploy بصلاحيات root لمعرفة الصلاحيات الفعلية للحساب، ثم استبدل القاعدة بالأوامر الدقيقة أو بسكريبت غلاف (wrapper script) مملوك لـ root.
كيف أعود إلى إصدار sudo الأصلي في Ubuntu 26.04؟
الإصدار الأصلي متاح في الحزمة sudo.ws. ثبّته باستخدام sudo apt install sudo.ws، ثم وجّه البديل (alternative) إليه باستخدام sudo update-alternatives --set sudo /usr/bin/sudo.ws. نفّذ update-alternatives --config sudo أولاً لقراءة المسارات الدقيقة التي يوفرها نظامك، واحتفظ بجلسة SSH ثانية مفتوحة أثناء إجراء التغيير. هذا الإجراء لا يعيد sudo-ldap، الذي أُزيل من إصدار 26.04 بغض النظر عن التنفيذ الذي تختاره.
هل يقرأ sudo-rs نفس ملف /etc/sudoers؟
نعم. يقرأ sudo-rs ملف /etc/sudoers وملفات الإضافة الموجودة في /etc/sudoers.d/، مع استخدام نفس الصيغة للمستخدمين والمجموعات والأسماء المستعارة (aliases) ومواصفات التشغيل بصلاحيات مستخدم آخر ووسم NOPASSWD. ينفذ sudo-rs مجموعة فرعية من لغة sudoers، لذا تظهر الاختلافات في صورة بنى برمجية مفقودة بدلاً من بنى تعمل بشكل مختلف. عدّل الملف باستخدام sudo visudo، ثم تحقق منه باستخدام sudo visudo -c قبل إغلاق جلستك.
ما الذي يحل محل sudo -E في sudo-rs؟
sudo -E غير منفذ، وكان استخدامه غير مستحسن بالفعل في إصدار sudo الأصلي، لأن منح عملية تعمل بصلاحيات root بيئة يتحكم فيها المستدعي هو وسيلة معروفة لتغيير سلوك تلك العملية. بدلاً من ذلك، حدد المتغيرات التي تحتاجها فعلياً في sudoers، باستخدام سطر مثل Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY". ميزة env_reset مفعلة دائماً في sudo-rs ولا يمكن تعطيلها، لذا يتم مسح كل متغير لا تحتفظ به.