الأمن السيبراني الأدلة التقنية

تسجيل جلسات المسؤولين ومراجعتها: sudo وtlog وauditd

أعدنا ليلة تغيير مجهول على سيرفر إنتاج، ثم أضفنا تسجيل جلسات sudo وسيرفر سجلات مركزياً وtlog حتى لا يمحو root أثره.

تسجيل جلسات المسؤولين ومراجعتها: sudo وtlog وauditd

لنفرض أن لديك شركة صغيرة في جدة تدير منصة حجوزات للعيادات، ولديها ثلاثة مسؤولي أنظمة يدخلون إلى السيرفرات عبر SSH بمفاتيحهم، وكل واحد منهم يستطيع أن يصبح root بالأمر sudo عندما يحتاج. وفي صباح يوم الأحد لاحظ الفريق أن إعداد قاعدة البيانات تغير، فقد أضيف سطر يرفع عدد الاتصالات إلى 500، وظهر على السيرفر حساب جديد اسمه svc-backup لا يعرفه أحد. والمسؤولون الثلاثة يقولون إنهم لم يلمسوا شيئاً، والمراجع الخارجي الذي يزور الشركة الأسبوع القادم سوف يسأل السؤال نفسه الذي يسأله الجميع الآن: من فعل هذا، ومتى، وماذا فعل غيره؟

هذا المقال يحكي قصة هذا السيرفر، ويعيد الليلة نفسها في مختبر حقيقي على Fedora 44 مرتين: مرة بالإعداد الافتراضي لنرى ماذا يبقى في السجلات، ومرة بعد أن نضيف طبقات التسجيل واحدة بعد الأخرى، حتى نصل إلى إعداد يجيب عن أسئلة الأحد حتى لو حاول من فعلها أن يمحو أثره. وقد ذكرنا تسجيل جلسات sudo باختصار في دليل IAM وPAM، وذكرنا auditd في مقال سجل التدقيق، أما هنا فندخل في التفاصيل: كيف يعمل كل جزء من الداخل، وأين يفشل، وكيف يبدو في مؤسسة حقيقية.

وسوف نناقش في هذا المقال ما يلي:

  • ماذا يبقى على السيرفر بعد ليلة كهذه في الإعداد الافتراضي، ولماذا لا يكفي.
  • لماذا تسجل المؤسسات جلسات المسؤولين، وما تطلبه الضوابط الأساسية للأمن السيبراني ECC، وقصة حقيقية من Cisco.
  • الطبقات الخمس للتسجيل: سجل الدخول، وسجل أحداث sudo، وتسجيل جلسة sudo، وتسجيل الجلسة كاملة، وتدقيق النواة auditd، وماذا ترى كل طبقة.
  • تجربة عملية: تسجيل جلسات sudo بالخيار log_output، وتشريح ملفات التسجيل، ثم نرى كيف يمحوها root بأمرين.
  • التسجيل المركزي بـ sudo_logsrvd عبر TLS، وما يحدث عندما يتوقف سيرفر السجلات، والمشكلة التي واجهتنا مع الشهادة.
  • خيار log_subcmds الذي لا يعمل في Fedora، وكيف عرفنا السبب.
  • tlog لتسجيل الجلسة كاملة بصيغة JSON، والمشكلتان اللتان أوقفتاه في البداية، وإعادة التشغيل بـ tlog-play.
  • auditd والبوابات، والخصوصية، ومدة الاحتفاظ، وكيف تراجع التسجيلات فعلاً، ثم قائمة تحقق.

ماذا يعرف السيرفر صباح الأحد؟

أعدنا ليلة الخميس في المختبر بنفس الأوامر: دخل المسؤول ali عبر SSH، ونفذ أمراً عادياً، ثم أصبح root بالأمر sudo -i، وأضاف السطر إلى ملف الإعداد، وأنشأ الحساب، ثم مسح تاريخ الأوامر بالأمر history -c وخرج. والصورة التالية تلخص الليلة وما بقي منها:

خط زمني لليلة الخميس: 02:10 دخول ali عبر SSH، و02:11 يصبح root بالأمر sudo -i، و02:12 يعدل الإعداد ويضيف الحساب svc-backup، و02:13 يمسح التاريخ بالأمر history -c، وصباح الأحد يكتشف الفريق التغيير، وتحته ثلاثة مصادر: سجل الدخول wtmp يعرف من دخل فقط، وسجل sudo لم يكتب شيئاً، وتاريخ الأوامر محذوف
في الإعداد الافتراضي يعرف السيرفر من دخل، ولا يعرف ماذا فعل

صباح الأحد بحثنا في السيرفر عن أي أثر. أول مصدر هو سجل الدخول، والأمر last يقرأ الملف /var/log/wtmp:

last -n 3
ali      pts/0        192.168.128.4    Sun Oct 25 22:48    gone - no logout

wtmp begins Sun Oct 25 22:48:50 2026

نعرف الآن أن ali دخل من العنوان 192.168.128.4 وفي أي وقت، ولكن لا شيء عما فعله. والمصدر الثاني هو سجل sudo نفسه، فهل يوجد في /var/log شيء يقول إن ali أصبح root؟

total 768
-rw-rw----  1 root root              0 Oct 25 22:28 btmp
-rw-r--r--  1 root root         747458 Oct 25 22:44 dnf5.log
drwxr-sr-x+ 2 root systemd-journal 4096 Oct 25 22:28 journal
-rw-rw-r--  1 root utmp         292292 Oct 25 22:48 lastlog
drwx------  2 root root           4096 Oct 25 22:28 private
-rw-rw-r--  1 root utmp            384 Oct 25 22:48 wtmp

لا يوجد ملف لـ sudo، والأمر journalctl رد بـ No journal files were found. والسبب أن sudo في Fedora مبني ليكتب أحداثه إلى syslog بالمنشأة authpriv، كما يظهر في خيارات البناء التي سوف نراها لاحقاً، وفي الـ Container لا يوجد systemd-journald يستقبلها، فذهبت إلى لا مكان. وعلى سيرفر حقيقي يعمل فيه journald سوف تجد سطراً يقول إن ali شغل /bin/bash كـ root، وهذا أفضل قليلاً، ولكنه سطر واحد لكل الجلسة، ولا يقول شيئاً عما حدث داخلها. والمصدر الثالث هو ملف ~/.bash_history، وهو ملف يملكه المستخدم نفسه، ومسحه ali بأمر واحد قبل أن يخرج.

وهذه هي المشكلة كلها: الإعداد الافتراضي في أغلب التوزيعات يجيب عن سؤال «من دخل؟» ولا يجيب عن سؤال «ماذا فعل؟»، والمصدر الوحيد الذي كان يمكن أن يجيب هو ملف يتحكم فيه الشخص الذي نسأل عنه.

لماذا نسجل جلسات المسؤولين أصلاً؟

قد يتساءل البعض: إذا كنا نثق بالمسؤولين حتى أعطيناهم root، فلماذا نسجل ما يفعلونه؟ والإجابة أن التسجيل لا يعني عدم الثقة، وإنما يخدم أربعة أهداف مختلفة، وكل هدف يغير طريقة الإعداد:

  • التحقيق بعد الحادث Incident Investigation: عندما يحدث شيء غريب تحتاج أن تعيد ما حدث كما حدث، وليس كما يتذكره الناس. وهذا يحتاج تسجيلاً كاملاً يبقى بعيداً عن متناول من تحقق في أمره.
  • الحساب الذي سرق Compromised Account: أغلب الحوادث لا يقوم بها المسؤول نفسه، وإنما شخص سرق مفتاح SSH الخاص به أو جلسته. والتسجيل هنا يحمي المسؤول قبل غيره، لأنه يثبت ما فعله هو وما لم يفعله.
  • الردع Deterrence والمسؤولية Accountability: الرسالة التي تظهر عند الدخول «جلستك مسجلة» تغير السلوك، فالمسؤول يتوقف عن الحلول السريعة على الإنتاج في منتصف الليل دون تذكرة.
  • الامتثال Compliance: المراجع يطلب دليلاً، وليس وعداً.

وفي السعودية، إذا كانت جهتك ضمن نطاق الضوابط الأساسية للأمن السيبراني ECC-2:2024 فإن المكون 2-12 الخاص بإدارة سجلات الأحداث ومراقبة الأمن السيبراني يطلب في الضابط 2-12-3-2 تفعيل سجلات الأحداث للحسابات ذات الصلاحيات الهامة والحساسة Privileged Accounts وللدخول عن بعد Remote Access، ويطلب في الضابط 2-12-3-5 ألا تقل مدة الاحتفاظ بالسجلات عن 12 شهراً، والضابط 2-2-3-4 يطلب إدارة الصلاحيات الهامة والحساسة PAM، كما في النسخة الإنجليزية من الوثيقة. ولاحظ أن الضوابط لا تسمي أداة، وإنما تطلب أن تكون الأحداث مسجلة ومحفوظة ومراقبة، وتسجيل الجلسات هو أوضح دليل على ذلك للحسابات المميزة.

📌
في أبريل 2018 تقريباً استقال مهندس من Cisco، وفي 24 سبتمبر 2018 دخل إلى البنية السحابية للشركة على AWS دون إذن، ونشر كوداً من حسابه في Google Cloud حذف 456 آلة افتراضية لتطبيق WebEx Teams، فتوقف أكثر من 16,000 حساب لمدة وصلت إلى أسبوعين، وأنفقت Cisco قرابة 1.4 مليون دولار من وقت موظفيها لإصلاح الضرر، وأعادت أكثر من مليون دولار للعملاء، كما في بيان وزارة العدل الأمريكية الذي أعلن الحكم عليه بالسجن 24 شهراً في ديسمبر 2020. والدرس هنا مزدوج: أن صلاحيات الموظف المغادر يجب أن تغلق في يومه الأخير، وهذا موضوع دورة حياة الحساب JML، وأن الشركة استطاعت أن تعرف من فعل ماذا ومتى لأن السجلات كانت موجودة خارج يد من فعلها.

خمس طبقات للتسجيل: ماذا ترى كل طبقة؟

قبل أن نبدأ الإعداد، من المهم أن تعرف أن «تسجيل الجلسات» ليس أداة واحدة، وإنما خمس طبقات، كل طبقة ترى شيئاً لا تراه الأخرى، ويستطيع أن يمحوها شخص مختلف:

جدول مرسوم بخمس طبقات: سجل الدخول wtmp وsshd يرى من دخل ولا يرى الأوامر، وسجل أحداث sudo يرى من شغل أي أمر كـ root ونتيجته ولكن داخل sudo -i يرى الشيل فقط، وتسجيل جلسة sudo I/O Log يرى كل ما ظهر على الشاشة داخل sudo، وتسجيل الجلسة كاملة بـ tlog أو بوابة يرى الطرفية من أول سطر، وتدقيق النواة auditd يرى كل برنامج شغل ومن خلفه auid ولا يعمل داخل Container
كل طبقة تجيب عن سؤال مختلف، وكل طبقة يمحوها شخص مختلف إذا بقيت على السيرفر
  1. سجل الدخول Login Records: الملف wtmp وسجلات sshd. يعرف من دخل ومن أين، ولا يعرف أي شيء بعد ذلك، وهو ما رأيناه صباح الأحد.
  2. سجل أحداث sudo Event Log: سطر لكل أمر يشغله المستخدم عبر sudo، فيه المستخدم والطرفية والأمر والمستخدم الهدف. ممتاز للأوامر المفردة مثل sudo systemctl restart nginx، ولكنه يرى /bin/bash فقط عندما يفتح المسؤول شيلاً كاملاً بـ sudo -i، وهذا ما يفعله أغلب المسؤولين.
  3. تسجيل جلسة sudo I/O Logging: كل ما يظهر على الشاشة داخل الأمر الذي يشغله sudo، ويعاد تشغيله بـ sudoreplay كأنه فيديو. وهذه الطبقة تجيب عن سؤال الأحد، ولكنها لا ترى ما يحدث قبل sudo أو خارجه.
  4. تسجيل الجلسة كاملة Session Recording: من أول سطر بعد الدخول إلى الخروج، سواء استخدم المسؤول sudo أم لا. وهذا ما تفعله tlog على السيرفر نفسه، أو بوابة الوصول المميز Bastion التي تسجل الجلسة قبل أن تصل إلى السيرفر.
  5. تدقيق النواة Kernel Audit: auditd يسجل كل برنامج يشغل، ومعه المعرف الأصلي للمستخدم auid الذي لا يتغير حتى بعد sudo، فيرى ما لا تراه الطرفية، مثل الأوامر التي تشغلها أداة أتمتة دون طرفية، ولكنه لا يرى ما ظهر على الشاشة.

والسؤال الثاني لكل طبقة هو: من يستطيع أن يمحوها؟ وهنا القاعدة التي سوف نعود إليها في كل قسم: أي سجل يبقى على نفس السيرفر يستطيع root على هذا السيرفر أن يمحوه، والتسجيل الذي نريد به أن نراقب من يملك root يجب أن يخرج من السيرفر فوراً.

المختبر: سيرفر حجوزات وحاسوب مسؤول وسيرفر سجلات

بنينا المختبر من ثلاث حاويات Fedora 44 على شبكة Docker واحدة، لأن Fedora وتوزيعات RHEL هي التي تأتي فيها tlog جاهزة في المستودعات، أما Debian 13 فيأتي فيه sudo_logsrvd داخل حزمة sudo نفسها ولا توجد فيه حزمة tlog:

  • srv.example.com: سيرفر الحجوزات، وعليه sudo 1.9.17p2 وtlog 14 وopenssh-server 10.2p1، والمستخدم ali في مجموعة wheel التي تعطيها sudoers الافتراضية في Fedora صلاحية sudo كاملة بكلمة مرور.
  • laptop.example.com: حاسوب المسؤول، ومنه يدخل ali بمفتاح SSH من نوع ed25519.
  • logsrv.example.com: سيرفر السجلات، وعليه الحزمة sudo-logsrvd.

ولكي نعيد الجلسات بنفس الشكل في كل مرة كتبنا الأوامر في سكربت صغير يرسلها إلى ssh -tt كأن شخصاً يكتبها، مع انتظار ثلاث ثوان بين كل سطر، وهذا هو سكربت ليلة الخميس session1.sh:

#!/bin/bash
# session1.sh: what ali types during the night session (fed to ssh -tt as if typed)
s() { sleep 3; echo "$1"; }
s 'grep -c . /etc/passwd'
s 'sudo -i'
s "$ALI_PW"
s 'head -n 2 /etc/os-release'
s 'echo "max_connections=500" >> /etc/app.conf'
s 'useradd -M -s /bin/bash svc-backup'
s 'history -c'
s 'exit'
s 'exit'
bash session1.sh | ssh -tt [email protected]

والخيار -tt يجبر ssh على طلب طرفية Pseudo Terminal حتى لو لم يكن مدخله طرفية، وهذا مهم لأن sudo يطلب كلمة المرور من الطرفية، ولأن تسجيل الجلسات كله مبني على الطرفية كما سنرى. وفي البداية كان الانتظار ثانية واحدة، فوصلت كلمة المرور قبل أن يطلبها sudo، فاعتبر sudo الأسطر التالية محاولات خاطئة وكتب 3 incorrect password attempts، لذلك إذا جربت هذه الطريقة على سيرفر بطيء فزد الانتظار.

الطبقة الثالثة: تسجيل جلسات sudo

الإعداد: ثلاثة أسطر في sudoers

نضيف ملفاً جديداً في /etc/sudoers.d بدلاً من تعديل /etc/sudoers نفسه، لأن تحديثات الحزمة قد تستبدل الملف الرئيسي، ولأن الملف المنفصل يسهل نشره بأدوات الإدارة مثل Ansible:

# /etc/sudoers.d/20-session-log: record what admins see inside sudo
Defaults logfile=/var/log/sudo.log
Defaults log_output
Defaults!/usr/bin/sudoreplay !log_output
chmod 440 /etc/sudoers.d/20-session-log
visudo -c
/etc/sudoers: parsed OK
/etc/sudoers.d/20-session-log: parsed OK

في الإعداد أعلاه لاحظ التالي:

  • logfile يجعل sudo يكتب أحداثه في ملف نصي بالإضافة إلى syslog، وهذا يحل مشكلة صباح الأحد على أي سيرفر لا يعمل فيه syslog أو journald، ولكنه ملف محلي يمحوه root كما سنرى.
  • log_output يفعل تسجيل كل ما يكتبه الأمر على الطرفية، بحسب صفحة sudoers.
  • السطر الثالث يستثني sudoreplay نفسه، والسبب أن المسؤول الذي يراجع تسجيلاً قديماً بـ sudo سوف ينتج تسجيلاً جديداً لكل مراجعة، وتمتلئ الملفات بتسجيلات المراجعات.
  • الأمر visudo -c ليس اختيارياً، فخطأ واحد في ملف sudoers يمنع sudo نفسه من العمل، وقد تجد نفسك خارج السيرفر دون طريقة لإصلاحه إلا بحساب root مباشر أو بالدخول من الطرفية الفعلية.

نعيد الليلة، ثم نشاهدها

شغلنا سكربت ليلة الخميس بعد الإعداد، وهذا ما رآه ali على شاشته، بعد أن أزلنا سلاسل التحكم التي يضيفها Fedora إلى كل سطر أوامر كما سنشرح:

Last login: Sun Oct 25 22:48:50 2026 from 192.168.128.4
grep -c . /etc/passwd
[ali@srv ~]$ grep -c . /etc/passwd
23
[ali@srv ~]$ sudo -i
[sudo] password for ali: 
[root@srv ~]# head -n 2 /etc/os-release
NAME="Fedora Linux"
VERSION="44 (Container Image)"
[root@srv ~]# echo "max_connections=500" >> /etc/app.conf
[root@srv ~]# useradd -M -s /bin/bash svc-backup
history -c
[root@srv ~]# history -c
[root@srv ~]# exit
logout
[ali@srv ~]$ exit
logout
Connection to srv.example.com closed.

والآن صباح الأحد، نقرأ أولاً ملف /var/log/sudo.log:

Oct 25 22:50:50 : ali : TTY=pts/0 ; PWD=/root ; USER=root ; TSID=000001 ;
    COMMAND=/bin/bash

هذا هو الحد الذي تصل إليه الطبقة الثانية: نعرف أن ali شغل /bin/bash كـ root من الطرفية pts/0 في الساعة 22:50:50، ولكن لا نعرف ماذا فعل في الشيل. والجديد هو TSID=000001، أي معرف الجلسة المسجلة. ثم نطلب قائمة الجلسات:

sudoreplay -l
Oct 25 22:50:50 2026 : ali : HOST=srv.example.com ; TTY=pts/0 ; CWD=/home/ali ; USER=root ; TSID=000001 ; COMMAND=/bin/bash

ثم نشاهد الجلسة نفسها، والخيار -m 0 يلغي الانتظار بين الأسطر، فبدونه يعيد sudoreplay الجلسة بنفس سرعتها الأصلية كأنك تشاهد فيديو:

sudoreplay -m 0 000001
Replaying sudo session: /bin/bash[root@srv ~]# head -n 2 /etc/os-release
NAME="Fedora Linux"
VERSION="44 (Container Image)"
[root@srv ~]# echo "max_connections=500" >> /etc/app.conf
[root@srv ~]# useradd -M -s /bin/bash svc-backup
history -c
[root@srv ~]# history -c
[root@srv ~]# exit
logout

والآن صار لأسئلة الأحد إجابة: ali أصبح root، وقرأ ملف إصدار النظام، وأضاف السطر إلى /etc/app.conf، وأنشأ الحساب svc-backup، ثم حاول أن يمسح تاريخ الأوامر. ولاحظ أن history -c نفسه ظهر في التسجيل، فمسح التاريخ يمسح ملف الشيل، ولا يمسح ما سجله sudo. ولاحظ أيضاً أن history -c ظهر مرتين، والسبب أن السكربت كتبه قبل أن يرجع الشيل، فظهر صدى الكتابة Echo مرة ثم ظهر مع السطر التالي، وهذا ما يحدث تماماً عندما يكتب الإنسان بسرعة.

ماذا يوجد داخل مجلد التسجيل؟

كل جلسة تحفظ في مجلد مساره مبني على TSID، والصورة التالية تبين الملفات الستة التي وجدناها في مجلد الجلسة الأولى:

مجلد /var/log/sudo-io/00/00/01 وفيه الملفات log وفيه سطر الوقت والمستخدم والطرفية، وlog.json وفيه الأمر والبيئة وSSH_CONNECTION، وtiming وفيه توقيت كل جزء، وttyout وفيه ما ظهر على الطرفية، وstdout وstderr للمخرج دون طرفية، وttyin وstdin مع log_input فقط وقد تحمل كلمات مرور، وبجانبها أوامر sudoreplay
الرقم 000001 يقسم إلى ثلاثة مجلدات، وملف ttyout هو قلب التسجيل
drwx------ 2 root root 4096 Oct 25 22:50 .
drwx------ 3 root root 4096 Oct 25 22:50 ..
-rw------- 1 root root   58 Oct 25 22:50 log
-rw------- 1 root root 1609 Oct 25 22:50 log.json
-rw------- 1 root root   25 Oct 25 22:50 stderr
-rw------- 1 root root   25 Oct 25 22:50 stdout
-r-------- 1 root root  199 Oct 25 22:50 timing
-rw------- 1 root root  546 Oct 25 22:50 ttyout

الملف log سطر واحد بالصيغة القديمة: وقت البداية بثواني Unix، ثم المستخدم والمستخدم الهدف والطرفية وحجمها:

1792968650:ali:root::/dev/pts/0:24:80
/home/ali
/bin/bash

أما log.json فهو الأهم في التحقيق، وهذا جزء منه:

{
    "timestamp": { "seconds": 1792968650, "nanoseconds": 71890134 },
    "submituser": "ali",
    "command": "/bin/bash",
    "runuser": "root",
    "source": "/etc/sudoers:107:21",
    "ttyname": "/dev/pts/0",
    "submithost": "srv.example.com",
    "submitcwd": "/home/ali",
    "submitenv": [
        "SSH_CONNECTION=192.168.128.4 40874 192.168.128.2 22",
        "SSH_CLIENT=192.168.128.4 40874 22",
        "SSH_TTY=/dev/pts/0"
    ]
}

في الملف أعلاه لاحظ التالي:

  • source يقول أي سطر في sudoers سمح بهذا الأمر: السطر 107 في /etc/sudoers، وهو سطر مجموعة wheel. وهذا مفيد جداً في المراجعة، لأنه يكشف القواعد القديمة التي ما زالت تعطي صلاحيات لا يعرف بها أحد.
  • SSH_CONNECTION في بيئة المستخدم يحمل عنوان الحاسوب الذي جاء منه ali والمنفذ، فيربط الجلسة بسجل الدخول وبسجلات الجدار الناري Firewall.
  • الأوقات بثواني Unix مع النانوثانية، لذلك اجعل ساعات السيرفرات متزامنة عبر NTP، وإلا لن تستطيع أن تربط هذا السجل بسجلات بقية الأنظمة.

والملفات timing وttyout وبقية الملفات مضغوطة بـ zlib في Fedora، لذلك ظهرت لنا رموزاً غير مقروءة عندما عرضناها بـ cat، ولا تقرؤها إلا بـ sudoreplay. والملف timing للقراءة فقط -r-------- لأن sudo يعلمه بعد انتهاء الجلسة، وهي علامة يستخدمها sudoreplay ليعرف أن الجلسة اكتملت.

ما الذي لم يره sudo؟

قارن الآن بين ما رآه ali على شاشته وما سجله sudo. في الشاشة كان أول أمر هو grep -c . /etc/passwd ونتيجته 23، وهذا الأمر غير موجود في التسجيل، لأنه نفذ قبل sudo، فلا يعرف عنه sudo شيئاً. ولو أن ali نسخ ملفاً حساساً إلى حاسوبه بـ scp أو قرأ ملفات يستطيع أن يقرأها دون root، فلن يظهر أي من ذلك هنا. وهناك فجوتان أخريان:

  • كلمة مرور sudo نفسها لم تسجل: لأن sudo يطلبها قبل أن يبدأ التسجيل، ولأن الطرفية لا تعرض ما يكتبه المستخدم في حقل كلمة المرور.
  • ما يكتبه المستخدم لا يسجل: فعلنا log_output فقط، أما log_input فيسجل كل ما يكتبه المستخدم، بما في ذلك كلمات المرور التي يكتبها داخل برامج أخرى بعد أن أصبح root، مثل كلمة مرور قاعدة بيانات في mysql -p. لذلك لا ينصح بتفعيله إلا إذا كانت التسجيلات نفسها محمية كما تحمي كلمات المرور، والمخرج يكفي غالباً لأن الطرفية تعرض ما يكتبه المستخدم من أوامر.

وهذه الفجوات هي سبب الطبقة الرابعة التي سنصل إليها مع tlog. ولكن قبلها هناك مشكلة أكبر.

نجرب: هل يستطيع root أن يمحو التسجيل؟

التسجيل محفوظ في /var/log/sudo-io على نفس السيرفر، وصلاحياته لـ root فقط، وهذا يحميه من المستخدم العادي، ولكن الشخص الذي نسجل جلسته أصبح root. فكتبنا سكربتاً ثانياً يفعل فيه ali ما يفعله أي مهاجم ذكي:

#!/bin/bash
# session2.sh: the same admin now hides his tracks
s() { sleep 3; echo "$1"; }
s 'sudo -i'
s "$ALI_PW"
s 'rm -rf /var/log/sudo-io'
s ': > /var/log/sudo.log'
s 'exit'
s 'exit'

والجلسة نفسها كانت كما يلي:

[sudo] password for ali:
[root@srv ~]# rm -rf /var/log/sudo-io
[root@srv ~]# : > /var/log/sudo.log
[root@srv ~]# exit
logout

ثم صباح الأحد:

sudoreplay -l
wc -c /var/log/sudo.log
sudoreplay: unable to open /var/log/sudo-io: No such file or directory
0 /var/log/sudo.log

لا يوجد تسجيل لليلة الخميس، ولا تسجيل لجلسة المحو نفسها، وملف الأحداث فارغ. وهذا ما قصدناه بالقاعدة: أي سجل يبقى على السيرفر يمحوه root على السيرفر. ولا يوجد إعداد في sudo يمنع root من حذف ملف، لأن root في Linux يستطيع أن يفعل أي شيء تقريباً على نظام ملفاته. والحل الوحيد أن تخرج التسجيلات من السيرفر أثناء الجلسة، وليس في نهاية اليوم بنسخة احتياطية، لأن من يمحو التسجيل سوف يمحوه قبل النسخة الاحتياطية.

التسجيل المركزي مع sudo_logsrvd

منذ الإصدار 1.9.0 في 2020 يأتي مع sudo سيرفر اسمه sudo_logsrvd يستقبل الأحداث والتسجيلات من sudo عبر الشبكة، فيرسل sudo كل ما يظهر على الشاشة إلى سيرفر السجلات أثناء الجلسة نفسها، ولا يكتب شيئاً محلياً. والصورة التالية تبين الشكل الذي بنيناه:

سيرفر الحجوزات srv وعليه sudo مع log_output وlog_servers يرسل عبر TLS على المنفذ 30343 إلى سيرفر السجلات logsrv وعليه sudo_logsrvd يحفظ في /var/log/sudo-io وevents.json، والأمر rm -rf /var/log/sudo-io على srv لا يجد شيئاً يمحوه، وفي الأسفل ما يحدث عندما يتوقف سيرفر السجلات: الافتراضي أن sudo يرفض الأمر، ومع ignore_iolog_errors يعمل الأمر دون تسجيل
الجلسة تصل إلى سيرفر السجلات أثناء حدوثها، فلا يبقى على سيرفر الإنتاج ما يمحى

إعداد سيرفر السجلات

في Fedora يأتي السيرفر في حزمة منفصلة اسمها sudo-logsrvd، وقد ثبتنا أولاً حزمة sudo وحدها فلم نجد البرنامج، ثم وجدناه بالأمر dnf repoquery --whatprovides "*/sudo_logsrvd". وهذا ملف الإعداد /etc/sudo_logsrvd.conf الذي استخدمناه:

[server]
listen_address = *:30343(tls)
tls_cacert = /etc/sudo_logsrvd/ca.pem
tls_cert = /etc/sudo_logsrvd/logsrv.pem
tls_key = /etc/sudo_logsrvd/logsrv.key

[iolog]
iolog_dir = /var/log/sudo-io
iolog_file = %{seq}
iolog_compress = true

[eventlog]
log_type = logfile
log_format = json

[logfile]
path = /var/log/sudo/events.json

في الإعداد أعلاه لاحظ التالي:

  • listen_address = *:30343(tls) يستقبل على المنفذ 30343 مع TLS، وهو المنفذ الافتراضي للاتصال المشفر بحسب صفحة sudo_logsrvd.conf، أما 30344 فهو الافتراضي دون تشفير، ولا تستخدمه إلا على شبكة تثق بها تماماً، لأن ما يمر فيه هو كل ما رآه root على الشاشة.
  • iolog_file = %{seq} يبني المسار من رقم تسلسلي كما رأينا، وتستطيع أن تجعله يحمل اسم المستخدم والسيرفر والتاريخ إذا أردت أن تجد التسجيلات بالتصفح.
  • iolog_compress = true يضغط التسجيلات، وهي نصوص تضغط بنسبة كبيرة.
  • سجل الأحداث بصيغة JSON في ملف، وهي الصيغة الأسهل للإرسال إلى SIEM مثل Wazuh.

المشكلة التي واجهتنا: شهادة جهة التوقيع

أنشأنا في البداية جهة توقيع CA تجريبية بأبسط أمر openssl req -x509، ثم شهادة للسيرفر موقعة منها، فرفض sudo_logsrvd أن يبدأ:

sudo_logsrvd: X509_verify_cert: /etc/sudo_logsrvd/logsrv.pem: CA cert does not include key usage extension
sudo_logsrvd: unable to initialize server TLS context

والسبب أن sudo_logsrvd يتحقق من سلسلة الشهادات بنفسه عند التشغيل، ويرفض جهة توقيع لا تحمل الامتداد keyUsage الذي يقول إنها مسموح لها أن توقع شهادات. فأعدنا إنشاء جهة التوقيع والشهادة بهذا السكربت داخل سيرفر السجلات:

#!/bin/bash
# mkcerts.sh: a small lab CA and a server certificate for logsrv.example.com
set -e
cd /etc/sudo_logsrvd
openssl req -x509 -newkey rsa:3072 -nodes -days 365 -subj "/CN=Example Log CA" \
  -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign" \
  -keyout ca.key -out ca.pem 2>/dev/null
openssl req -newkey rsa:3072 -nodes -subj "/CN=logsrv.example.com" -keyout logsrv.key -out logsrv.csr 2>/dev/null
printf 'subjectAltName=DNS:logsrv.example.com\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth\n' > san.ext
openssl x509 -req -in logsrv.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 -extfile san.ext -out logsrv.pem 2>/dev/null
chmod 600 ca.key logsrv.key
openssl x509 -in logsrv.pem -noout -subject -ext subjectAltName
subject=CN=logsrv.example.com
X509v3 Subject Alternative Name:
    DNS:logsrv.example.com

ثم بدأ السيرفر وأصبح يستمع على المنفذ 30343. وفي الإنتاج استخدم جهة التوقيع الداخلية لمؤسستك إذا كانت موجودة، والمهم أن يحمل اسم الشهادة subjectAltName الاسم الذي يتصل به sudo، لأنه يتحقق منه.

إعداد sudo على سيرفر الحجوزات

ننسخ شهادة جهة التوقيع العامة فقط ca.pem إلى سيرفر الحجوزات، ونضيف ملفاً آخر إلى sudoers:

# /etc/sudoers.d/30-log-server: send events and recordings to the log server, not only to this disk
Defaults log_servers=logsrv.example.com:30343(tls)
Defaults log_server_cabundle=/etc/sudo-logsrv-ca.pem
Defaults log_server_timeout=10

في الإعداد أعلاه لاحظ التالي:

  • log_servers يقبل أكثر من سيرفر مفصولة بفواصل، ويجرب sudo الثاني إذا لم يصل إلى الأول، وهذا هو طريق التوافرية High Availability هنا.
  • log_server_cabundle هو ما يتحقق به sudo من شهادة سيرفر السجلات، فلا يرسل الجلسة إلى أي سيرفر ينتحل الاسم.
  • log_server_timeout مهلة الاتصال بالثواني، فإذا كانت الشبكة بطيئة لن ينتظر المسؤول طويلاً.
  • بقي log_output من الملف السابق، فهو الذي يقرر أن الجلسة تسجل، وlog_servers يقرر أين تذهب.

ثم أعدنا ليلة الخميس كما هي، وبحثنا على سيرفر الحجوزات أولاً:

ls: cannot access '/var/log/sudo-io': No such file or directory
Oct 25 22:57:50 : ali : TTY=pts/0 ; PWD=/root ; USER=root ; COMMAND=/bin/bash

لا يوجد مجلد تسجيلات محلي، وسطر الحدث في sudo.log لا يحمل TSID هذه المرة لأن التسجيل ليس محلياً. ثم على سيرفر السجلات:

sudoreplay -d /var/log/sudo-io -l
sudoreplay -d /var/log/sudo-io -m 0 000001
Oct 25 22:57:50 2026 : ali : HOST=srv.example.com ; TTY=pts/0 ; CWD=/home/ali ; USER=root ; TSID=000001 ; COMMAND=/bin/bash
Replaying sudo session: /bin/bash[root@srv ~]# head -n 2 /etc/os-release
NAME="Fedora Linux"
VERSION="44 (Container Image)"
[root@srv ~]# echo "max_connections=500" >> /etc/app.conf
[root@srv ~]# useradd -M -s /bin/bash svc-backup
useradd: user 'svc-backup' already exists
[root@srv ~]# history -c
[root@srv ~]# exit
logout

نفس الجلسة، ولكن على سيرفر آخر لا يدخل إليه مسؤولو الإنتاج. وظهر هذه المرة سطر جديد useradd: user 'svc-backup' already exists لأن الحساب بقي من المرة الأولى، وهذا يذكرك أن التسجيل يحفظ ما حدث فعلاً، وليس ما أراد المسؤول أن يفعله.

نجرب المحو مرة أخرى

أعدنا سكربت المحو نفسه. على سيرفر الحجوزات لم يجد rm -rf /var/log/sudo-io شيئاً يمحوه، أما على سيرفر السجلات فظهرت جلسة جديدة:

Oct 25 22:57:50 2026 : ali : HOST=srv.example.com ; TTY=pts/0 ; CWD=/home/ali ; USER=root ; TSID=000001 ; COMMAND=/bin/bash
Oct 25 22:59:19 2026 : ali : HOST=srv.example.com ; TTY=pts/0 ; CWD=/home/ali ; USER=root ; TSID=000002 ; COMMAND=/bin/bash
Replaying sudo session: /bin/bash[root@srv ~]# rm -rf /var/log/sudo-io
[root@srv ~]# : > /var/log/sudo.log
[root@srv ~]# exit
logout

ومحاولة المحو نفسها أصبحت دليلاً، فالجلسة 000002 تبين أن ali حاول أن يحذف التسجيلات ويفرغ ملف الأحداث. وفي التحقيق الحقيقي هذه المحاولة أهم من التغيير نفسه، لأنها تبين النية.

ماذا يحدث إذا توقف سيرفر السجلات؟

هذا هو القرار الأهم في التصميم كله، فأوقفنا sudo_logsrvd وحاول ali أن ينفذ sudo id:

Last login: Sun Oct 25 22:59:13 2026 from 192.168.128.4
[ali@srv ~]$ sudo id
[sudo] password for ali: 
sudo: unable to connect to log server
sudo: error initializing I/O plugin sudoers_io
[ali@srv ~]$ exit
logout
Connection to srv.example.com closed.

رفض sudo تنفيذ الأمر، والسبب أن ملحق التسجيل sudoers_io لم يستطع أن يبدأ، وsudo في وضعه الافتراضي لا ينفذ أمراً لا يستطيع أن يسجله. وهذا ما يسمى الفشل المغلق Fail-closed. ثم أضفنا الخيار ignore_iolog_errors وأعدنا المحاولة:

Last login: Sun Oct 25 22:59:53 2026 from 192.168.128.4
[ali@srv ~]$ sudo id
[sudo] password for ali: 
sudo: unable to connect to log server
uid=0(root) gid=0(root) groups=0(root)
[ali@srv ~]$ exit
logout
Connection to srv.example.com closed.

نفذ الأمر هذه المرة دون تسجيل، مع تحذير في سطر واحد، وهذا هو الفشل المفتوح Fail-open. وأيهما تختار؟ لا توجد إجابة واحدة، ولكن فكر في الأمر كما يلي: مع الفشل المغلق يستطيع المهاجم أن يوقف sudo على كل السيرفرات إذا أوقف سيرفر السجلات، ولا يستطيع المسؤول أن يصلح عطلاً في الساعة الثالثة فجراً إذا كان العطل في الشبكة نفسها. ومع الفشل المفتوح يكفي أن يقطع المهاجم الطريق إلى سيرفر السجلات حتى يعمل دون تسجيل. والحل العملي في أغلب الشركات:

  • اترك الفشل المغلق، وهو الافتراضي، وضع سيرفري سجلات في log_servers حتى لا يكون أحدهما نقطة فشل واحدة.
  • جهز حساب طوارئ Break-glass على السيرفر يستطيع أن يعمل عندما يتوقف كل شيء، مع تنبيه فوري عند استخدامه، وهذا موضوع مقال الوصول المؤقت وحسابات الطوارئ.
  • راقب سيرفر السجلات نفسه بتنبيه عندما يتوقف، لأن توقفه يوقف عمل كل المسؤولين.

بعد التجربة حذفنا ملف ignore_iolog_errors وأعدنا تشغيل sudo_logsrvd.

سجل الأحداث على سيرفر السجلات

إلى جانب التسجيل نفسه يكتب sudo_logsrvd حدثاً بصيغة JSON لكل أمر يقبله sudo، وهذا جزء من الحدث الأول:

{
    "accept": {
        "uuid": "83c673cea5-3e52-43bb-7e32-2c2072af4f",
        "server_time": { "seconds": 1792969070, "iso8601": "20261025225750Z" },
        "submit_time": { "seconds": 1792969070, "iso8601": "20261025225750Z" },
        "peeraddr": "192.168.128.2",
        "iolog_path": "/var/log/sudo-io/00/00/01",
        "command": "/bin/bash",
        "runuser": "root",
        "source": "/etc/sudoers:107:21",
        "submitcwd": "/home/ali"
    }
}

لاحظ حقلين لا تجدهما في السجل المحلي: server_time وقت وصول الحدث إلى سيرفر السجلات بساعته هو، وsubmit_time الوقت بحسب ساعة سيرفر الحجوزات، والفرق بينهما يكشف السيرفر الذي غيرت ساعته عمداً لإرباك التحقيق. وpeeraddr هو عنوان السيرفر الذي أرسل الحدث كما رآه سيرفر السجلات، وليس كما يقول السيرفر عن نفسه. وهذا الحدث هو ما ترسله إلى SIEM، كما شرحنا في مقال سجل التدقيق، حتى ينبهك عندما يستخدم أحد sudo خارج نافذة التغييرات المعتمدة.

الخيار log_subcmds: الوعد الذي لم يعمل في Fedora

منذ sudo 1.9.8 يوجد خيار اسمه log_subcmds، والفكرة أن sudo لا يسجل الشاشة فقط، وإنما يكتب حدثاً في سجل الأحداث لكل برنامج يشغله الشيل الذي فتحه sudo -i، أي أن السطر COMMAND=/bin/bash يصبح قائمة بكل أمر نفذه root، كما تصف صفحة sudoers. وهذا يحل مشكلة الطبقة الثانية دون أن تحتاج إلى مشاهدة التسجيل. فأضفنا:

Defaults log_subcmds
Defaults log_exit_status

وأعدنا ليلة الخميس، فكان سجل الأحداث:

Oct 25 23:12:37 : ali : TTY=pts/2 ; PWD=/root ; USER=root ; COMMAND=/bin/bash
Oct 25 23:12:52 : ali : TTY=pts/2 ; PWD=/root ; USER=root ; COMMAND=/bin/bash ;
    EXIT=0

ظهر EXIT=0 من الخيار الثاني، ولم يظهر أي أمر من الأوامر داخل الشيل. جربنا تغيير طريقة الاعتراض بـ intercept_type=dso فلم يتغير شيء، فسألنا sudo نفسه عن خيارات بنائه:

sudo -V | grep -i intercept
Configure options: … --disable-root-mailer --disable-intercept --with-logging=syslog --with-logfac=authpriv … --with-linux-audit --with-sssd
The mechanism used by the intercept and log_subcmds options: dso

حزمة sudo في Fedora 44 مبنية بالخيار --disable-intercept، أي أن الميزة غير موجودة في البرنامج أصلاً، ومع ذلك يقبل visudo -c الخيار دون أي تحذير. وهذا درس عام في تسجيل الجلسات: لا تفترض أن الإعداد يعمل لأنه قبل، وإنما جرب كل طبقة بجلسة حقيقية ثم ابحث عن أثرها، تماماً كما فعلنا هنا. وإذا احتجت هذه الميزة فتحقق من خيارات بناء sudo في توزيعتك بالأمر sudo -V، وإلا فطبقة auditd تعطيك نفس المعلومة من النواة. ولاحظ في نفس السطر --with-logging=syslog --with-logfac=authpriv، وهو ما يفسر لماذا لم نجد شيئاً صباح الأحد الأول، و--with-linux-audit الذي يعني أن sudo يرسل أحداثه إلى نظام تدقيق النواة أيضاً على سيرفر يعمل فيه auditd.

الطبقة الرابعة: تسجيل الجلسة كاملة مع tlog

تسجيل sudo لا يرى أول أمر في ليلة الخميس، ولا يرى ما يفعله المسؤول دون sudo. والحل على السيرفر نفسه هو tlog، وهو مشروع مفتوح المصدر بدأته Red Hat، ويستخدم في RHEL تحت اسم Session Recording مع واجهة في Cockpit. وآخر إصدار له v14 في فبراير 2024، وفي Fedora 44 الحزمة tlog-14-7، أي أن المشروع مستقر ولا تصدر له إصدارات كثيرة. والفكرة موضحة في الصورة التالية:

ali من حاسوبه يتصل عبر ssh إلى sshd الذي يشغل الشيل المسجل tlog-rec-session، وهو يعمل بهوية المستخدم tlog بصلاحية setuid، ثم يشغل /bin/bash الحقيقي، وكل ما يظهر على الشاشة يكتب كرسائل JSON فيها ver وhost وrec وuser وsession وtiming وin_txt وout_txt، والكاتب ملف أو journal أو syslog ثم SIEM، والمدخلات in_txt فارغة افتراضياً، والإعادة بالأمر tlog-play
tlog يصبح شيل المستخدم، ويشغل الشيل الحقيقي تحته، ويسجل ما بينهما

tlog لا يحتاج أي تغيير في sshd ولا في sudo، وإنما تجعل البرنامج tlog-rec-session هو شيل الدخول للمستخدم، فيشغل الشيل الحقيقي داخل طرفية جديدة، ويسجل كل ما يمر بينهما، ثم يكتبه رسائل JSON. ولاحظ صلاحيات البرنامج في الحزمة:

-rwxr-xr-x 1 root root 11552 Jan 17  2026 /usr/bin/tlog-play
-rwxr-xr-x 1 root root 11552 Jan 17  2026 /usr/bin/tlog-rec
-rwsr-sr-x 1 tlog tlog 15664 Jan 17  2026 /usr/bin/tlog-rec-session

الحرف s في صلاحيات tlog-rec-session يعني أنه يعمل بهوية المستخدم tlog مهما كان من شغله، وهذا هو التصميم الذي يجعل ali لا يستطيع أن يلمس التسجيل: الملف يكتبه المستخدم tlog، وali لا يملك صلاحية عليه.

الإعداد

نكتب إعداد التسجيل في /etc/tlog/tlog-rec-session.conf، وهو ملف JSON:

{
    "shell": "/bin/bash",
    "notice": "\nATTENTION! Your session is being recorded!\n\n",
    "writer": "file",
    "file": { "path": "/var/log/tlog/tlog.log" }
}

ثم نجهز مجلد السجل ونغير شيل ali:

install -d -o tlog -g tlog -m 750 /var/log/tlog
usermod -s /usr/bin/tlog-rec-session ali
echo /usr/bin/tlog-rec-session >> /etc/shells

في الإعداد أعلاه لاحظ التالي:

  • shell هو الشيل الحقيقي الذي يشغله tlog، فالمستخدم لا يلاحظ أي فرق في عمله.
  • notice هي الرسالة التي تظهر عند الدخول، وهي ليست تفصيلاً تقنياً، وإنما جزء من الشفافية مع المسؤولين كما سنشرح في قسم الخصوصية.
  • الكاتب file اخترناه لأن الـ Container لا يعمل فيه journald، وعلى سيرفر حقيقي الأفضل journal، لأن journald يضيف إلى كل رسالة حقولاً موثوقة مثل معرف المستخدم، ثم ترسل السجلات منه إلى سيرفر مركزي، كما في توثيق tlog.
  • المجلد 750 للمستخدم tlog، فلا يستطيع ali أن يرى حتى أسماء الملفات فيه.

مشكلتان أوقفتا tlog في البداية

في أول دخول بعد الإعداد رأى ali هذه الرسائل، ثم أغلق الاتصال فوراً:

Failed creating lock file /var/run/tlog/session.352.lock
Assuming session was unlocked

ATTENTION! Your session is being recorded!

Failed adding a utmp record
Success
Failed removing utmp record
Failed setting up the I/O tap
No such file or directory

والسبب الأول أن المجلد /run/tlog غير موجود، فهو مجلد مؤقت تنشئه systemd عند الإقلاع من الملف /usr/lib/tmpfiles.d/tlog.conf، وفي الـ Container لا يوجد systemd، فأنشأناه بالأمر systemd-tmpfiles --create /usr/lib/tmpfiles.d/tlog.conf. وبقيت الرسالة الأخيرة، والسبب الثاني أن الملف /run/utmp الذي يسجل فيه tlog الطرفية الجديدة غير موجود أيضاً للسبب نفسه، فأنشأناه بمجموعة utmp وصلاحية 664، فعمل التسجيل. ولاحظ الدرس هنا: tlog يفشل مغلقاً مثل sudo، فإذا لم يستطع أن يسجل لا يفتح الجلسة أصلاً، وهذا يعني أن المسؤول الذي غيرت شيله إلى tlog لن يستطيع الدخول إذا تعطل tlog، فلا تجربه أول مرة على حسابك الوحيد، واترك جلسة root مفتوحة حتى تتأكد أنه يعمل.

جلسة كاملة، ثم الإعادة

كتبنا سكربتاً يعمل فيه ali خارج sudo أولاً، ثم يستخدم sudo:

#!/bin/bash
# session4.sh: work outside sudo, the part sudo never sees
s() { sleep 3; echo "$1"; }
s 'grep -c . /etc/passwd'
s 'cat /etc/app.conf'
s 'sudo id'
s "$ALI_PW"
s 'exit'

وما رآه ali:

Last login: Sun Oct 25 23:02:03 2026 from 192.168.128.4
Locale charset is ANSI_X3.4-1968 (ASCII)
Assuming locale environment is lost and charset is UTF-8

ATTENTION! Your session is being recorded!

[ali@srv ~]$ grep -c . /etc/passwd
24
[ali@srv ~]$ cat /etc/app.conf
max_connections=500
max_connections=500
[ali@srv ~]$ sudo id
[sudo] password for ali: 
uid=0(root) gid=0(root) groups=0(root)
[ali@srv ~]$ exit
logout

Connection to srv.example.com closed.

والسطران Locale charset is ANSI_X3.4-1968 سببهما أن جلسة ssh من الـ Container لم تحمل إعداد اللغة، وهما تحذير من tlog بأنه سوف يفترض UTF-8، ولا يظهران عادة من حاسوب حقيقي. أما ما كتبه tlog في الملف فهو رسائل JSON، وهذه آخر رسالتين من الجلسة:

{"ver":"2.3","host":"srv.example.com","rec":"bfd2797f5f8240a49e33cdb91446bcdf-329-187dc8c","user":"ali","term":"","session":356,"id":1,"pos":0,"time":1792969359.770,"timing":"=0x0+100>13+1845>23+2>17+3007>19+1>42+16>13+2979>9+230>25","in_txt":"","in_bin":[],"out_txt":"[ali@srv ~]$ grep -c . /etc/passwd\r\n24\r\n[ali@srv ~]$ cat /etc/app.conf\r\nmax_connections=500\r\nmax_connections=500\r\n[ali@srv ~]$ sudo id\r\n[sudo] password for ali: ","out_bin":[]}
{"ver":"2.3","host":"srv.example.com","rec":"bfd2797f5f8240a49e33cdb91446bcdf-329-187dc8c","user":"ali","term":"","session":356,"id":2,"pos":10955,"time":1792969370.726,"timing":"=0x0>2+432>40+47>13+2521>14","in_txt":"","in_bin":[],"out_txt":"\r\nuid=0(root) gid=0(root) groups=0(root)\r\n[ali@srv ~]$ exit\r\nlogout\r\n","out_bin":[]}

في الرسائل أعلاه لاحظ التالي:

  • rec معرف التسجيل، وهو ثابت لكل رسائل الجلسة الواحدة، وبه تطلب الإعادة.
  • id رقم الرسالة داخل التسجيل، وpos موضعها بالمللي ثانية من بداية الجلسة، فإذا ضاعت رسالة في الطريق إلى SIEM تعرف ذلك من فجوة في الأرقام.
  • timing سلسلة مضغوطة تقول متى ظهر كل جزء، فالجزء +1845 يعني انتظار 1845 مللي ثانية، و>23 يعني 23 حرفاً من المخرج، وهذا ما يجعل الإعادة بنفس السرعة الأصلية.
  • in_txt فارغ، لأن tlog لا يسجل المدخلات افتراضياً، للسبب نفسه الذي ذكرناه مع log_input، وكلمة مرور sudo لا تظهر في out_txt لأن الطرفية لا تعرضها.
  • الأهم: الأمر grep -c . /etc/passwd الذي نفذه ali قبل sudo موجود هذه المرة، ومعه قراءة ملف الإعداد، وهذه هي الفجوة التي لم يغطها sudo.

ثم نعيد الجلسة بالأمر tlog-play، والخيار -s 10 يسرعها عشر مرات:

tlog-play -r file -i /var/log/tlog/tlog.log -m bfd2797f5f8240a49e33cdb91446bcdf-329-187dc8c -s 10
[ali@srv ~]$ grep -c . /etc/passwd
24
[ali@srv ~]$ cat /etc/app.conf
max_connections=500
max_connections=500
[ali@srv ~]$ sudo id
[sudo] password for ali: 
uid=0(root) gid=0(root) groups=0(root)
[ali@srv ~]$ exit
logout

وtlog-play يحتاج طرفية حقيقية حتى يعمل، فعندما شغلناه دون طرفية خرج دون أن يعرض شيئاً، لذلك شغلناه داخل الأمر script الذي يوفر له طرفية.

هل يستطيع ali أن يمحو تسجيل tlog؟

#!/bin/bash
s() { sleep 3; echo "$1"; }
s 'rm -f /var/log/tlog/tlog.log'
s 'ls -l /var/log/tlog/'
s 'exit'
rm: cannot remove '/var/log/tlog/tlog.log': Permission denied
[ali@srv ~]$ ls -l /var/log/tlog/
ls: cannot open directory '/var/log/tlog/': Permission denied

المستخدم العادي لا يستطيع، وهذا ما وعد به تصميم setuid. ولكن ali يملك sudo، وroot يستطيع أن يحذف الملف كما حذف تسجيلات sudo، لذلك ينطبق على tlog ما انطبق على sudo: أرسل الرسائل خارج السيرفر أثناء الجلسة، عبر journald ثم ناقل سجلات مثل rsyslog أو Vector إلى SIEM، أو عبر الكاتب syslog مباشرة إلى سيرفر مركزي.

الطبقة الخامسة: auditd، ولماذا لم نشغله هنا

الطبقات السابقة كلها مبنية على الطرفية، فإذا شغل المسؤول سكربتاً عبر ssh srv 'command' دون طرفية، أو شغلت أداة أتمتة أوامر root، فلن تسجل tlog ولا sudo I/O شيئاً مفيداً. وهنا يأتي نظام التدقيق في النواة auditd، الذي يسجل كل برنامج يشغل بالقاعدة execve، ومعه auid، أي المعرف الأصلي للمستخدم الذي دخل، والذي يبقى ali حتى بعد أن يصبح root. وقد شرحنا القواعد المناسبة وطريقة قفلها بالخيار -e 2 في قسم auditd من مقال سجل التدقيق، وذكرنا هناك أن auditd لا يعمل داخل Container، لأن النواة ترفض أوامر التحكم بالتدقيق من غير فضاء أسماء العمليات الأول Initial PID Namespace، لذلك لم نشغله في هذا المختبر. وهناك أيضاً وحدة PAM اسمها pam_tty_audit ترسل ما يكتبه المستخدم إلى نظام التدقيق، وتنبه صفحتها نفسها إلى أنها قد تسجل كلمات المرور، فاستخدمها بنفس الحذر الذي ذكرناه مع log_input.

متى تنتقل إلى بوابة تسجل الجلسة؟

كل ما سبق يعمل على كل سيرفر بمفرده، ويحتاج إعداداً على كل سيرفر، ويعتمد على أن root على السيرفر لم يعطل التسجيل قبل أن يبدأ. والطريقة الأخرى أن يمر كل دخول إلى السيرفرات عبر بوابة واحدة Bastion أو Jump Server تسجل الجلسة قبل أن تصل إلى السيرفر، فلا يستطيع root على أي سيرفر أن يمحو تسجيلاً لا يوجد عنده أصلاً. ومن البوابات مفتوحة المصدر Teleport Community Edition وWarpgate وJumpServer، وApache Guacamole للدخول من المتصفح، وقد قارنا بينها في خريطة أدوات الوصول الآمن. والقاعدة العملية:

الحالةالأنسب
سيرفرات قليلة، ومسؤولون يستخدمون sudosudo I/O مع sudo_logsrvd
تحتاج كل ما يحدث في الجلسة، حتى خارج sudotlog مع journald وSIEM
عشرات السيرفرات، ومتعاقدون من خارج الشركةبوابة تسجل الجلسة، ومعها sudo I/O كطبقة ثانية
أتمتة وسكربتات دون طرفيةauditd مع قواعد execve وauid
متطلبات امتثال صارمةبوابة أو tlog، وsudo I/O، وauditd، كلها خارج السيرفر

الخصوصية والشفافية: ما يجب أن يعرفه المسؤولون

تسجيل الجلسات يسجل أشخاصاً، وقد يظهر في التسجيل ما لا علاقة له بالعمل، مثل رسالة شخصية فتحها المسؤول في طرفية أو كلمة مرور ظهرت في مخرج أمر. لذلك عامل التسجيلات كبيانات حساسة:

  • أعلن التسجيل: الرسالة ATTENTION! Your session is being recorded! في tlog، ورسالة مشابهة في /etc/issue.net مع الخيار Banner في sshd_config، وبند في سياسة الاستخدام المقبول التي يوقعها المسؤولون. والتسجيل المعلن يردع، والتسجيل السري يهدم الثقة.
  • لا تسجل المدخلات إلا بقرار: log_input وإدخال tlog وpam_tty_audit تحفظ كلمات المرور، فلا تفعلها إلا إذا كانت هناك حاجة مكتوبة.
  • حدد من يشاهد: سيرفر السجلات لا يدخل إليه مسؤولو الإنتاج، والمشاهدة تحتاج سبباً مسجلاً، مثل رقم حادث، ومشاهدة التسجيل نفسها تسجل.
  • مدة الاحتفاظ: 12 شهراً على الأقل لمن يخضع لـ ECC كما ذكرنا، واكتب المدة في السياسة، ثم احذف ما بعدها، فالتسجيل الذي لا تحتاجه عبء وليس أصلاً.
  • البيانات الشخصية: إذا ظهرت في التسجيلات بيانات شخصية لعملاء المنصة فهي تخضع لنظام حماية البيانات الشخصية PDPL مثل أي نسخة أخرى منها، فاحمها بنفس المستوى.

التسجيل الذي لا يشاهده أحد لا يحمي أحداً

أكثر خطأ يتكرر أن تفعل التسجيل ثم لا يفتحه أحد حتى يقع الحادث، وعندها تكتشف أن التسجيل كان متوقفاً منذ شهرين. لذلك اجعل المراجعة عملاً دورياً صغيراً:

  • تنبيه آلي: حدث sudo على سيرفر إنتاج خارج نافذة التغييرات Change Window، أو جلسة root أطول من ساعة، أو أي جلسة من حساب الطوارئ، كلها تنبيهات تصل فوراً إلى الفريق.
  • عينة أسبوعية: يختار شخص غير المسؤول عن السيرفرات جلستين أو ثلاثاً كل أسبوع ويشاهدها بسرعة -m 0، ويقارنها بالتذاكر المفتوحة.
  • اختبار التسجيل نفسه: مرة كل شهر نفذ أمراً معروفاً بـ sudo ثم ابحث عنه على سيرفر السجلات، فهذه أسرع طريقة لتعرف أن الطبقة ما زالت تعمل، وتذكر ما حدث مع log_subcmds.
  • ربط السجلات: اجمع حدث الدخول من sshd، وحدث sudo من sudo_logsrvd، ورسائل tlog، في SIEM واحد بنفس المعرفات، فتستطيع أن تنتقل من تنبيه إلى الجلسة كاملة بنقرة.

أما الحجم فهو أصغر مما يتوقع الناس: كل جلسات المختبر أخذت على سيرفر السجلات 72 كيلوبايت مضغوطة، والبرنامج sudo_logsrvd نفسه كان يستهلك قرابة 7 ميجابايت من الذاكرة. والجلسات التي تعرض مخرجات ضخمة، مثل tail -f لسجل مشغول طوال الليل، هي التي تكبر، فراقب حجم المجلد في الأسابيع الأولى قبل أن تقرر المساحة.

كيف طبقت شركة الحجوزات ذلك خلال شهر؟

بعد صباح الأحد قرر فريق الشركة ألا يفعل كل شيء في يوم واحد، لأن تسجيلاً يتعطل فيمنع المسؤولين من sudo في منتصف حادث أسوأ من عدم التسجيل. فكانت الخطة على أربعة أسابيع، وكل أسبوع له علامة نجاح يقيسها الفريق قبل أن ينتقل إلى التالي:

  1. الأسبوع الأول، الأحداث: التأكد أن journald يعمل على كل سيرفر، وإضافة logfile إلى sudoers، وإرسال سجلات sshd وsudo إلى SIEM. وعلامة النجاح أن كل أمر sudo تجريبي يظهر في SIEM خلال دقيقة.
  2. الأسبوع الثاني، سيرفر السجلات: سيرفران صغيران عليهما sudo_logsrvd خلف شهادات من جهة التوقيع الداخلية، لا يدخل إليهما إلا مسؤول الأمن، ومراقبة تنبه عندما يتوقف أي منهما. وعلامة النجاح أن إيقاف أحدهما لا يوقف sudo لأن الثاني يستقبل.
  3. الأسبوع الثالث، تسجيل sudo: تفعيل log_output وlog_servers على سيرفر اختبار ثم على سيرفر إنتاج واحد ثم على الباقي، مع حساب طوارئ مختبر على كل سيرفر. وعلامة النجاح أن يعيد مسؤول الأمن جلسة من كل سيرفر بـ sudoreplay.
  4. الأسبوع الرابع، الجلسة كاملة: tlog لحسابات المسؤولين فقط على سيرفرات قاعدة البيانات والدفع، مع الكاتب journal، ثم سياسة مكتوبة تذكر ما يسجل ومن يشاهد ومدة الاحتفاظ، يوقعها المسؤولون الثلاثة.

ولو تكررت ليلة الخميس بعد هذا الشهر، فإن صباح الأحد سوف يبدأ بتنبيه في SIEM يقول إن ali استخدم sudo على سيرفر الإنتاج في الساعة الثانية فجراً دون تذكرة، ثم بجلسة كاملة على سيرفر السجلات فيها كل أمر، ومنها محاولة المحو إن حدثت. وهذا هو الفرق بين سؤال «من فعل؟» الذي لا يجيب عنه أحد، وسؤال «لماذا فعل؟» الذي يمكن أن تسأله لشخص معروف.

قائمة تحقق

  • اكتب أحداث sudo إلى مكان موجود فعلاً: journald أو ملف logfile، وتأكد أنك تجد السطر بعد أمر تجريبي.
  • فعل log_output لكل الحسابات المميزة، واستثن sudoreplay، واترك log_input معطلاً إلا بقرار مكتوب.
  • أرسل التسجيلات إلى sudo_logsrvd عبر TLS على 30343، مع سيرفرين في log_servers، وشهادة بامتداد keyUsage صحيح.
  • اختر الفشل المغلق، وجهز حساب طوارئ مراقباً للحالة التي يتوقف فيها سيرفر السجلات.
  • إذا احتجت كل الجلسة فاجعل tlog شيل المسؤولين، مع الكاتب journal ونقل السجلات إلى الخارج، وجربه أولاً على حساب ليس حسابك الوحيد.
  • فعل auditd على السيرفرات نفسها مع قواعد execve وauid وقفلها، للأوامر التي لا تمر بطرفية.
  • لا تثق بأي خيار لأنه قبل في visudo -c، وجرب كل طبقة بجلسة حقيقية، مثل log_subcmds المعطل في Fedora.
  • أعلن التسجيل، وحدد من يشاهد، واحتفظ 12 شهراً على الأقل لمن يخضع لـ ECC، وراجع عينة كل أسبوع.
دليلإدارة الهوية والوصول IAM والوصول المميز PAMمن يملك صلاحية root على سيرفراتك ومنذ متى، وكيف تضبط حسابات المديرين بصلاحيات مؤقتة وجلسات مسجلة.دليلAudit Trail: كيف تبني سجل تدقيق لا يقبل العبثكيف تصمم سجلاً يجيب عن سؤال من غير هذه الصلاحية ومتى، وتحميه من الحذف والتعديل بعد كتابته.دليلأدوات الوصول الآمن إلى السيرفرات: خريطة مفتوحة المصدرخريطة لأدوات الوصول الآمن مفتوحة المصدر إلى السيرفرات، ماذا تحل كل عائلة منها وما ترخيصها، وكيف تختار ما يناسب فريقك.دليلدورة حياة حساب الموظف JML من أول يوم إلى آخر يومكيف تجعل الموارد البشرية مصدر كل حساب، فيجد الموظف ما يحتاجه في يومه الأول وتغلق كل أبوابه في يوم مغادرته.دليلApache Guacamole: الوصول إلى سيرفراتك من المتصفحكيف تثبت Apache Guacamole على سيرفرك، وتدخل منه إلى سيرفراتك من المتصفح بجلسات مسجلة ودخول موحد ومصادقة ثنائية.دليلالضوابط الأساسية للأمن السيبراني NCA ECC بلغة بسيطةكيف تقرأ الضوابط الأساسية ECC دون أن تضيع في أرقامها، وعلى من تنطبق، وما المطلوب من جهتك فعلاً في كل مكون.

الخلاصة

  • الإعداد الافتراضي يعرف من دخل ولا يعرف ماذا فعل، وفي تجربتنا لم يبق من ليلة كاملة إلا سطر في wtmp، لأن sudo يكتب إلى syslog ولم يكن هناك من يستقبله، وتاريخ الأوامر مسحه صاحبه.
  • التسجيل خمس طبقات: الدخول، وأحداث sudo، وتسجيل جلسة sudo، والجلسة كاملة، وتدقيق النواة، وكل طبقة ترى ما لا تراه الأخرى.
  • log_output في sudo يسجل كل ما ظهر على الشاشة داخل sudo -i ويعاد بـ sudoreplay، ولكن root يمحو التسجيل المحلي بأمرين كما رأينا.
  • sudo_logsrvd يخرج التسجيل من السيرفر أثناء الجلسة عبر TLS، فتصبح محاولة المحو نفسها دليلاً، والافتراضي أن sudo يرفض العمل إذا لم يصل إلى سيرفر السجلات.
  • log_subcmds لا يعمل في Fedora 44 لأن sudo مبني بـ --disable-intercept، وvisudo لا يحذرك، فجرب كل إعداد بجلسة حقيقية.
  • tlog يسجل الجلسة كلها بصيغة JSON بما فيها ما قبل sudo، ويعمل بهوية tlog فلا يمحوه المستخدم، واحتاج في الـ Container إلى /run/tlog و/run/utmp.
  • التسجيل بيانات حساسة: أعلنه، ولا تسجل المدخلات دون قرار، وحدد من يشاهد، واحتفظ 12 شهراً، وراجع عينة كل أسبوع.

سجل التحديثات

  • أكتوبر 2026: كتابة المقال، وتنفيذ التجربة على Fedora 44 مع sudo 1.9.17p2 وsudo-logsrvd وtlog 14 وOpenSSH 10.2p1.
Could not load content
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.