رفتن به محتوا
LoopX

root و sudo

توی این درس یاد می‌گیری کاربر root (مدیر سیستم) کیست و چرا هر کاری می‌تواند بکند؛ چطور با sudo فقط یک دستور را با دسترسی مدیر اجرا کنی؛ فرق sudo -i و su - چیست؛ چطور با گروه sudo به یک کاربر اجازه بدهی؛ چرا فایل /etc/sudoers را فقط با sudo visudo (ویرایش امن) عوض می‌کنیم؛ و اصل کمترین دسترسی: هر کس فقط همان چیزی را داشته باشد که لازم دارد. همه را روی کاربرهای واقعی امتحان می‌کنی. تمرین: به کاربری که می‌سازی دسترسی sudo می‌دهی و درستی‌اش را تست می‌کنی.

مسئله: کار مدیریتی لازم است، ولی همیشه مدیر بودن خطرناک است

Section titled “مسئله: کار مدیریتی لازم است، ولی همیشه مدیر بودن خطرناک است”

نصب برنامه، ویرایش فایل‌های /etc، راه‌اندازی سرویس، ساختن کاربر: همه کار مدیر سیستم‌اند و کاربر عادی اجازه‌شان را ندارد (درس قبل دیدی: Permission denied). یک راه ساده این است که همیشه با کاربر root کار کنی. ولی root هیچ ترمزی ندارد: یک اشتباه تایپی همه‌چیز را پاک می‌کند و هیچ‌کس هم نمی‌فهمد کار چه کسی بوده.

تشبیه: کلید مادر ساختمان

Section titled “تشبیه: کلید مادر ساختمان”

root مثل کلید مادر است که همه‌ی درها را باز می‌کند. دو راه برای کار با آن هست. راه اول: کلید را همیشه دست همه بدهی (ورود مستقیم با root). راه دوم: یک نگهبان (sudo) داشته باشی که برای هر کار، کلید را موقت بدهد، قبلش بپرسد «تو مجاز هستی؟» و در دفتر بنویسد «علی ساعت ۱۰:۳۰ در انبار را باز کرد». دستور su مثل این است که کلید را برداری و تا وقتی خودت نگذاشته‌ای‌اش، همراهت باشد.

مسیر یک دستور sudo: اول بررسی می‌شود که تو در قاعده‌های sudoers مجاز باشی، بعد (اگر چند دقیقه‌ی اخیر نداده‌ای) رمز خودت را می‌پرسد، و فقط همان یک دستور با هویت root اجرا و ثبت می‌شود.

کاربر root: همه‌کاره و خطرناک

Section titled “کاربر root: همه‌کاره و خطرناک”

root کاربری با UID صفر است. هسته‌ی لینوکس برای او قاعده‌های دسترسی (درس قبل) را نادیده می‌گیرد:

کاربر عادی root
UID معمولاً از ۱۰۰۰ به بالا 0
پوشه‌ی خانه /home/نام /root
علامت ترمینال (prompt) $ #
دسترسی به فایل‌ها طبق rwx مالک/گروه/بقیه همه را دور می‌زند
کارهای سیستمی ندارد نصب برنامه، ساختن کاربر، بستن هر پروسه، پورت زیر ۱۰۲۴، mount…

برای همین مهم‌ترین قاعده‌ی امنیت لینوکس این است: با root کار روزمره نکن؛ فقط برای همان لحظه‌ای که لازم است قدرتش را قرض بگیر. ابزار این قرض‌گرفتن sudo است.

برای آزمایش به چند کاربر نیاز داریم؛ dev1 و dev2 را می‌سازیم و در پایان درس پاک می‌کنیم.

Terminal window
echo "من: $(id -un) (UID=$(id -u))"
echo "root: $(id -un 0) (UID=0)"
echo "--- ردیف root در /etc/passwd:"
getent passwd root
echo "--- وضعیت رمز root:"
sudo passwd -S root | cut -d' ' -f1,2
خروجی
من: ali (UID=1001)
root: root (UID=0)
--- ردیف root در /etc/passwd:
root:x:0:0:root:/root:/bin/bash
--- وضعیت رمز root:
root L

سطر /etc/passwd را در درس قبل خواندی: UID و GID برابر 0 و پوشه‌ی خانه‌ی /root. در خروجی passwd -S حرف L یعنی رمز root قفل است: روی Ubuntu نمی‌شود با رمز به root تبدیل شد؛ فقط با sudo. (این یک تصمیم امنیتی است: رمز root‌ای که وجود ندارد، نمی‌شود حدس زد.)

حالا ببین root قاعده‌های دسترسی را چطور دور می‌زند:

Terminal window
mkdir -p ex1 && cd ex1
echo "راز" > locked.txt && chmod 000 locked.txt
ls -l locked.txt
cat locked.txt
echo "--- با sudo:"
sudo cat locked.txt
echo "--- فایل رمزهای کاربران (فقط root و گروه shadow):"
ls -l /etc/shadow
cat /etc/shadow
echo "تعداد خط‌ها با sudo: $(sudo cat /etc/shadow | wc -l)"
خروجی
---------- 1 ali ali 7 Oct 3 20:43 locked.txt
cat: locked.txt: Permission denied
--- با sudo:
راز
--- فایل رمزهای کاربران (فقط root و گروه shadow):
-rw-r----- 1 root shadow 665 Oct 3 20:43 /etc/shadow
cat: /etc/shadow: Permission denied
تعداد خط‌ها با sudo: 25

فایلی که هیچ دسترسی‌ای ندارد (----------) برای من (مالک) هم خواندنی نیست، ولی با sudo (یعنی root) خوانده می‌شود. و /etc/shadow که رمزهای (رمزنگاری‌شده‌ی) کاربران در آن است را فقط root و گروه shadow می‌خوانند. (محتوایش را عمداً چاپ نکرده‌ام؛ فقط تعداد خط‌ها را شمردم.)

مثال ۲: sudo، یک دستور با دسترسی root

Section titled “مثال ۲: sudo، یک دستور با دسترسی root”

sudo (مخفف superuser do) دستوری که بعد از خودش می‌آید را با هویت root اجرا می‌کند و بعد از آن دوباره خودت هستی:

Terminal window
echo "قبل: $(whoami)"
sudo whoami
sudo id
echo "بعد: $(whoami)"
echo "--- sudo یادش می‌ماند چه کسی صدایش زده:"
sudo env | grep -E '^(USER|HOME|SUDO_USER|SUDO_COMMAND)='
خروجی
قبل: ali
root
uid=0(root) gid=0(root) groups=0(root)
بعد: ali
--- sudo یادش می‌ماند چه کسی صدایش زده:
USER=root
HOME=/root
SUDO_COMMAND=/usr/bin/env
SUDO_USER=ali

فقط خود دستور whoami با root اجرا شد؛ همین‌که تمام شد، من دوباره ali هستم. متغیرهای محیطی نشان می‌دهند در آن لحظه USER=root است، ولی SUDO_USER=ali می‌گوید چه کسی این قدرت را خواسته (برای لاگ و اسکریپت‌ها). با sudo -u کاربر دستور هم می‌توانی به‌جای کاربر دیگری (نه فقط root) اجرا کنی؛ در درس قبل برای آزمایش دسترسی‌ها از همین استفاده کردیم.

⚡ بررسی سریع

وقتی sudo رمز می‌خواهد، رمز چه کسی را باید بدهی؟

مثال ۳: کاربری که اجازه‌ی sudo ندارد

Section titled “مثال ۳: کاربری که اجازه‌ی sudo ندارد”

در آزمایشگاه من ali بدون رمز sudo می‌کند (راحتی خودم برای ساختن درس)، پس رمزخواستن را نمی‌بینیم. برای دیدن رفتار واقعی، کاربر dev1 را با یک رمز آزمایشی می‌سازم:

Terminal window
PW=lab-pass
sudo useradd -m -s /bin/bash dev1
echo "dev1:$PW" | sudo chpasswd
id dev1
خروجی
uid=1002(dev1) gid=1002(dev1) groups=1002(dev1)

برای دیدن صفحه‌ی واقعی ترمینال هنگام ورود کاربر و رمزپرسی، یک ترمینال مجازی (tmux) باز می‌کنم و تایپ‌ها را خودکار می‌فرستم (مثل درس ویرایشگرها). دو تابع کمکی:

Terminal window
run_in_tmux() { tmux -u kill-server 2>/dev/null; tmux -u new-session -d -s t -x 76 -y "${TH:-24}" "$@"; sleep 1; }
screen() { tmux capture-pane -t t -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }
echo "توابع کمکی آماده شد"
خروجی
توابع کمکی آماده شد

حالا با su - dev1 (با رمز خودش) به dev1 تبدیل می‌شوم و sudo را امتحان می‌کنم. (رمزی که تایپ می‌شود روی صفحه نمایش داده نمی‌شود، مثل ترمینال واقعی.)

Terminal window
run_in_tmux "bash --norc"
tmux send-keys -t t "su - dev1" Enter; sleep 1
tmux send-keys -t t "$PW" Enter; sleep 1
tmux send-keys -t t "sudo whoami" Enter; sleep 1
tmux send-keys -t t "$PW" Enter; sleep 2
screen
خروجی
bash-5.2$ su - dev1
Password:
dev1@pc:~$ sudo whoami
[sudo] password for dev1:
dev1 is not in the sudoers file.
dev1@pc:~$

dev1 رمزش را درست داد، ولی sudo می‌گوید dev1 is not in the sudoers file: یعنی در فهرست مجازها نیست و دستور اجرا نمی‌شود. (این تلاش در لاگ هم ثبت می‌شود؛ مثال ۹.) پس اول باید به او اجازه داد.

مثال ۴: گروه sudo، دادن دسترسی به یک کاربر

Section titled “مثال ۴: گروه sudo، دادن دسترسی به یک کاربر”

در Ubuntu و Debian قاعده‌ای در /etc/sudoers هست که به همه‌ی اعضای گروه sudo اجازه می‌دهد (مثال ۶). پس برای دادن دسترسی کافی است کاربر را در این گروه بگذاری (در توزیع‌های Red Hat مثل Fedora و Rocky همین گروه wheel نام دارد):

Terminal window
echo "--- اعضای فعلی گروه sudo (ubuntu را خودِ تصویر Ubuntu ساخته):"
getent group sudo
sudo usermod -aG sudo dev1
echo "--- بعد از usermod -aG sudo dev1:"
getent group sudo
id dev1
echo "--- sudo -l -U: dev1 چه اجازه‌هایی دارد؟"
sudo -l -U dev1 | tail -2
خروجی
--- اعضای فعلی گروه sudo (ubuntu را خودِ تصویر Ubuntu ساخته):
sudo:x:27:ubuntu
--- بعد از usermod -aG sudo dev1:
sudo:x:27:ubuntu,dev1
uid=1002(dev1) gid=1002(dev1) groups=1002(dev1),27(sudo)
--- sudo -l -U: dev1 چه اجازه‌هایی دارد؟
User dev1 may run the following commands on pc:
(ALL : ALL) ALL

-aG یعنی اضافه کن (a = append) به گروه (G)؛ بدون -a گروه‌های قبلی کاربر پاک می‌شد (درس کاربرها). خروجی sudo -l -U می‌گوید dev1 می‌تواند همه‌ی دستورها را به‌عنوان هر کاربر و گروهی اجرا کند: (ALL : ALL) ALL. حالا همان آزمایش را تکرار می‌کنم، این بار با چند اتفاق دیگر که باید ببینی: حافظه‌ی رمز، sudo -k و sudo !!:

Terminal window
run_in_tmux "bash --norc"
tmux send-keys -t t "su - dev1" Enter; sleep 1
tmux send-keys -t t "$PW" Enter; sleep 1
tmux send-keys -t t "sudo whoami" Enter; sleep 1
tmux send-keys -t t "$PW" Enter; sleep 1
tmux send-keys -t t "sudo whoami" Enter; sleep 1
tmux send-keys -t t "sudo -k" Enter "sudo whoami" Enter; sleep 1
tmux send-keys -t t "$PW" Enter; sleep 1
tmux send-keys -t t "wc -l /etc/shadow" Enter; sleep 1
tmux send-keys -t t 'sudo !!' Enter; sleep 1
screen
خروجی
bash-5.2$ su - dev1
Password:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
dev1@pc:~$ sudo whoami
[sudo] password for dev1:
root
dev1@pc:~$ sudo whoami
root
dev1@pc:~$ sudo -k
dev1@pc:~$ sudo whoami
[sudo] password for dev1:
root
dev1@pc:~$ wc -l /etc/shadow
wc: /etc/shadow: Permission denied
dev1@pc:~$ sudo !!
sudo wc -l /etc/shadow
26 /etc/shadow
dev1@pc:~$

مرحله به مرحله:

  1. اولین بار [sudo] password for dev1: رمز خودش را خواست و نتیجه root شد. (پیام اول، «To run a command as administrator…»، راهنمای Ubuntu برای کاربرهایی است که عضو گروه sudo هستند و هنوز sudo نکرده‌اند.)
  2. بار دوم رمز نخواست: sudo رمزِ درست را برای هر ترمینال چند دقیقه (پیش‌فرض ۱۵) یادش نگه می‌دارد.
  3. با sudo -k آن حافظه پاک شد و بار بعد دوباره رمز خواست.
  4. wc -l /etc/shadow بدون sudo Permission denied داد؛ sudo !! یعنی «همان دستور قبلی را دوباره، این بار با sudo». (!! را خود bash جایگزین می‌کند و دستور کامل را چاپ می‌کند.) یک ترفند پرکاربرد وقتی بعد از زدن دستور یادت می‌افتد sudo لازم بود.

گاهی می‌خواهی چند دستور پشت‌سرهم با root بزنی. سه راه هست؛ فرقشان را با یک دستور که کاربر، پوشه‌ی خانه و پوشه‌ی فعلی را چاپ می‌کند ببین:

Terminal window
cd /tmp
echo "--- sudo دستور (همان پوشه‌ی فعلی):"
sudo sh -c 'echo "کاربر=$(id -un) HOME=$HOME PWD=$PWD"'
echo "--- sudo -i (ورود کامل مثل login؛ می‌رود به HOME):"
sudo -i sh -c 'echo "کاربر=$(id -un) HOME=$HOME PWD=$PWD"'
echo "--- sudo -s (shell با root، در همان پوشه):"
sudo -s sh -c 'echo "کاربر=$(id -un) HOME=$HOME PWD=$PWD"'
خروجی
--- sudo دستور (همان پوشه‌ی فعلی):
کاربر=root HOME=/root PWD=/tmp
--- sudo -i (ورود کامل مثل login؛ می‌رود به HOME):
کاربر=root HOME=/root PWD=/root
--- sudo -s (shell با root، در همان پوشه):
کاربر=root HOME=/root PWD=/tmp

sudo -i (interactive login) یک شل ورود کامل برای root می‌سازد: به /root می‌روی و فایل‌های شروع root اجرا می‌شود. sudo -s فقط یک shell با هویت root در همان پوشه‌ی فعلی می‌دهد. این هم صفحه‌ی واقعی sudo -i؛ ببین علامت ترمینال از $ به # عوض شد:

Terminal window
run_in_tmux "bash --norc"
tmux send-keys -t t "sudo -i" Enter; sleep 1
tmux send-keys -t t "whoami; pwd" Enter; sleep 1
tmux send-keys -t t "exit" Enter; sleep 1
screen
خروجی
bash-5.2$ sudo -i
root@pc:~# whoami; pwd
root
/root
root@pc:~# exit
logout
bash-5.2$

su (switch user) هم به کاربر دیگری تبدیل می‌شود، ولی رمز کاربر مقصد را می‌خواهد. برای root یعنی رمز root، که روی Ubuntu قفل است:

Terminal window
run_in_tmux "bash --norc"
tmux send-keys -t t "su -" Enter; sleep 1
tmux send-keys -t t "hope-this-works" Enter; sleep 6
screen
خروجی
bash-5.2$ su -
Password:
su: Authentication failure
bash-5.2$

(رمز اشتباه است و حتی رمز درستی هم وجود ندارد: su: Authentication failure.) پس روی Ubuntu همان sudo -i جای su - را می‌گیرد. su - برای وقتی است که رمز کاربر مقصد را داری. فرق خط‌تیره را وقتی می‌بینی که به یک کاربر عادی تبدیل شوی (اینجا چون با sudo اجرا می‌کنم رمز نمی‌خواهد):

Terminal window
cd /tmp
echo "--- su dev1 (بدون خط‌تیره: پوشه‌ی فعلی می‌ماند):"
sudo su dev1 -c 'echo "کاربر=$(id -un) HOME=$HOME PWD=$PWD"'
echo "--- su - dev1 (ورود کامل: به HOME او می‌روی):"
sudo su - dev1 -c 'echo "کاربر=$(id -un) HOME=$HOME PWD=$PWD"'
خروجی
--- su dev1 (بدون خط‌تیره: پوشه‌ی فعلی می‌ماند):
کاربر=dev1 HOME=/home/dev1 PWD=/tmp
--- su - dev1 (ورود کامل: به HOME او می‌روی):
کاربر=dev1 HOME=/home/dev1 PWD=/home/dev1
sudo دستور sudo -i sudo -s su - su
رمز چه کسی؟ خودت خودت خودت کاربر مقصد (مثلاً root) کاربر مقصد
چه می‌شود؟ فقط همان یک دستور با root شل ورود کامل root (در /root) شل root در همان پوشه شل ورود کامل کاربر مقصد شل با محیط فعلی تو
ثبت در لاگ هر دستور جدا، با نام تو فقط شروع جلسه فقط شروع جلسه فقط ورود فقط ورود
روی Ubuntu (root قفل) کار می‌کند کار می‌کند کار می‌کند نه (رمز root نیست) نه

نتیجه: برای کار معمول sudo دستور را بزن (کمترین قدرت، بهترین ثبت). فقط وقتی ده‌ها دستور مدیریتی پشت‌سرهم داری از sudo -i استفاده کن و بعد حتماً exit.

قاعده‌های sudo در /etc/sudoers و فایل‌های پوشه‌ی /etc/sudoers.d/ هستند. فقط root می‌خواندشان:

Terminal window
sudo ls -l /etc/sudoers
echo "--- قاعده‌های فعال (بدون توضیح و خط خالی):"
sudo grep -v '^#' /etc/sudoers | grep -v '^$'
echo "--- پوشه‌ی sudoers.d:"
sudo ls -l /etc/sudoers.d/
خروجی
-r--r----- 1 root root 1800 Jan 29 2024 /etc/sudoers
--- قاعده‌های فعال (بدون توضیح و خط خالی):
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
Defaults use_pty
root ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d
--- پوشه‌ی sudoers.d:
total 8
-r--r----- 1 root root 1068 Jan 29 2024 README
-r--r----- 1 root root 27 Oct 3 09:36 ali

هر قاعده این شکل را دارد: چه کسی، روی کدام میزبان = (به‌عنوان چه کاربری) چه دستورهایی:

قطعه معنی نمونه
%sudo چه کسی: علامت % یعنی یک گروه؛ بدون آن، نام کاربر %sudo، root، dev2
ALL (اول) روی کدام میزبان (در سرورهای تکی همیشه ALL) ALL
(ALL:ALL) به‌عنوان کدام کاربر:گروه می‌تواند اجرا کند (root)، (ALL)
NOPASSWD: برچسب اختیاری: بدون پرسیدن رمز NOPASSWD:
ALL (آخر) کدام دستورها؛ مسیر کامل یا ALL /usr/bin/du -sh /var/log

پس %sudo ALL=(ALL:ALL) ALL یعنی «اعضای گروه sudo، روی هر میزبان، به‌عنوان هر کاربر و گروه، هر دستور را می‌توانند اجرا کنند.» خط آخر (@includedir) هر فایل داخل sudoers.d را هم می‌خواند. فایل ali را آزمایشگاه برای راحتی من ساخته (NOPASSWD:ALL)؛ روی سرور واقعی چنین قاعده‌ای برای یک آدم نمی‌گذاری.

مثال ۷: ویرایش امن با visudo

Section titled “مثال ۷: ویرایش امن با visudo”

اگر /etc/sudoers را مثل یک فایل عادی ویرایش کنی و یک غلط تایپی بگذاری، دستور همان خط کار نمی‌کند و (مثال «اشتباهات رایج») هر بار هم sudo خطا چاپ می‌کند. برای همین قاعده‌ها را فقط با sudo visudo عوض می‌کنیم. این دستور:

  • روی یک نسخه‌ی موقت کار می‌کند، نه خود فایل؛
  • بعد از بستن ویرایشگر، نحو (syntax) را بررسی می‌کند و فقط اگر درست بود جایگزین می‌کند؛
  • در عین حال قفل می‌گذارد تا دو نفر همزمان ویرایش نکنند.

ویرایشگر پیش‌فرضش با EDITOR عوض می‌شود. بهتر است قاعده‌ها را به‌جای دست‌زدن به فایل اصلی، در یک فایل جدا در /etc/sudoers.d/ بنویسی (-f همان فایل را به visudo می‌دهد). برای dev2 یک قاعده‌ی محدود می‌نویسم: فقط اجازه‌ی یک دستور. اینجا هم با tmux nano را باز می‌کنم و خطی را که تو تایپ می‌کردی می‌فرستم:

Terminal window
sudo useradd -m -s /bin/bash dev2
TH=12 # ارتفاع ترمینال مجازی (برای nano کوتاه‌تر کافی است)
run_in_tmux "bash --norc"
tmux send-keys -t t "sudo EDITOR=nano visudo -f /etc/sudoers.d/dev2" Enter; sleep 2
tmux send-keys -t t -l "dev2 ALL=(root) NOPASSWD: /usr/bin/du -sh /var/log"; sleep 1
screen
خروجی
GNU nano 7.2 /etc/sudoers.d/dev2.tmp *
dev2 ALL=(root) NOPASSWD: /usr/bin/du -sh /var/log
^G Help ^O Write Out ^W Where Is ^K Cut ^T Execute
^X Exit ^R Read File ^\ Replace ^U Paste ^J Justify

به نام فایل بالای nano دقت کن: dev2.tmp، همان نسخه‌ی موقت. حالا ذخیره می‌کنم (Ctrl+X، بعد Y و Enter) و ببین visudo چه می‌گوید:

Terminal window
tmux send-keys -t t C-x; sleep 1
tmux send-keys -t t y; sleep 1
tmux send-keys -t t Enter; sleep 2
screen
خروجی
bash-5.2$ sudo EDITOR=nano visudo -f /etc/sudoers.d/dev2
bash-5.2$

ساکت بود، یعنی نحو درست بود و فایل جایگزین شد. بررسی کامل با visudo -c (فایل جدید را 0640 می‌سازد ولی visudo -c برای فایل‌های sudoers.d حالت 0440 می‌خواهد):

Terminal window
sudo ls -l /etc/sudoers.d/dev2
sudo visudo -c
echo "--- اصلاح دسترسی و بررسی دوباره:"
sudo chmod 440 /etc/sudoers.d/dev2
sudo visudo -c
خروجی
-rw-r----- 1 root root 51 Oct 3 20:44 /etc/sudoers.d/dev2
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/ali: parsed OK
/etc/sudoers.d/dev2: bad permissions, should be mode 0440
--- اصلاح دسترسی و بررسی دوباره:
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/ali: parsed OK
/etc/sudoers.d/dev2: parsed OK

حالا عمداً یک غلط می‌گذارم: بعد از NOPASSWD دو نقطه (:) را فراموش می‌کنم. با Alt+/ می‌روم آخر فایل:

Terminal window
tmux send-keys -t t "clear" Enter
tmux send-keys -t t "sudo EDITOR=nano visudo -f /etc/sudoers.d/dev2" Enter; sleep 2
tmux send-keys -t t M-/; sleep 1
tmux send-keys -t t -l "dev2 ALL=(ALL) NOPASSWD ALL"; sleep 1
tmux send-keys -t t C-x; sleep 1
tmux send-keys -t t y; sleep 1
tmux send-keys -t t Enter; sleep 2
tmux send-keys -t t "?" Enter; sleep 1
screen
خروجی
bash-5.2$ sudo EDITOR=nano visudo -f /etc/sudoers.d/dev2
/etc/sudoers.d/dev2:2:25: syntax error
dev2 ALL=(ALL) NOPASSWD ALL
^~~
What now? ?
Options are:
(e)dit sudoers file again
e(x)it without saving changes to sudoers file
(Q)uit and save changes to sudoers file (DANGER!)
What now?

visudo شماره‌ی خط و ستون خطا را نشان داد (:2:25) و پرسید «What now؟». سه گزینه دارد: دوباره ویرایش کن (e)، بدون ذخیره خارج شو (x)، یا با خطر ذخیره کن (Q؛ هرگز). x را می‌زنم و می‌بینم فایل دست‌نخورده مانده:

Terminal window
tmux send-keys -t t "x" Enter; sleep 1
tmux send-keys -t t "clear" Enter
tmux send-keys -t t "sudo cat /etc/sudoers.d/dev2" Enter; sleep 1
screen
tmux -u kill-server
خروجی
bash-5.2$ sudo cat /etc/sudoers.d/dev2
dev2 ALL=(root) NOPASSWD: /usr/bin/du -sh /var/log
bash-5.2$

فقط قاعده‌ی درست مانده. برای بررسی یک فایل قبل از نصب (مثلاً در اسکریپت) از visudo -cf استفاده کن:

Terminal window
printf 'dev2 ALL=(ALL) NOPASSWD ALL\n' > /tmp/bad.sudoers
sudo visudo -cf /tmp/bad.sudoers; echo "کد خروجی: $?"
printf 'dev2 ALL=(root) NOPASSWD: /usr/bin/id\n' > /tmp/good.sudoers
sudo visudo -cf /tmp/good.sudoers; echo "کد خروجی: $?"
rm /tmp/bad.sudoers /tmp/good.sudoers
خروجی
/tmp/bad.sudoers:1:25: syntax error
dev2 ALL=(ALL) NOPASSWD ALL
^~~
کد خروجی: 1
/tmp/good.sudoers: parsed OK
کد خروجی: 0

مثال ۸: اصل کمترین دسترسی، فقط یک دستور

Section titled “مثال ۸: اصل کمترین دسترسی، فقط یک دستور”

اصل کمترین دسترسی (principle of least privilege) یعنی هر کاربر و هر برنامه فقط همان اندازه که برای کارش لازم است دسترسی داشته باشد، نه بیشتر. dev2 که الان فقط قاعده‌ی du را دارد را آزمایش می‌کنم (-n یعنی اگر رمز لازم شد نپرس و خطا بده):

Terminal window
sudo -l -U dev2 | tail -2
echo "--- dev2 دستور مجاز را اجرا می‌کند:"
sudo -u dev2 sudo -n du -sh /var/log
echo "--- دستور دیگر (غیرمجاز):"
sudo -u dev2 sudo -n cat /etc/shadow
خروجی
User dev2 may run the following commands on pc:
(root) NOPASSWD: /usr/bin/du -sh /var/log
--- dev2 دستور مجاز را اجرا می‌کند:
620K /var/log
--- دستور دیگر (غیرمجاز):
sudo: a password is required

dev2 اندازه‌ی پوشه‌ی لاگ‌ها را می‌تواند ببیند (که بدون root ممکن نیست)، ولی هر چیز دیگر sudo: a password is required می‌گیرد: چون قاعده‌ی دیگری ندارد، و رمز هم که ندارد. این همان چیزی است که برای حساب‌های اتوماسیون (دیپلوی، مانیتورینگ) می‌خواهی: «فقط همین یک کار».

قاعده‌های سرانگشتی:

  1. کار روزمره را با کاربر عادی انجام بده و فقط دستور لازم را با sudo بزن.
  2. sudo دستور را به sudo -i ترجیح بده تا هر دستور با نام خودت ثبت شود.
  3. برای آدم‌ها و سرویس‌ها فقط دستور مشخص با مسیر کامل مجاز کن (در sudoers.d)، نه ALL.
  4. NOPASSWD: ALL را فقط برای ماشین‌های آزمایشی بگذار، نه سرور واقعی و نه آدم‌ها.
  5. هرگز ویرایشگر (vim، nano)، pager (less) یا shell را با sudo مجاز نکن؛ از داخلشان می‌شود shell گرفت و آن کاربر root کامل است.
  6. از * در قاعده‌ها دوری کن (در «اشتباهات رایج» می‌بینی چرا).

مثال ۹: sudo همه‌چیز را ثبت می‌کند

Section titled “مثال ۹: sudo همه‌چیز را ثبت می‌کند”

بزرگ‌ترین فایده‌ی sudo نسبت به ورود مستقیم با root ردپا است. این بار روی سرور systemd‌دار آزمایشگاه (که سرویس لاگ دارد) کار می‌کنم: ali دو دستور با sudo می‌زند، و کاربر eve که مجاز نیست رمز اشتباه می‌دهد؛ بعد لاگ را می‌خوانم. (روی Ubuntu و Debian لاگ sudo در /var/log/auth.log و در journal است؛ روی Red Hat در /var/log/secure.)

Terminal window
useradd -m -s /bin/bash eve 2>/dev/null
echo "eve:pw1234" | chpasswd
su - ali -c 'sudo whoami; sudo ls /root > /dev/null'
su - eve -c 'echo wrongpass | sudo -S whoami' > /dev/null 2>&1
sleep 1
echo "--- /var/log/auth.log (آخرین سه دستور):"
grep 'COMMAND=' /var/log/auth.log | tail -3 | sed -E 's/^[^ ]+ //'
echo "--- journal:"
journalctl -t sudo --no-pager -o cat | grep 'COMMAND=' | tail -3
userdel -r eve 2>/dev/null
خروجی
root
--- /var/log/auth.log (آخرین سه دستور):
server sudo: ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/whoami
server sudo: ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/ls /root
server sudo: eve : user NOT in sudoers ; PWD=/home/eve ; USER=root ; COMMAND=/usr/bin/whoami
--- journal:
ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/whoami
ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/ls /root
eve : user NOT in sudoers ; PWD=/home/eve ; USER=root ; COMMAND=/usr/bin/whoami

هر خط می‌گوید چه کسی (ali)، از کدام پوشه (PWD)، با چه هویتی (USER=root) و چه دستوری (COMMAND=) را اجرا کرده، و تلاش eve هم با عبارت user NOT in sudoers ثبت شده است. اگر هر دو با root وارد می‌شدند، هیچ‌کدام از این‌ها معلوم نبود.

پشت پرده: sudo چطور قدرت می‌گیرد؟

Section titled “پشت پرده: sudo چطور قدرت می‌گیرد؟”

خود sudo یک برنامه‌ی معمولی روی دیسک است، ولی با یک حالت ویژه: حرف s به‌جای x در دسترسی مالک (setuid). یعنی هر کس اجرایش کند، برنامه با هویت مالکش (root) اجرا می‌شود. (درس بعدی «دسترسی‌های ویژه» دقیقاً درباره‌ی همین است.)

Terminal window
ls -l /usr/bin/sudo /usr/bin/su
echo "--- حافظه‌ی رمز: برای هر کاربر یک فایل زمان‌سنج (نامش UID کاربر است):"
sudo ls -l /run/sudo/ts/
خروجی
-rwsr-xr-x 1 root root 67744 Aug 19 16:47 /usr/bin/su
-rwsr-xr-x 1 root root 335120 Sep 21 18:37 /usr/bin/sudo
--- حافظه‌ی رمز: برای هر کاربر یک فایل زمان‌سنج (نامش UID کاربر است):
total 12
-rw------- 1 root dev1 448 Oct 3 20:43 1002
-rw------- 1 root dev2 336 Oct 3 20:44 1003
-rw------- 1 root 1004 224 Oct 3 20:43 1004

مراحل اجرای یک sudo دستور:

  1. هسته چون sudo مالکش root و بیت setuid دارد، آن را با دسترسی root اجرا می‌کند.
  2. sudo قاعده‌های /etc/sudoers و sudoers.d را می‌خواند و بررسی می‌کند تو مجاز هستی یا نه؛ آخرین قاعده‌ی منطبق تعیین‌کننده است.
  3. اگر لازم باشد، رمز تو را از طریق PAM (سیستم احراز هویت لینوکس) می‌پرسد، مگر در /run/sudo/ts/ گواهی تازه‌ای باشد.
  4. محیط را پاک‌سازی می‌کند (env_reset) و PATH را به یک مقدار امن ثابت (secure_path) برمی‌گرداند. (این همان چیزی است که یکی از «اشتباهات رایج» را می‌سازد.)
  5. دستور را با هویت مقصد اجرا و رویداد را در syslog ثبت می‌کند.
دستور کار
sudo دستور اجرای یک دستور با root
sudo -u کاربر دستور اجرا با هویت یک کاربر دیگر
sudo -i شل ورود کامل root (و exit در پایان)
sudo -s شل root در همان پوشه
sudo -l / sudo -l -U کاربر فهرست اجازه‌های خودت / کاربر دیگر
sudo -k فراموش‌کردن رمز کش‌شده
sudo -n دستور اگر رمز لازم شد نپرس و خطا بده (برای اسکریپت)
sudo -S خواندن رمز از stdin (فقط آزمایش)
sudo !! دستور قبلی را دوباره با sudo بزن
su - کاربر ورود کامل با رمز آن کاربر
sudo visudo ویرایش امن sudoers
sudo visudo -f /etc/sudoers.d/نام ویرایش امن یک فایل جدا
visudo -c / visudo -cf فایل بررسی نحو همه‌ی فایل‌ها / یک فایل
usermod -aG sudo کاربر دادن دسترسی کامل (Debian/Ubuntu)
Terminal window
sudo echo "setting=1" > /etc/lxtest.conf
echo "--- راه‌حل: tee با sudo اجرا شود:"
echo "setting=1" | sudo tee /etc/lxtest.conf
sudo rm /etc/lxtest.conf
خروجی
bash: /etc/lxtest.conf: Permission denied
--- راه‌حل: tee با sudo اجرا شود:
setting=1

sudo فقط echo را با root اجرا کرد؛ ریدایرکت (>) را خود shell تو انجام می‌دهد و تو اجازه‌ی نوشتن در /etc را نداری. راه‌حل: tee را با sudo اجرا کن (... | sudo tee فایل؛ برای اضافه‌کردن tee -a).

۲) فایل با نقطه در نام، داخل sudoers.d

Section titled “۲) فایل با نقطه در نام، داخل sudoers.d”
Terminal window
echo 'dev2 ALL=(root) NOPASSWD: /usr/bin/id' | sudo tee /etc/sudoers.d/dev2-extra.conf > /dev/null
sudo chmod 440 /etc/sudoers.d/dev2-extra.conf
sudo visudo -c
echo "--- قاعده‌های dev2:"
sudo -l -U dev2 | tail -2
echo "--- بعد از حذف پسوند .conf از نام فایل:"
sudo mv /etc/sudoers.d/dev2-extra.conf /etc/sudoers.d/dev2-extra
sudo -l -U dev2 | tail -3
sudo rm /etc/sudoers.d/dev2-extra
خروجی
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/ali: parsed OK
/etc/sudoers.d/dev2: parsed OK
--- قاعده‌های dev2:
User dev2 may run the following commands on pc:
(root) NOPASSWD: /usr/bin/du -sh /var/log
--- بعد از حذف پسوند .conf از نام فایل:
User dev2 may run the following commands on pc:
(root) NOPASSWD: /usr/bin/du -sh /var/log
(root) NOPASSWD: /usr/bin/id

sudo فایل‌هایی که در نامشان . دارند یا با ~ تمام می‌شوند را بی‌صدا نادیده می‌گیرد (حتی visudo -c اسمی از آن نمی‌آورد). قاعده‌ی id تا وقتی اسم فایل .conf داشت اعمال نشد. راه‌حل: نام فایل‌های sudoers.d را بدون نقطه بنویس (dev2-extra).

۳) sudo: command not found برای برنامه‌ی خودت

Section titled “۳) sudo: command not found برای برنامه‌ی خودت”
Terminal window
mkdir -p ~/bin
printf '#!/bin/sh\necho "mytool اجرا شد"\n' > ~/bin/mytool
chmod +x ~/bin/mytool
export PATH="$HOME/bin:$PATH"
which mytool
mytool
sudo mytool
echo "--- راه‌حل: مسیر کامل یا ردکردن PATH:"
sudo "$(which mytool)"
sudo env "PATH=$PATH" mytool
rm -rf ~/bin
خروجی
/home/ali/bin/mytool
mytool اجرا شد
sudo: mytool: command not found
--- راه‌حل: مسیر کامل یا ردکردن PATH:
mytool اجرا شد
mytool اجرا شد

شل تو برنامه را در PATH خودت (شامل ~/bin) پیدا می‌کند، ولی sudo برای امنیت PATH را به secure_path ثابت برمی‌گرداند (مثال ۶). راه‌حل: مسیر کامل را بده، یا برنامه را در یک پوشه‌ی سیستمی مثل /usr/local/bin بگذار.

Terminal window
echo 'dev2 ALL=(root) NOPASSWD: /usr/bin/cat /var/log/*' | sudo tee /etc/sudoers.d/dev2-wild > /dev/null
sudo chmod 440 /etc/sudoers.d/dev2-wild
echo "--- مجاز: خواندن لاگ‌ها (قصد قاعده همین بود):"
sudo -u dev2 sudo -n cat /var/log/dpkg.log | head -1
echo "--- نفوذ: نام کاربران از /etc/shadow (فقط ستون اول):"
sudo -u dev2 sudo -n cat /var/log/../../etc/shadow | cut -d: -f1 | head -3
echo "--- مستقیم:"
sudo -u dev2 sudo -n cat /etc/shadow
sudo rm /etc/sudoers.d/dev2-wild
خروجی
--- مجاز: خواندن لاگ‌ها (قصد قاعده همین بود):
2026-09-11 02:06:06 startup archives install
--- نفوذ: نام کاربران از /etc/shadow (فقط ستون اول):
root
daemon
bin
--- مستقیم:
sudo: a password is required

* در قاعده‌ی sudoers هر چیزی را شامل / و .. می‌شود؛ پس /var/log/../../etc/shadow هم با آن می‌خواند و dev2 در عمل همه‌ی فایل‌های سیستم را می‌خواند. راه‌حل: مسیر دقیق بده (/usr/bin/cat /var/log/syslog) یا بهتر، کاربر را عضو گروه adm کن که لاگ‌ها را بخواند، بدون sudo.

۵) ویرایش مستقیم /etc/sudoers و غلط تایپی

Section titled “۵) ویرایش مستقیم /etc/sudoers و غلط تایپی”

این بار در سرور systemd‌دار آزمایشگاه (یک ماشین دورریختنی) عمداً بدون visudo یک خط خراب به فایل اصلی اضافه می‌کنم: برای eve قاعده‌ای بنویسم و دو نقطه‌ی NOPASSWD: را فراموش کنم:

Terminal window
cp /etc/sudoers /tmp/sudoers.bak
useradd -m -s /bin/bash eve 2>/dev/null
echo "eve ALL=(ALL) NOPASSWD ALL" >> /etc/sudoers
echo "--- یک کاربر دیگر (ali) که فقط sudo می‌زند:"
su - ali -c 'sudo -n true'; echo "کد خروجی ali: $?"
echo "--- eve که قاعده‌اش خراب است:"
su - eve -c 'sudo -n true'; echo "کد خروجی eve: $?"
cp /tmp/sudoers.bak /etc/sudoers && chmod 440 /etc/sudoers
userdel -r eve 2>/dev/null; rm /tmp/sudoers.bak
خروجی
--- یک کاربر دیگر (ali) که فقط sudo می‌زند:
/etc/sudoers:58:24: syntax error
eve ALL=(ALL) NOPASSWD ALL
^~~
کد خروجی ali: 0
--- eve که قاعده‌اش خراب است:
/etc/sudoers:58:24: syntax error
eve ALL=(ALL) NOPASSWD ALL
^~~
sudo: a password is required
کد خروجی eve: 1

هر اجرای sudo، برای همه، خطای نحو را چاپ می‌کند، و قاعده‌ای که غلط داشت اجرا نمی‌شود: eve هیچ دسترسی‌ای ندارد (و اگر آن خط، دسترسی خودِ تو بود، همین‌جا sudo را از دست می‌دادی؛ روی Ubuntu که root رمز ندارد یعنی نیاز به حالت بازیابی سیستم). راه‌حل: همیشه با sudo visudo و ترجیحاً فایل جدا در sudoers.d کار کن؛ مثال ۷.

✎ تمرینآسان

با id و sudo id نشان بده «من» چه کسی هستم و با sudo چه کسی می‌شوم، UID root چند است، و با sudo -l فهرست اجازه‌هایت را بگیر.

دیدن جواب
Terminal window
echo "من: $(id -un) | با sudo: $(sudo id -un) | UID با sudo: $(sudo id -u)"
sudo -l | tail -2
خروجی
من: ali | با sudo: root | UID با sudo: 0
User ali may run the following commands on pc:
(ALL) NOPASSWD: ALL
✎ تمرینمتوسط

تمرین اصلی: به کاربری که می‌سازی دسترسی sudo بده و تست کن. کاربر dev3 را با رمز بساز، ثابت کن قبل از افزودن به گروه sudo نمی‌تواند، بعد او را اضافه کن و ثابت کن حالا می‌تواند. (برای رمزدادن در اسکریپت: echo رمز | sudo -S؛ فقط برای آزمایش.)

دیدن جواب
Terminal window
PW=lab-pass
sudo useradd -m -s /bin/bash dev3
echo "dev3:$PW" | sudo chpasswd
echo "گروه‌ها قبل: $(id -nG dev3)"
sudo su - dev3 -c "echo $PW | sudo -S -p '' whoami" 2>&1
sudo usermod -aG sudo dev3
echo "گروه‌ها بعد: $(id -nG dev3)"
sudo -l -U dev3 | tail -2
sudo su - dev3 -c "echo $PW | sudo -S -p '' whoami" 2>&1
sudo userdel -r dev3 2>/dev/null
خروجی
گروه‌ها قبل: dev3
dev3 is not in the sudoers file.
گروه‌ها بعد: dev3 sudo
User dev3 may run the following commands on pc:
(ALL : ALL) ALL
root

قبل از usermod -aG sudo خطای is not in the sudoers file و بعد از آن root. (-p '' یعنی پیام رمزپرسی چاپ نشود.)

✎ تمرینسخت

یک حساب deploy بساز که فقط بتواند touch /var/tmp/deploy.flag را با root اجرا کند، نه هیچ دستور دیگری. قاعده را قبل از نصب با visudo -cf بررسی کن، با install -m 440 در sudoers.d بگذار، و هم مجاز و هم غیرمجاز را تست کن.

دیدن جواب
Terminal window
sudo useradd -m -s /bin/bash deploy
echo 'deploy ALL=(root) NOPASSWD: /usr/bin/touch /var/tmp/deploy.flag' > /tmp/deploy.sudoers
sudo visudo -cf /tmp/deploy.sudoers
sudo install -m 440 -o root -g root /tmp/deploy.sudoers /etc/sudoers.d/deploy
sudo -l -U deploy | tail -2
echo "--- مجاز:"
sudo -u deploy sudo -n /usr/bin/touch /var/tmp/deploy.flag && ls -l /var/tmp/deploy.flag
echo "--- غیرمجاز (فایل دیگر):"
sudo -u deploy sudo -n /usr/bin/touch /var/tmp/other.flag
sudo rm -f /var/tmp/deploy.flag /etc/sudoers.d/deploy /tmp/deploy.sudoers
sudo userdel -r deploy 2>/dev/null
خروجی
/tmp/deploy.sudoers: parsed OK
User deploy may run the following commands on pc:
(root) NOPASSWD: /usr/bin/touch /var/tmp/deploy.flag
--- مجاز:
-rw-r--r-- 1 root root 0 Oct 3 20:44 /var/tmp/deploy.flag
--- غیرمجاز (فایل دیگر):
sudo: a password is required

فایل ساخته‌شده مالکش root است (دستور با هویت root اجرا شد) و دستور دیگر برای deploy مجاز نیست.

؟ آزمونک
  1. UID کاربر root چند است؟

  2. وقتی sudo رمز می‌خواهد، رمز چه کسی را باید داد؟

  3. روی Ubuntu برای دادن دسترسی sudo به کاربر dev1 کدام دستور درست است؟

  4. چرا ویرایش /etc/sudoers را با visudo انجام می‌دهیم؟

  5. کدام قاعده به اصل کمترین دسترسی نزدیک‌تر است؟

  6. چرا sudo echo x > /etc/file خطا می‌دهد؟

  • root (UID صفر) قاعده‌های دسترسی را دور می‌زند؛ کار روزمره با او خطرناک و بی‌ردپاست.
  • sudo دستور فقط همان یک دستور را با root اجرا می‌کند، رمز خودت را می‌خواهد (چند دقیقه یادش می‌ماند؛ sudo -k فراموشش می‌کند) و هر دستور را با نام تو ثبت می‌کند.
  • sudo -i شل ورود کامل root است؛ su - رمز کاربر مقصد را می‌خواهد و روی Ubuntu برای root کار نمی‌کند (رمز قفل است).
  • دسترسی sudo با عضویت در گروه sudo (usermod -aG sudo کاربر) یا قاعده‌ی جدا در /etc/sudoers.d/ داده می‌شود؛ sudo -l -U کاربر اجازه‌ها را نشان می‌دهد.
  • قاعده‌ها را فقط با sudo visudo (ترجیحاً -f /etc/sudoers.d/نام، بدون نقطه در نام، دسترسی 0440) و با visudo -c بررسی کن.
  • اصل کمترین دسترسی: دستور مشخص با مسیر کامل، نه ALL، نه *، نه ویرایشگر؛ NOPASSWD فقط جاهای ایزوله.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
sudo دستوریک دستور با root (رمز خودت)
sudo -u کاربر دستوراجرا با هویت کاربر دیگر
sudo -iشل ورود کامل root (در پایان exit)
sudo -sشل root در همان پوشه
sudo !!دستور قبلی را دوباره با sudo
sudo -l / sudo -l -U کاربرفهرست اجازه‌ها
sudo -kپاک‌کردن رمز کش‌شده
su - کاربرورود کامل با رمز آن کاربر
sudo usermod -aG sudo کاربردادن دسترسی sudo (Debian/Ubuntu)
sudo visudoویرایش امن /etc/sudoers
sudo visudo -f /etc/sudoers.d/نامویرایش امن یک فایل جدا
sudo visudo -c / visudo -cf فایلبررسی نحو
grep COMMAND= /var/log/auth.logردگیری دستورهای sudo (Debian/Ubuntu)