تغییرات sudo-rs در Ubuntu 26.04 و فایل sudoers
در Ubuntu 26.04 با جایگزینی sudo-rs، قوانین wildcard در sudoers دیگر کار نمیکنند. این مطلب توضیح میدهد چرا الگوهای glob در نسخه Rust اجرا نمیشوند و راه حل جایگزین چیست.
تغییرات sudo-rs در Ubuntu
نسخه Ubuntu 26.04 LTS از sudo-rs به عنوان sudo پیشفرض استفاده میکند، بنابراین دستور sudo در یک سرور تازه، به جای برنامه اصلی C، پیادهسازی مبتنی بر Rust را اجرا میکند. اکثر فایلهای sudoers بدون تغییر به کار خود ادامه میدهند. تنها قانونی که دچار اختلال میشود، قانونی است که در آرگومانهای یک دستور از wildcard استفاده میکند، زیرا sudo-rs الگوهای glob را با متن آرگومانها تطبیق نمیدهد.
نسخه Ubuntu 25.10 اولین نسخهای بود که این تغییر را اعمال کرد و Ubuntu 26.04 LTS نیز آن را حفظ کرده است. نسخه Ubuntu 24.04 LTS تحت تأثیر این تغییر قرار نمیگیرد، زیرا همچنان از sudo اصلی استفاده میکند، مگر آنکه sudo-rs را بهصورت دستی نصب کنید. اهمیت این موضوع زمانی مشخص میشود که شما از Ubuntu 24.04 به 26.04 ارتقا دهید، یا زمانی که یک سرور جدید با نسخه جدیدتر راهاندازی کنید. اگر از نسخههای interim نیز استفاده میکنید، تفاوت نسخههای LTS و interim اوبونتو در سرور توضیح میدهد که کدام ماشین زودتر با چنین تغییری مواجه میشود.
بررسی اینکه سرور شما واقعاً از کدام sudo استفاده میکند
این موضوع را بر اساس شماره نسخه حدس نزنید. از خود ماشین بپرسید.
sudo --version
update-alternatives --config sudo
dpkg -l 'sudo*'به sudo --version روی سیستم خودتان بیش از هر جدول نسخهای در اینترنت، از جمله این صفحه، اعتماد کنید. update-alternatives --config sudo نیمه دیگر پاسخ است: این دستور تمام ارائهدهندههای نصبشده برای /usr/bin/sudo را فهرست کرده و گزینه انتخابشده را مشخص میکند. نصب بودن یک بسته به معنای انتخاب شدن آن نیست، بنابراین به جای فهرست بستهها، وضعیت انتخاب را بخوانید.
هر دو پیادهسازی در طول دوره گذار بستهبندی میشوند. نسخه Rust با نام sudo-rs شناخته میشود که در نسخه 26.04 تا اوت 2026 دارای نسخه 0.2.13 است. نسخه اصلی که توسط Todd C. Miller نگهداری میشود، با نام sudo.ws بستهبندی شده و برنامههای آن دارای پسوند .ws هستند: sudo.ws و visudo.ws.
چرا اوبونتو به سراغ sudo-rs رفت
برنامه sudo دارای قابلیت setuid root است. هر کاربری در سیستم میتواند آن را اجرا کند و برنامه با دسترسی کامل شروع به کار میکند؛ بنابراین یک باگ حافظه در آن، یک آسیبپذیری local root محسوب میشود. مورد CVE-2021-3156 دقیقاً همین بود: یک سرریز بافر heap که توسط هر کاربر محلی قابل بهرهبرداری بود و حدود 10 سال در کدهای منتشرشده باقی مانده بود. زبان Rust این دسته از باگها را در زمان کامپایل شناسایی میکند و این تمام استدلال برای بازنویسی آن است.
دلیل دوم، دامنه عملکرد است و این همان موردی است که تنظیمات شما را تحت تأثیر قرار میدهد. برنامه اصلی sudo طی سه دهه، مجموعه ویژگیهای گستردهای را جمعآوری کرده است و هر ویژگی به معنای کدهای بیشتری است که با دسترسی root اجرا میشوند. برنامه sudo-rs عمداً زیرمجموعهای از آن ویژگیها را پیادهسازی میکند. هر چیزی که نویسندگان آن، خاص یا ذاتاً مخرب تشخیص دادهاند، حذف شده است؛ بنابراین یک دستور در sudoers که سالها کار میکرد، ممکن است به سادگی وجود نداشته باشد. قانون wildcard شما یکی از همین موارد است.
امنیت حافظه، یک دسته از باگها را حذف میکند، اما برنامه را بدون باگ نمیکند. sudo-rs نیز از زمانی که به گزینه پیشفرض تبدیل شده، اصلاحات امنیتی خاص خود را دریافت کرده است. آن را مانند هر نرمافزار دیگری وصله (patch) کنید.
کدام قوانین 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 اختصاصی خود اجرا میشود.
چرا قانون wildcard در sudoers دیگر مطابقت ندارد
استفاده از wildcardها هنوز در یک بخش مجاز است: نام فایلِ دستور. قانونی مانند %ops ALL = /sbin/fsck* همچنان اجازه اجرای sudo fsck و sudo fsck_exfat را میدهد، زیرا * بخشی از مسیری است که با سیستم فایل تطبیق داده میشود.
در لیست آرگومانها، sudo-rs تنها دو فرم خاص را میپذیرد و هیچکدام از آنها الگو (pattern) نیستند. "" به معنای عدم وجود آرگومان است. یک * در انتها به معنای هرگونه آرگومانِ دنبالهدار است. هر آرگومان دیگری به صورت متن دقیق (literal) مقایسه میشود. بنابراین %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 با فضای خالی (whitespace) نیز مطابقت دارد. این همان نکتهای است که تقریباً همه از آن غافل میشوند.
مستندات 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 است نامگذاری میکند، هرگز مطابقت پیدا نمیکند و خطای آن دقیقاً مشابه مشکل مجوزها (permissions) به نظر میرسد. با استفاده از command -v systemctl تأیید کنید و خروجی آن را کپی کنید.
قانون را بهجای /etc/sudoers در فایل drop-in اختصاصی خود قرار دهید تا ارتقای بسته (package upgrade) هرگز با ویرایش شما تداخل پیدا نکند:
sudo visudo -f /etc/sudoers.d/90-deploy
sudo visudo -c
sudo -l -U deployنام فایل را بدون نقطه و بدون tilde در انتها انتخاب کنید. نسخه اصلی sudo فایلهایی را که نامشان حاوی نقطه است در sudoers.d نادیده میگیرد، بنابراین 90-deploy.conf یک نمونه کلاسیک از عملیات بیاثر (silent no-op) است و رعایت این قرارداد هزینهای ندارد.
استفاده از یک wrapper با مالکیت 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 ندارد. برای unitهای سیستم، systemd این تصمیم را به polkit واگذار میکند؛ بنابراین یک قانون میتواند یک unit خاص و یک اپراتور مشخص را تعیین کند:
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 بتواند بدون نیاز به هیچگونه sudo، دستور systemctl restart app-api را اجرا کند. این مورد را دقیقاً در همان بستری که قرار است از آن استفاده شود تست کنید، زیرا قانونی که در نشست 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 escapeها در دستورات مجاز جلوگیری کند، پیادهسازی نشده است. این قابلیت در برابر یک کاربر مصمم هرگز کارایی نداشت. اگر قانونی به کسی اجازه دهد ویرایشگر یا مفسری را با دسترسی root اجرا کند، آن فرد دسترسی root را در اختیار دارد و هیچ گزینه sudo نمیتواند این موضوع را تغییر دهد.
ضبط نشست (Session recording) پیادهسازی نشده است، بنابراین هیچ I/O log و sudoreplay وجود ندارد. ثبت وقایع (Logging) فقط به 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 از همان فهرست است.
پیش از آنکه تغییری در sudo ایجاد کنید، یک نشست SSH دوم باز نگه دارید که در آن لاگین کرده و فعال باشد. اگر فایل sudoers دچار خطای تجزیه (parse) شود یا یک alternative به باینری اشاره کند که نصب نشده است، ممکن است راهی برای دسترسی root به سرور از راه دور نداشته باشید. این عادت باید در کنار سایر اقدامات شما در ده دقیقه اول روی یک VPS جدید قرار بگیرد.
بازگشت به حالت قبل را به عنوان یک مهلت در نظر بگیرید، نه یک راهکار دائمی. این کار به شما یک هفته فرصت میدهد تا قوانین را بهدرستی بازنویسی کنید؛ بازنویسی قوانین به خودی خود ارزشمند است، زیرا هر قانون wildcard که حذف میکنید، دسترسیهای بیش از حدی را که نویسنده اولیه متوجه آن نبوده است، محدود میکند.
FAQ
چرا قانون wildcard من در sudoers روی Ubuntu 26.04 از کار افتاد؟
زیرا Ubuntu 26.04 LTS از sudo-rs به عنوان sudo پیشفرض استفاده میکند و sudo-rs الگوهای wildcard را در آرگومانهای یک دستور تطبیق نمیدهد. این ابزار فقط wildcard را در نام فایل دستور، "" برای عدم وجود آرگومان، و یک * تکی به عنوان آخرین آرگومان میپذیرد. قانونی مانند /usr/bin/systemctl restart app-* که یک الگو را در میان یک آرگومان قرار میدهد، هیچ دسترسیای اعطا نمیکند و دستور رد میشود. دستور sudo -l -U deploy را به عنوان root اجرا کنید تا ببینید حساب کاربری واقعاً چه دسترسیهایی دارد، سپس قانون را با دستورات دقیق یا یک اسکریپت wrapper که مالکیت آن با root است، جایگزین کنید.
چگونه در Ubuntu 26.04 به sudo اصلی برگردم؟
نسخه اصلی با نام بسته sudo.ws ارائه شده است. آن را با sudo apt install sudo.ws نصب کنید، سپس با sudo update-alternatives --set sudo /usr/bin/sudo.ws آن را به عنوان جایگزین (alternative) تنظیم کنید. ابتدا update-alternatives --config sudo را اجرا کنید تا مسیرهای دقیق ارائهشده توسط سیستم را بخوانید و هنگام تغییر، یک نشست SSH دوم باز نگه دارید. این کار sudo-ldap را بازنمیگرداند، چرا که صرفنظر از پیادهسازی انتخابی شما، این قابلیت از نسخه 26.04 حذف شده است.
آیا sudo-rs همان فایل /etc/sudoers را میخواند؟
بله. sudo-rs فایل /etc/sudoers و فایلهای موجود در مسیر /etc/sudoers.d/ را با همان نحو برای کاربران، گروهها، نامهای مستعار (aliases)، مشخصات run-as و تگ NOPASSWD میخواند. این ابزار زیرمجموعهای از زبان 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 همیشه فعال است و قابل غیرفعالسازی نیست، بنابراین هر متغیری که نگه ندارید، پاکسازی میشود.