SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

نحوه ثبت و حسابرسی دستورات کاربران در لینوکس

تاریخچه شل قابل تغییر است. برای امنیت واقعی از sudo logging، ضبط نشست، shell hooks یا auditd با قوانین execve استفاده کنید و لاگ‌ها را برای تحلیل به سرور دیگری بفرستید.

چه چیزی واقعاً دستورات اجرا شده توسط کاربران روی سرور شما را ثبت می‌کند

برای حسابرسی دستوراتی که کاربران روی سرور شما اجرا کرده‌اند، به رکوردی نیاز دارید که کاربر نتواند آن را ویرایش کند. تاریخچه شل (Shell history) چنین رکوردی نیست. این فایل صرفاً یک ابزار راحتی است که مالکیت آن در اختیار همان حسابی است که آن را نوشته، و هر کسی که به آن شل دسترسی داشته باشد می‌تواند آن را غیرفعال یا حذف کند.

چهار لایه وجود دارند که یک رکورد واقعی نگه می‌دارند و هر کدام هزینه‌ای دارند. sudo برای هر دستور یک خط در syslog می‌نویسد. قابلیت sudo I/O logging کل یک نشست (session) را برای یک حساب کاربری ضبط می‌کند. یک shell hook مانند PROMPT_COMMAND آنچه را که یک کاربر تعاملی bash تایپ کرده است، ثبت می‌کند. زیرسیستم audit هسته (kernel audit subsystem) خودِ فراخوانی سیستمی execve را ثبت می‌کند؛ به همین دلیل است که این تنها لایه‌ای است که تمام پردازش‌ها را می‌بیند. این راهنما از این نردبان بالا می‌رود، می‌گوید هر لایه کجا متوقف می‌شود و در نهایت به بخشی می‌رسد که تعیین می‌کند آیا این ثبت‌ها ارزشی دارند یا خیر: انتقال رکوردها از ماشین، پیش از آنکه شخصی که در حال حسابرسی او هستید به آن‌ها دسترسی پیدا کند.

یک هشدار پیش از شروع: زیرسیستم audit یک عملیات در سطح هسته است، بنابراین هیچ‌کدام از این موارد را نمی‌توان درون containerهایی که از هسته میزبان (host kernel) استفاده می‌کنند، تست کرد. این دستورات را روی یک KVM VPS اجرا کنید که هسته آن در اختیار خودتان است.

چرا تاریخچه shell یک مسیر حسابرسی (audit trail) نیست

~/.bash_history به چهار دلیل عادی به عنوان مدرک شکست می‌خورد و هیچ‌کدام از آن‌ها نیازی به یک مهاجم باهوش ندارند.

این فایل متعلق به کاربر است. مجوز این فایل 600 است و مالکیت آن در اختیار همان حساب کاربری است، بنابراین rm ~/.bash_history به هیچ امتیازی نیاز ندارد. باز کردن آن در یک ویرایشگر و حذف بیست خطی که اهمیت دارند نیز به همین سادگی است.

این فایل هنگام خروج از shell نوشته می‌شود. نشست (session)ای که با kill -9 $$ یا با قطع اتصال به پایان برسد، چیزی نمی‌نویسد. اجرای history -c پیش از exit نیز همین نتیجه را دارد و طوری به نظر می‌رسد که گویی هیچ اتفاقی نیفتاده است.

با یک کلمه غیرفعال می‌شود. unset HISTFILE باعث می‌شود فایل برای آن نشست نوشته نشود. set +o history ضبط را بلافاصله متوقف می‌کند. HISTCONTROL=ignorespace هر دستوری را که با یک فاصله (space) شروع شود، پنهان می‌کند. تمام این موارد در man bash وجود دارند، زیرا این قابلیت برای کنترل توسط کاربر طراحی شده است.

این فایل چیزی را که تایپ شده ضبط می‌کند، نه چیزی را که اجرا شده است. وجود یک alias یا یک shell function به این معنی است که متن موجود در فایل، همان برنامه‌ای نیست که kernel اجرا کرده است.

همچنین هیچ مهر زمانی (timestamp) وجود ندارد، مگر اینکه HISTTIMEFORMAT در زمان نوشته شدن ورودی تنظیم شده باشد، زیرا bash خطوط نشانگر #1755043200 خود را فقط زمانی می‌نویسد که آن متغیر تنظیم شده باشد.

در یک ورود به سیستم مشترک، این فایل نمی‌تواند به شما بگوید چه کسی دستور را اجرا کرده است. سه نفر که از یک حساب deploy استفاده می‌کنند، یک فایل درهم‌تنیده تحت یک uid واحد ایجاد می‌کنند. هیچ لایه لاگ‌گیری نمی‌تواند یک عمل را به یک انسان نسبت دهد وقتی دو انسان در یک uid مشترک هستند؛ این همان استدلال عملی برای استفاده از یک حساب کاربری بدون امتیاز برای هر شخص به جای ورود مشترک است.

تاریخچه shell در وظیفه اصلی خود خوب عمل می‌کند، که همان کمک به شما برای تایپ مجدد دستور دیروز است. از آن به عنوان یک سرنخ استفاده کنید. هرگز آن را به عنوان مدرک ارائه ندهید.

ثبت لاگ‌های sudo و محدودیت‌های آن

ابزار sudo برای هر دستوری که اجرا می‌کند، یک خط در facility مربوط به authpriv در syslog ثبت می‌کند.

sudo grep 'sudo:' /var/log/auth.log | tail -5
journalctl -t sudo -n 5

هر خط شامل نام کاربر، ترمینال، دایرکتوری کاری، کاربر مقصد و دستور اجرا شده است:

sudo:    alice : TTY=pts/0 ; PWD=/home/alice ; USER=root ; COMMAND=/usr/bin/apt update

اگر /var/log/auth.log وجود نداشته باشد، یعنی rsyslog روی آن ایمیج نصب نیست و سوابق تنها در journal موجود هستند. پیش از تکیه بر journal، بررسی کنید که آیا داده‌های آن فرار (volatile) هستند یا خیر:

journalctl --list-boots

اگر تنها بوت فعلی لیست شده باشد، به این معناست که /var/log/journal وجود ندارد؛ بنابراین journal در مسیر /run قرار دارد و با هر بار reboot، تمام لاگ‌ها پاک می‌شوند. برای دائمی کردن آن از دستور زیر استفاده کنید:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

و اما محدودیت؛ sudo تنها دستوری را ثبت می‌کند که از آن خواسته شده اجرا شود. این ابزار فعالیت‌های بعدی آن دستور را لاگ نمی‌کند. بنابراین، ردپای فعالیت در یک نقطه به پایان می‌رسد:

sudo -i

لاگ تنها یک رکورد برای shell ثبت می‌کند. هر دستوری که داخل آن root shell تایپ شود، برای sudo نامرئی است، زیرا sudo دیگر در مسیر اجرا قرار ندارد. دستورات sudo su -، sudo bash و sudo vim /etc/shadow به همراه :!bash همگی ساختار مشابهی دارند. قانونی در sudoers که اجازه اجرای هر برنامه‌ای با قابلیت shell escape (مانند vim یا find) را می‌دهد، در واقع دسترسی root بدون ثبت لاگ اعطا می‌کند. پیش از اعتماد به خطوط لاگ یک حساب کاربری، بررسی کنید که آن حساب واقعاً به چه منابعی دسترسی دارد:

sudo -l -U alice

ضبط کامل نشست برای یک حساب کاربری

ابتدا مشخص کنید از کدام نسخه sudo استفاده می‌کنید، زیرا این قابلیت در بازنویسی Rust وجود ندارد:

sudo --version | head -1

اگر خروجی نام sudo-rs را نشان می‌دهد، از این بخش صرف‌نظر کرده و از زیرسیستم audit استفاده کنید. مستندات رسمی اوبونتو برای انتشار‌های 25.10 و 26.04، قابلیت I/O logging و sudoreplay را پشتیبانی‌نشده اعلام کرده‌اند و این موضوع تا آگوست 2026 همچنان صادق بوده است. این مسئله اهمیت دارد زیرا sudo-rs در آن نسخه‌ها sudo پیش‌فرض است، بنابراین یک ارتقا می‌تواند کنترلی را که تصور می‌کردید دارید، حذف کند. مطالعه فهرست کامل تغییرات رفتاری sudo-rs پیش از برنامه‌ریزی برای هرگونه لاگ‌گیری مبتنی بر sudo توصیه می‌شود.

با استفاده از نسخه اصلی sudo که همچنان در Ubuntu 24.04 LTS ارائه می‌شود، I/O logging را برای یک حساب کاربری فعال کنید:

sudo visudo -f /etc/sudoers.d/iolog
Defaults:deploy log_output
Defaults!/usr/bin/sudoreplay !log_output

به‌جای ویرایشگر متن از visudo استفاده کنید، زیرا این ابزار از ذخیره فایلی که دارای خطای نحوی باشد جلوگیری می‌کند. یک فایل sudoers خراب، دسترسی همه کاربران به sudo را مسدود می‌کند. سپس برای بازپخش یک نشست:

sudo sudoreplay -l user=deploy
sudo sudoreplay 000001

sudoreplay -l نشست‌ها را به همراه شناسه آن‌ها فهرست می‌کند و اگر log_output هرگز برای آن کاربر اعمال نشده باشد، خروجی نمایش نمی‌دهد. هزینه این کار: هر بایتی که از ترمینال عبور می‌کند در مسیر /var/log/sudo-io ذخیره می‌شود، بنابراین یک نشست پرحجم، فضای زیادی اشغال می‌کند. خط دوم sudoers از ضبط شدنِ خودِ فرآیند بازپخش جلوگیری می‌کند. هزینه واقعی، افشای اطلاعات حساس است؛ زیرا لاگ I/O هر آنچه تایپ یا چاپ شده باشد، از جمله رمز عبوری که در یک اعلان (prompt) داخل نشست وارد شده را نگه می‌دارد، بنابراین این لاگ‌ها به همان سطح حفاظتی نیاز دارند که برای ذخیره‌سازی رمز عبور اعمال می‌شود. پوشش این روش نیز محدود است. این ابزار فقط دستوراتی را می‌بیند که از طریق sudo اجرا شده‌اند. کسی که وارد سیستم شده و تمام کارها را با حساب کاربری خودش انجام می‌دهد، اصلاً ضبط نخواهد شد.

هوک‌های شل و نحوه دقیق دور زدن آن‌ها

دستورالعملی که برای «ثبت لاگ تمام دستورات» دست‌به‌دست می‌شود، یک هوک PROMPT_COMMAND است که در /etc/profile.d/ قرار می‌گیرد:

# /etc/profile.d/00-cmdlog.sh
PROMPT_COMMAND='logger -p local6.info -t cmdlog "$(whoami) $$ $(history 1 | sed "s/^ *[0-9]* *//")"'

bash دستور PROMPT_COMMAND را پیش از نمایش هر prompt اجرا می‌کند، بنابراین یک خط به محض تایپ شدن به syslog می‌رسد و نه در لحظه خروج؛ همچنین logger از طریق دیمون لاگ سیستم می‌نویسد، بنابراین مجوزهای فایل کاربر هرگز در این فرآیند دخیل نمی‌شوند. یک شل لاگین جدید باز کنید و با sudo tail -f /var/log/syslog، یا در ایمیجی که فاقد rsyslog است با journalctl -t cmdlog -f، آن را بررسی کنید.

سپس این روش از کار می‌افتد؛ این اتفاق به پنج روش رخ می‌دهد که هر کدام را می‌توانید در یک دقیقه بازتولید کنید.

  • شل‌های غیرتعاملی (non-interactive) هرگز prompt نمایش نمی‌دهند. ssh you@server 'id' دستور را اجرا کرده و بازمی‌گردد، و هیچ چیزی ثبت نمی‌شود، زیرا PROMPT_COMMAND هرگز ارزیابی نشده است.
  • این یک متغیر است. unset PROMPT_COMMAND آن را برای باقی نشست (session) غیرفعال می‌کند و نیازی به هیچ امتیازی ندارد.
  • این فایل توسط شل‌های لاگین خوانده می‌شود. bash --noprofile --norc هرگز /etc/profile.d/ را source نمی‌کند.
  • این قابلیت مختص bash است. zsh، sh، python3 -c 'import os; os.system("id")' و :!id در داخل vim همگی برنامه‌هایی را اجرا می‌کنند که هیچ هوک prompt در bash هرگز آن‌ها را نخواهد دید.
  • این روش خط را همان‌طور که تایپ شده ثبت می‌کند، بنابراین یک alias یا یک function همچنان دستوری را که واقعاً اجرا شده است، مخفی نگه می‌دارد.

از هوک شل به عنوان یک ابزار راحتی استفاده کنید. این ابزار برای کاربران همکاری‌کننده به پرسش «سه‌شنبه گذشته چه دستوری اجرا کردم» پاسخ می‌دهد. اجازه ندهید یک چک‌لیست از آن به عنوان یک کنترل امنیتی یاد کند.

زیرسیستم audit هسته، تمام فراخوانی‌های execve را مشاهده می‌کند

زیرسیستم audit در لینوکس که توسط دیمون auditd هدایت می‌شود، تنها لایه‌ای است که کاربر نمی‌تواند از آن عبور کند، زیرا رکورد در لحظه اجرای syscall درون هسته ایجاد می‌شود. اگر فرآیندی برنامه‌ای را اجرا کند، یک رویداد ثبت می‌شود. شل، زبان برنامه‌نویسی و وجود یا عدم وجود ترمینال تفاوتی در این موضوع ایجاد نمی‌کنند.

sudo apt update && sudo apt install -y auditd audispd-plugins
sudo systemctl enable --now auditd
sudo auditctl -s

دستور auditctl -s وضعیت دیمون را نمایش می‌دهد. enabled 1 با یک pid غیر صفر به معنای در حال اجرا بودن آن است و lost 0 نشان می‌دهد که هنوز هیچ رکوردی از دست نرفته است. این شمارنده lost را به خاطر بسپارید، زیرا بعداً به آن باز خواهیم گشت.

auid فیلدی است که audit را ارزشمند می‌کند. PAM هنگام شروع یک نشست، یک login uid تنظیم می‌کند و هسته آن را از آن لحظه به بعد برای تمام فرآیندهای فرزند حفظ می‌کند. مقدار خود را بررسی کنید:

cat /proc/self/loginuid

یک نشست تعاملی SSH مقدار uid شما را چاپ می‌کند، زیرا /etc/pam.d/sshd شامل pam_loginuid.so است. مقدار 4294967295 به این معنی است که loginuid هرگز تنظیم نشده است، که برای فرآیندی که توسط یک دیمون سیستمی در زمان بوت شروع شده، طبیعی است. نکته مهم این است که sudo -i آن را تغییر نمی‌دهد: یک شل root که توسط alice باز شده همچنان auid 1000 را حمل می‌کند، بنابراین هر دستوری درون آن به alice قابل انتساب است. این دقیقاً همان شکافی است که sudo باقی می‌گذارد. تغییر loginuid پس از تنظیم، نیازمند CAP_AUDIT_CONTROL است که کاربران عادی آن را ندارند، و sudo auditctl --loginuid-immutable این دسترسی را حتی برای root تا بوت بعدی مسدود می‌کند.

بررسی کنید که /etc/pam.d/sshd، /etc/pam.d/login و /etc/pam.d/cron هر کدام شامل pam_loginuid.so باشند، در غیر این صورت رویدادها بدون انتساب به هیچ کاربری ثبت خواهند شد. این همان لیست فایلی است که هنگام ایمن‌سازی دسترسی SSH روی یک VPS با آن سروکار دارید، بنابراین هر دو کار را با هم انجام دهید.

مجموعه قوانین اولیه برای auditd

قوانین در /etc/audit/rules.d/*.rules قرار دارند. augenrules آن‌ها را به ترتیب نام فایل در یک لیست واحد ادغام می‌کند و ترتیب، تعیین‌کننده رفتار است، زیرا هسته در اولین قانون منطبق متوقف می‌شود. پیش از افزودن هر چیزی، محتوای موجود را بخوانید، زیرا یک -D در فایلی که بعداً می‌آید، تمام موارد بارگذاری‌شده قبلی را پاک می‌کند.

ls /etc/audit/rules.d/
cat /etc/audit/rules.d/audit.rules

سپس /etc/audit/rules.d/50-exec.rules را بنویسید:

## Suppressions first: the kernel takes the first matching rule.
-a never,exit -F arch=b64 -S execve -F exe=/usr/bin/dpkg
-a never,exit -F arch=b64 -S execve -F exe=/usr/bin/dpkg-deb
-a never,exit -F arch=b64 -S execve -F exe=/usr/bin/dpkg-query
-a never,exit -F arch=b64 -S execve -F exe=/usr/bin/dpkg-split
-a never,exit -F arch=b64 -S execve -F exe=/usr/bin/dpkg-trigger

## Every program started by a logged-in human.
-a always,exit -F arch=b64 -S execve,execveat -F auid>=1000 -F auid!=unset -k exec
-a always,exit -F arch=b32 -S execve,execveat -F auid>=1000 -F auid!=unset -k exec

## Changes to who may become root.
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity

## Changes to the audit configuration itself.
-w /etc/audit/ -p wa -k auditconfig

آن را بارگذاری کرده و تأیید کنید:

sudo augenrules --load
sudo auditctl -l

auditctl -l چاپ مجدد قوانین شما به این معنی است که آن‌ها فعال هستند. No rules به معنای شکست در بارگذاری است و journalctl -u auditd -n 20 نام فایل و خطی که توسط تحلیل‌گر رد شده است را مشخص می‌کند. نسخه‌های قدیمی‌تر فضای کاربری audit، کلمه کلیدی unset را نمی‌شناسند. اگر لودر در مورد آن فیلد خطا داد، به جای آن -F auid!=4294967295 را بنویسید که همان مقدار را به صورت کامل بیان می‌کند.

اکنون رویدادها را بازخوانی کنید:

sudo ausearch -k exec -ts recent -i | tail -40
sudo ausearch -ul 1000 -ts today -i
sudo aureport -k --summary -i

-i شناسه‌های کاربری (uid) و شماره‌های syscall را به نام تبدیل می‌کند و در عمل اختیاری نیست. -ts recent ده دقیقه اخیر را پوشش می‌دهد. هر اجرا به صورت گروهی از رکوردها می‌رسد: یک رکورد SYSCALL که شامل uid، auid، وضعیت خروج و کلید است، یک رکورد EXECVE با لیست کامل آرگومان‌ها، به علاوه رکوردهای CWD و PATH برای زمینه (context).

یک محدودیت صادقانه، زیرا افراد را غافلگیر می‌کند: audit، فراخوانی‌های سیستمی (syscall) را ثبت می‌کند و یک دستور داخلی shell (builtin) هیچ syscall مستقلی ندارد. cd /root هیچ برنامه‌ای را اجرا نمی‌کند. echo evil >> /etc/passwd که در خط فرمان bash تایپ می‌شود نیز هیچ برنامه‌ای را اجرا نمی‌کند، زیرا هم echo و هم تغییر مسیر (redirect) در داخل فرآیند shell که از قبل در حال اجراست، رخ می‌دهند. بنابراین قوانین execve برنامه‌ها را می‌بینند و قوانین -w عملیات نوشتن را مشاهده می‌کنند. هیچ‌کدام به تنهایی کافی نیستند.

در نهایت، پیکربندی را قفل کنید:

## /etc/audit/rules.d/99-finalize.rules
-e 2

-e 2 مجموعه قوانین را تا reboot بعدی غیرقابل تغییر (immutable) می‌کند. پس از بارگذاری آن، auditctl -s مقدار enabled 2 را گزارش می‌دهد و هرگونه تلاش برای افزودن یا حذف یک قانون با خطای Operation not permitted مواجه می‌شود، حتی برای کاربر root. این فایل را در آخرین مرحله اضافه کنید و انتظار داشته باشید که برای هر تغییر در قوانین، نیاز به reboot باشد. این معامله، هدف اصلی است: مجموعه قانونی که هر کسی بتواند بی‌سروصدا آن را خاموش کند، مدرک محسوب نمی‌شود.

لاگ حسابرسی که کسی آن را نمی‌خواند، صرفاً یک مستند برای انطباق است

حالت شکست auditd این نیست که رویدادها را ثبت نمی‌کند؛ بلکه این است که آن‌قدر داده ثبت می‌کند که هیچ‌کس به آن‌ها نگاه نمی‌کند و در نتیجه، لاگ تنها برای پر کردن چک‌لیست وجود دارد، نه برای پاسخ دادن به یک پرسش.

پیش از هرگونه تنظیم، محاسبات را روی سرور خود انجام دهید:

sudo aureport -k --summary -i
sudo du -sh /var/log/audit

یک sudo apt upgrade واحد، هزاران پردازش کوتاه‌مدت اجرا می‌کند و هر کدام از آن‌ها auid شما را به همراه دارند؛ بنابراین یک به‌روزرسانی پکیج می‌تواند از حجم کل تایپ یک هفتهٔ یک انسان بیشتر باشد. به همین دلیل است که در حذف‌های بالا، نام dpkg و ابزارهای کمکی آن آمده است. حذف را بر اساس فایل اجرایی انجام دهید، نه کاربر: یک استثنا برای /usr/bin/dpkg حفره‌ای است که می‌توانید در یک جمله توصیفش کنید، در حالی که استثنا برای یک حساب کاربری، حفره‌ای است که دقیقاً به شکل همان چیزی است که سعی در شناسایی‌اش داشتید.

کلید -k در هر قانون، همان چیزی است که باعث می‌شود لاگ یک ماه بعد قابل جستجو باشد. ausearch -k sudoers پرسشی است که پاسخی دارد. ausearch بدون فیلتر، دیواری از متن است که شما را عادت می‌دهد دیگر آن را نخوانید. اگر جمع‌آوری‌کنندهٔ لاگ شما به جای فرمت اصلی، به JSON نیاز دارد، laurel یک پلاگین برای auditd است که هر رویداد را به صورت یک آبجکت JSON با آرگومان‌های رمزگشایی‌شده بازنویسی می‌کند. این پلاگین مانند هر پلاگین دیگری در /etc/audit/plugins.d/ ثبت می‌شود و auditd تغییرات پلاگین را در sudo pkill -HUP auditd اعمال می‌کند.

هزینه‌های واقعی auditd

هر فراخوانی سیستمی (syscall) که با قوانین مطابقت داشته باشد، به رکوردی تبدیل می‌شود که هسته آن را قالب‌بندی کرده و به فضای کاربری (userspace) تحویل می‌دهد. هزینه این کار در دو بخش نمود پیدا می‌کند و هر دو مورد، به‌جای حدس زدن بر اساس اعداد منتشرشده توسط دیگران، روی بار کاری خودتان قابل اندازه‌گیری هستند.

  • پردازنده و تأخیر. ماشینی که دائماً فرآیند ایجاد می‌کند (fork)، مانند یک میزبان build یا یک runner برای CI، به ازای هر exec یک رکورد تولید می‌کند. هنگامی که backlog هسته پر شود، --backlog_wait_time باعث می‌شود هسته، فرآیندی که رویداد را ایجاد کرده تا زمان خالی شدن فضا متوقف کند؛ بنابراین audit به‌جای درصد مصرف CPU، خود را به شکل کند شدن buildها نشان می‌دهد. در حین بار کاری واقعی، backlog و lost را در sudo auditctl -s زیر نظر بگیرید. افزایش lost به این معنی است که رکوردها از دست رفته‌اند و لاگی که دارای شکاف‌های پنهان باشد، از نداشتن لاگ بدتر است، زیرا همچنان به آن اعتماد خواهید کرد.
  • دیسک. /etc/audit/auditd.conf را مطالعه کنید و آگاهانه تصمیم بگیرید که هنگام پر شدن دیسک چه اتفاقی بیفتد، زیرا مقادیر پیش‌فرض ارائه‌شده صرفاً یک پیشنهاد هستند. max_log_file، num_logs و max_log_file_action چرخش (rotation) لاگ‌ها را کنترل می‌کنند. space_left_action، admin_space_left_action و disk_full_action شرایط اضطراری را مدیریت می‌کنند و برخی از اقدامات موجود، از جمله halt و single، به‌جای از دست دادن یک رکورد، کل ماشین را از دسترس خارج می‌کنند.

خط -f در /etc/audit/rules.d/audit.rules همان تصمیم را در سطح هسته اعمال می‌کند: -f 1 خطای audit را به syslog گزارش می‌دهد و -f 2 باعث panic هسته می‌شود. گزینه 2 را تنها در صورتی انتخاب کنید که واقعاً ترجیح می‌دهید سرور را از دست بدهید تا اینکه یک رکورد را از دست ندهید. روی یک VPS که سرویس‌های حیاتی روی آن اجرا می‌شوند، از چرخش لاگ استفاده کنید و مشکل ذخیره‌سازی را به خارج از آن ماشین منتقل کنید.

انتقال لاگ‌ها از سرور به خارج، تقریباً به‌صورت بلادرنگ

این بخشی است که گزارش‌های حوادث امنیتی دائماً بر اهمیت آن تأکید می‌کنند. لاگ‌هایی که روی سرورِ نفوذشده باقی می‌مانند، توسط هر کسی که به آن دسترسی پیدا کرده باشد، قابل ویرایش هستند. کاربر root می‌تواند /var/log/auth.log را بازنویسی کند، /var/log/audit/audit.log را حذف کند و دیمون مربوطه را متوقف سازد. -e 2 از تخلیه شدن قوانین جلوگیری می‌کند، اما در برابر rm هیچ کاری انجام نمی‌دهد. هر لایه امنیتی بالاتر، تنها در صورتی شواهد معتبری ارائه می‌دهد که یک نسخه از آن ابتدا از ماشین خارج شده باشد.

انتقال داده‌های Audit توسط پلاگین audisp-remote از بسته audispd-plugins انجام می‌شود. آن را در /etc/audit/plugins.d/au-remote.conf فعال کنید:

active = yes
direction = out
path = /usr/sbin/audisp-remote
type = always
format = string

پیش از reload کردن، path را با command -v audisp-remote مطابقت دهید، زیرا مسیر اشتباه هیچ خروجی‌ای جز یک خط در journal ایجاد نمی‌کند. مقادیر remote_server و port را در /etc/audit/audisp-remote.conf تنظیم کنید و در سمت جمع‌آوری‌کننده (collector)، مقدار tcp_listen_port = 60 را در auditd.conf مربوط به خودش قرار دهید. با دستور sudo pkill -HUP auditd سرویس را reload کنید. در بسیاری از ایمیج‌ها، دستور systemctl restart auditd رد می‌شود، زیرا فایل unit مقدار RefuseManualStop=yes را تنظیم کرده است؛ بنابراین ارسال سیگنال، روش مطمئن‌تری است.

گزینه دیگر، وارد کردن رویدادهای audit به جریان syslog است که هم‌اکنون آن را forward می‌کنید. /etc/audit/plugins.d/syslog.conf به همراه active = no ارائه می‌شود. آن را روی yes تنظیم کرده و reload کنید؛ حالا رویدادهای audit به خطوط مربوط به sudo و سایر لاگ‌ها می‌پیوندند. سپس کل آن‌ها را با استفاده از rsyslog و از طریق TLS (امنیت لایه انتقال) ارسال کنید که نیازمند بسته rsyslog-gnutls است:

# /etc/rsyslog.d/60-forward.conf
*.* action(type="omfwd"
    target="logs.example.net" port="6514" protocol="tcp"
    StreamDriver="gtls" StreamDriverMode="1"
    StreamDriverAuthMode="x509/name"
    StreamDriverPermittedPeers="logs.example.net"
    action.resumeRetryCount="-1"
    queue.type="linkedList" queue.filename="fwd" queue.saveOnShutdown="on")

تنظیمات صف (queue) بخش مهم ماجراست. action.resumeRetryCount="-1" عملیات ارسال را تا بی‌نهایت تکرار می‌کند و صفِ مبتنی بر دیسک با queue.saveOnShutdown="on"، رکوردها را در زمانی که collector در دسترس نیست نگه می‌دارد و پس از برقراری مجدد ارتباط، آن‌ها را ارسال می‌کند. بدون این دو مورد، reboot شدن collector باعث ایجاد حفره در شواهد شما می‌شود و هیچ هشداری هم مبنی بر وجود این حفره دریافت نخواهید کرد. تنظیمات را با sudo systemctl restart rsyslog اعمال کنید و پیش از اعتماد کامل به سیستم، تأیید کنید که رکوردها واقعاً به collector می‌رسند.

تنها یک حلقه باقی می‌ماند که باید بسته شود: collector باید ماشینی باشد که افراد تحت نظارت، امکان ورود به آن را نداشته باشند. اگر همان گروه ادمین، دسترسی root روی سرور لاگ داشته باشند، شما فقط فایل را کپی کرده‌اید و آن را ایمن نکرده‌اید. از اعتبارنامه‌های جداگانه، کلیدهای مجزا و در حالت ایده‌آل، یک حساب کاربری جداگانه در سرویس‌دهنده استفاده کنید. این همان منطقی است که باعث می‌شود ایجاد یک روش متمرکز برای مدیریت چندین سرور لینوکسی پیش از آنکه به آن نیاز پیدا کنید، ارزشمند باشد و تفاوت بین یک ساعت اولِ مفید و یک ساعتِ بی‌فایده را هنگام کار روی یک VPS نفوذشده رقم می‌زند.

بررسی عدم امکان بازنویسی رکورد توسط کاربر عادی

به‌جای فرض کردن، ادعا را آزمایش کنید. از یک حساب کاربری معمولی و بدون دسترسی sudo:

echo test >> /var/log/auth.log
cat /var/log/audit/audit.log
auditctl -D
id -nG

به ترتیب موارد زیر را انتظار داشته باشید: Permission denied، زیرا مالک auth.log کاربر syslog با گروه adm است و مجوز آن 640 می‌باشد؛ مجدداً Permission denied، زیرا لاگ audit دارای مجوز 600 و متعلق به root است؛ خطای عدم اجازه برای اجرا، زیرا تغییر قوانین audit نیازمند CAP_AUDIT_CONTROL است؛ و فهرستی از گروه‌ها که شامل adm یا systemd-journal نیست.

آخرین بررسی، موردی است که افراد در آن دچار اشتباه می‌شوند. عضویت در adm دسترسی خواندن به /var/log/auth.log را فراهم می‌کند و عضویت در systemd-journal دسترسی خواندن به کل journal را می‌دهد. هیچ‌کدام دسترسی نوشتن نمی‌دهند، بنابراین هیچ‌کدام اجازه دست‌کاری را صادر نمی‌کنند. هر دو به فرد اجازه می‌دهند تمام خطوط احراز هویت روی سیستم را بخواند؛ این تصمیمی است که باید آگاهانه اتخاذ شود، نه با کپی کردن یک خط usermod -aG از پاسخ‌های موجود در انجمن‌ها.

در نهایت، دو موردی که باید پس از reboot باقی بمانند را تأیید کنید:

sudo auditctl -s
systemctl is-enabled auditd

enabled 2 به این معنی است که مجموعه قوانین تا بوت بعدی قفل شده است. enabled از دستور دوم به این معنی است که auditd پس از آن بوت دوباره شروع به کار می‌کند. مجموعه قوانینی که فقط تا به‌روزرسانی بعدی kernel دوام می‌آورد، audit trail محسوب نمی‌شود.

FAQ

چگونه می‌توانم تمام دستوراتی که یک کاربر خاص اجرا کرده است را ببینم؟

ابتدا uid کاربر را با دستور id -u alice پیدا کنید، سپس لاگ audit را بر اساس login uid جستجو کنید: sudo ausearch -ul 1000 -ts today -i. برای محدود کردن نتایج به قانون execve، عبارت -k exec را اضافه کنید. login uid در زمان ورود به سیستم تنظیم می‌شود و در طول su و sudo -i تغییر نمی‌کند؛ بنابراین این روش دستوراتی که در یک root shell باز شده توسط آن حساب اجرا شده‌اند را نیز ثبت می‌کند. این روش فقط برای دستوراتی کار می‌کند که پس از بارگذاری قوانین اجرا شده‌اند، زیرا audit هیچ سابقه‌ای از رویدادهایی که برای ثبت آن‌ها پیکربندی نشده است، نگه نمی‌دارد. اگر می‌خواهید ابتدا ساختار داده‌ها را ببینید، sudo aureport -k --summary -i تعداد رویدادها به تفکیک هر قانون را به شما نشان می‌دهد.

آیا کاربر می‌تواند تاریخچه bash خود را برای پنهان کردن فعالیت‌هایش پاک کند؟

بله، و این کار نیازی به هیچ سطح دسترسی خاصی ندارد. فایل ~/.bash_history متعلق به همان کاربر است و با مجوز 600 ایجاد شده، بنابراین کاربر می‌تواند آن را ویرایش، کوتاه یا حذف کند. آن‌ها همچنین می‌توانند با unset HISTFILE از نوشتن در فایل جلوگیری کنند، با set +o history ثبت تاریخچه را در میانه نشست متوقف کنند، یا با قرار دادن یک فاصله قبل از دستور در حالی که HISTCONTROL=ignorespace تنظیم شده است، دستورات خاصی را مخفی کنند. Bash فایل را هنگام خروج از shell می‌نویسد، بنابراین نشستی که با kill -9 $$ بسته شود، هیچ چیزی ثبت نمی‌کند. تاریخچه shell را فقط به عنوان یک راهنما در نظر بگیرید، نه به عنوان مدرک.

آیا sudo فعالیت‌های داخل sudo -i را ثبت می‌کند؟

خیر. sudo فقط دستوری که از آن خواسته شده اجرا شود را ثبت می‌کند، بنابراین sudo -i تنها یک خط برای shell تولید می‌کند و پس از آن چیزی ثبت نمی‌شود. هر دستوری که در آن root shell تایپ شود برای sudo نامرئی است، زیرا sudo دیگر در آن دخالتی ندارد. sudo su -، sudo bash و هر برنامه مجاز دیگری که دارای shell escape باشد نیز به همین صورت عمل می‌کنند. دو راه برای پر کردن این شکاف وجود دارد: تنظیم قوانین audit روی execve که هر برنامه را با login uid اصلی متصل به آن ثبت می‌کند، و تنظیم قوانین sudoers به گونه‌ای که اصلاً shell در اختیار کاربر قرار ندهد.

آیا auditd سرعت سرور من را کاهش می‌دهد؟

این موضوع کاملاً به تعداد پردازش‌هایی که workload شما ایجاد می‌کند بستگی دارد، بنابراین به جای اعتماد به یک عدد، آن را اندازه‌گیری کنید. سروری که عمدتاً به درخواست‌ها پاسخ می‌دهد، تعداد کمی exec انجام می‌دهد و متوجه تأثیری نخواهد شد. یک build host یا CI runner دائماً در حال اجرای exec است و ممکن است تأثیر زیادی را حس کند، زیرا وقتی backlog مربوط به audit هسته پر شود، پردازشی که رویداد را ایجاد کرده تا زمان خالی شدن فضا متوقف می‌شود. دستور sudo auditctl -s را تحت بار واقعی اجرا کنید و backlog و lost را زیر نظر بگیرید. هر مقدار lost بالاتر از صفر به این معنی است که برخی رکوردها از دست رفته‌اند؛ این بدترین نتیجه ممکن است، زیرا لاگ اکنون دارای شکاف‌های نامرئی است.

لاگ‌های audit باید کجا ذخیره شوند؟

روی یک ماشین دیگر، با تأخیری که بر حسب ثانیه اندازه‌گیری می‌شود. هر کسی که در میزبان تحت نظارت به دسترسی root برسد می‌تواند /var/log/audit/audit.log را حذف کرده و /var/log/auth.log را بازنویسی کند، بنابراین نسخه‌های محلی فقط به سوالاتی درباره حوادثی پاسخ می‌دهند که کسی سعی در پنهان کردن آن‌ها نداشته است. لاگ‌ها را با استفاده از پلاگین audisp-remote به یک auditd مرکزی ارسال کنید، یا پلاگین audit syslog را فعال کرده و کل جریان syslog را با استفاده از rsyslog و از طریق TLS منتقل کنید. برای جمع‌آوری‌کننده (collector) اعتبارنامه‌های جداگانه در نظر بگیرید و مطمئن شوید حساب‌هایی که تحت نظارت هستند، هیچ دسترسی به آن ندارند.