FAQ

این راهنمای نصب، پیکربندی و نگهداری Arcivéo Security Monitor است. بخش‌ها گروه‌بندی شده‌اند: مرور کلی، استقرار پنل، اتصال ابزارهای امنیتی، ماژول‌های داخلی و عیب‌یابی. دستورها را می‌توان با دکمهٔ سمت راست کپی کرد.

شروع کار

01. نصب پنل — روش را انتخاب کنید

نصب پنل در صفحه‌های گام‌به‌گام جداگانه توضیح داده شده است. روش را انتخاب کنید:

مطمئن نیستید — روش خودکار را انتخاب کنید. این راهنما همچنان مرجع یکپارچه SSL، ابزارها، cron و عیب‌یابی است — صفحه‌های نصب به بخش‌های آن ارجاع می‌دهند و چیزی را تکرار نمی‌کنند.

نمای کلی

02. Arcivéo Security Monitor چیست

Arcivéo Security Monitor — داشبورد امنیت سرور. داده‌ها را از ابزارهای نصب‌شده (Fail2ban، UFW، Lynis، ModSecurity، AIDE، ClamAV، Auditd، CrowdSec، Suricata، Falco و غیره) جمع‌آوری کرده و آن‌ها را در یک رابط یکپارچه با داشبورد، نقشهٔ حملات و صفحات جزئیات هر ابزار نمایش می‌دهد.

Monitor یک ابزار حفاظتی فعال نیست — خودش حملات را مسدود نمی‌کند. وظیفهٔ آن گردآوری اطلاعات از ابزارهای درحال‌اجرا و نمایش آن به شکلی کاربردی است.

03. نحوهٔ کار مانیتور روی سرور

مانیتور فقط به‌صورت محلی کار می‌کند — باید روی همان سروری نصب شود که پایش می‌کند. هیچ SSH یا API از راه دور وجود ندارد.

همهٔ فرمان‌ها (fail2ban-client، ufw status، ipset list و غیره) را پنل با کاربر وب‌سرور (معمولاً www-data، و در پنل‌های میزبانی حساب سایت) و با مجموعهٔ محدودی از دسترسی‌های sudo اجرا می‌کند — فقط روی ابزارهای مشخص، بدون دسترسی کامل root. نتایج تجزیه شده و در مرورگر نمایش داده می‌شوند.

دو ریتم کار. صفحه‌های پنل در همان لحظهٔ باز شدن از ابزارها می‌پرسند — بنابراین همیشه وضعیت کنونی را می‌بینید. جدا از آن، کارهایی هم طبق زمان‌بندی (cron) در پس‌زمینه اجرا می‌شوند:

  • هر ۵ دقیقه — نمای لحظه‌ای بار برای صفحهٔ «کارایی»؛
  • هر ۵ دقیقه — حسگر رویدادهای امنیتی: مسدودسازی‌های تازه، هشدارهای IDS، سرویس حفاظتی متوقف‌شده، بندری که باز شده است (بخش «رویدادهای امنیتی»)؛
  • روزی یک بار — گزارش امنیتی به Telegram و ایمیل، و نیز بررسی‌های سنگین (Lynis، AIDE، ClamAV، debsums، Logwatch)؛
  • هر ۱۵ دقیقه — خلاصهٔ کارساز به حساب کاربری، در صورتی که روشن باشد.

فهرست کامل کارها در بخش «همه‌ی cron-jobها در یک جا» آمده است.

اگر چند کارساز دارید، مانیتور را روی هرکدام جداگانه و با دامنهٔ اختصاصی نصب کنید. برای دیدن همه در یک فهرست، خلاصهٔ کارسازها را روشن کنید (بخش «خلاصهٔ کارسازها در حساب کاربری»).

04. نحوهٔ محاسبهٔ امتیاز امنیت

امتیاز از حداکثر آغاز می‌شود و برای هر مشکل شناسایی‌شده کاهش می‌یابد:

  • UFW فعال نیست — −30
  • Fail2ban اجرا نشده (jail فعالی وجود ندارد) — −25
  • هیچ کلید WebAuthn وجود ندارد — −15
  • شاخص hardening ابزار Lynis < 60 — −20؛ 60–79 — −10
  • IPset ipsum بارگذاری نشده — −10
  • تهدیدهای یافت‌شده توسط ClamAV — −20
  • تغییرات فایل‌ها توسط AIDE — −15
  • SSL منقضی شده — −30، انقضا در <14 روز — −15، <30 روز — −5
  • CrowdSec نصب شده اما اجرا نشده — −5
  • Suricata نصب شده اما اجرا نشده — −5
  • پایگاه‌داده/کش (MySQL، PostgreSQL، Redis…) از بیرون در دسترس‌اند — −10
  • ورود root از طریق SSH مجاز است (PermitRootLogin yes) — −20
  • ورود با گذرواژه از طریق SSH فعال است (PasswordAuthentication yes) — −10
  • تنظیمات جزئی SSH: X11Forwarding فعال است، MaxAuthTries بیش از ۳ است یا فایل sshd_config برای همهٔ حساب‌ها خواندنی است — −5
  • سخت‌سازی وب‌سرور: پاسخ‌ها نسخهٔ وب‌سرور یا PHP را نشان می‌دهند، متد TRACE فعال است یا هدر Strict-Transport-Security یا X-Frame-Options وجود ندارد — −5
  • به‌روزرسانی‌های امنیتی در انتظار نصب هستند — −5

نتیجه: 80+ = محافظت‌شده، 60–79 = هشدار، <60 = در معرض خطر.

کسر امتیاز برای ClamAV، AIDE، CrowdSec و Suricata فقط زمانی اعمال می‌شود که ابزار نصب شده باشد. Lynis و AIDE بدون پایگاه‌دادهٔ مقداردهی‌شده به‌صورت «بدون داده» نمایش داده می‌شوند و امتیاز را کاهش نمی‌دهند. تعداد حملات امروز در داشبورد نمایش داده می‌شود اما بر امتیاز امنیت اثری ندارد. اگر هیچ سایتی به بررسی پاسخ ندهد، سخت‌سازی وب‌سرور در امتیاز حساب نمی‌شود.

تنظیمات و لایسنس

05. WebAuthn — احراز هویت دو عاملی

WebAuthn — استاندارد احراز هویت بدون رمز عبور از طریق کلید سخت‌افزاری. از YubiKey، Touch ID، Face ID، Windows Hello و Passkey پشتیبانی می‌کند.

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

برای پیکربندی، بخش کلیدهای WebAuthn را در منوی کناری باز کنید و روی «ثبت کلید» بزنید. همان ابتدا دو کلید ثبت کنید: اگر تنها کلید گم شود یا خراب شود، ورود به پنل با آن ممکن نخواهد بود.

WebAuthn فقط از طریق HTTPS کار می‌کند. روی اتصال HTTP ثبت و ورود با کلید در دسترس نیست.

06. اعلان‌ها: Telegram، ایمیل، webhook و اعلان فوری

کانال‌ها چهارتاست: Telegram، ایمیل، webhook و اعلان فوری در مرورگر. اینها از یکدیگر مستقل‌اند — هرکدام را که خواستید روشن کنید، حتی هر چهار تا را. گزارش امنیتی روزانه، هشدارهای بار و یافته‌های حسگر رویداد هم از همین کانال‌ها می‌روند؛ تنظیمشان یک بار همین‌جا، در «تنظیمات» انجام می‌شود.

Telegram. به توکن ربات و chat id نیاز دارید:

  1. در Telegram به @BotFather پیام دهید ← /newbot ← یک توکن به شکل 123456:ABC... دریافت کنید.
  2. به ربات جدید خود هر پیامی بفرستید (تا بتواند به شما پاسخ دهد).
  3. chat id خود را پیدا کنید: به ربات @userinfobot پیام دهید، یا https://api.telegram.org/bot<TOKEN>/getUpdates را باز کنید و "chat":{"id":...} را بیابید.
  4. توکن و chat id را در «تنظیمات» ← Telegram وارد کنید و «ذخیره و ارسال آزمایشی» را بزنید.

ایمیل. دو روش برای انتخاب در «تنظیمات» ← ایمیل:

  • SMTP — هاست، پورت (465/SSL یا 587/TLS)، نام کاربری و رمز عبور صندوق پستی شما؛
  • Resend — یک API مدرن: کلید API (re_...) و دامنه تأییدشده فرستنده را وارد کنید.

Webhook. یک نشانی که POST بپذیرد، Slack و Discord و Mattermost و n8n و پردازشگر خودتان را پوشش می‌دهد. «تنظیمات» ← Webhook: نشانی را بچسبانید و کلید را روشن کنید.

  • به Slack و Mattermost شکل {"text": …} فرستاده می‌شود و به Discord شکل {"content": …} (Discord پیام‌های بلندتر از ۲۰۰۰ نویسه را نمی‌پذیرد، از این رو متن کوتاه می‌شود)؛
  • هر نشانی دیگر JSON ساخت‌یافته دریافت می‌کند: text، event، severity، source، count، host، ts — دیگر لازم نیست متن پیام دوباره تجزیه شود.

اعلان‌های فوری. پیام مستقیم به مرورگر می‌رسد و اگر پنل را به صفحهٔ اصلی گوشی افزوده باشید، مانند اعلان معمولی یک برنامه نمایان می‌شود — بی‌آنکه بات یا کارساز ایمیلی در میان باشد. «تنظیمات» ← اعلان‌های فوری: کلید را روشن کنید و روی هر دستگاهی که باید اعلان بگیرد دکمهٔ «اتصال این دستگاه» را بزنید. شمارندهٔ «دستگاه‌های متصل» نشان می‌دهد چند تا هستند.

اعلان فوری تنها روی HTTPS کار می‌کند (درست مانند WebAuthn) و اجازه دو بار لازم است: نخست در مرورگر — پرسش پس از دکمهٔ «اتصال این دستگاه» ظاهر می‌شود — و سپس در خود سیستم‌عامل. اگر در Windows کلید اصلی تنظیمات ← سیستم ← اعلان‌ها خاموش باشد، مرورگر اجازه را می‌دهد و پنل هم ارسال را گزارش می‌کند، اما روی صفحه هیچ چیز ظاهر نمی‌شود. حالت «مزاحم نشوید» و خاموش‌بودن اعلان‌های مرورگر در macOS (تنظیمات سیستم ← اعلان‌ها) نیز همین نتیجه را دارد.
هر کانال دکمهٔ «ارسال آزمایشی» خودش را دارد که بی‌درنگ آن را می‌آزماید. زمان‌بندی گزارش خودکار از راه cron انجام می‌شود (بخش «همه‌ی cron-jobها»): cron ارسال را آغاز می‌کند و کانال‌ها از تنظیمات برداشته می‌شوند.

وضعیت گزارش: «هشدار» یا «سالم». عنوان فقط در صورت وجود یک مشکل واقعی یا اقدامی در انتظار، به «هشدار» تغییر می‌کند: کشف تهدید ClamAV، تغییر فایل‌ها در AIDE، رویدادهای بحرانی Falco (Emergency/Alert/Critical در ۲۴ ساعت گذشته)، از کار افتادن سرویس در Monit، نیاز به راه‌اندازی مجدد، انقضای SSL (≤۱۴ روز) یا انتظار برای به‌روزرسانی‌های امنیتی. نویز پس‌زمینه — تلاش‌های SSH ربات‌ها، IPهای بن‌شده توسط fail2ban، هشدارهای Suricata، اخطارهای Lynis و درخواست‌های ازپیش‌دفع‌شده ModSecurity — وضعیت را بالا نمی‌برد، بنابراین چنین اعدادی در گزارش به‌خودی‌خود به معنای «هشدار» نیستند.

07. لایسنس — وارد کردن و فعال‌سازی

ماژول‌های تفصیلی پایش (Lynis، UFW، ModSecurity، نقشه حملات، AIDE، ClamAV و غیره) با داشتن لایسنس معتبر باز می‌شوند. بدون آن، داشبورد، تنظیمات و حساب کاربری کار می‌کنند، اما ماژول‌ها کارت «نیازمند لایسنس» را نشان می‌دهند.

پس از خرید در حساب کاربری، یک کد فعال‌سازی به شکل ARCIVEO-XXXX-XXXX-XXXX-XXXX دارید. باید آن را روی دامنه پنل خود «فعال» کنید — این کار کد را به یک فایل لایسنس امضاشده (بلوک [license]) تبدیل می‌کند که آن را در پنل وارد می‌کنید.

نحوه فعال‌سازی (۳ گام):

  1. کد فعال‌سازی را بردارید. حساب کاربری my.arciveo.com ← بخش «لایسنس‌ها» / «فعال‌سازی لایسنس» — کد ARCIVEO-… را کپی کنید.
  2. کد را روی دامنه خود فعال کنید. در همان حساب کاربری، «فعال‌سازی لایسنس» را باز کنید و وارد کنید: کد فعال‌سازی، ایمیل شما و دامنه پنل (نشانی‌ای که مانیتور با آن باز می‌شود، مثلاً monitor.example.com). روی فعال‌سازی بزنید — سیستم یک فایل لایسنس متصل به این دامنه می‌سازد و آن را در فیلدی با دکمه «کپی» نمایش می‌دهد.
  3. کلید را در پنل وارد کنید. کل متن لایسنس را کپی کنید ← در پنل «تنظیمات» ← بلوک «لایسنس» را باز کنید، جای‌گذاری کنید و «ذخیره» را بزنید. ماژول‌ها بلافاصله باز می‌شوند.

پنل کلید را به‌صورت رمزنگاری‌شده بررسی می‌کند: امضا، اتصال به دامنه و مدت اعتبار.

دامنه هنگام فعال‌سازی باید دقیقاً با نشانی پنل یکسان باشد. آن را از ثابت APP_URL در config.php بردارید و فقط نام میزبان را وارد کنید — بدون https:// و بدون پیشوند www. فعال‌سازی یک‌بار مصرف است: کد به لایسنسِ دامنه واردشده تبدیل می‌شود و دوباره فعال نمی‌شود — با اشتباه در دامنه، کلید با پنل شما جور در نمی‌آید و کد مصرف می‌شود. بنابراین دامنه را با دقت وارد کنید.
اگر مدت اعتبار به پایان رسید یا دامنه تغییر کرد — در سربرگ پنل هشداری ظاهر می‌شود. لایسنس برای همیشه به دامنه متصل است و به دامنه دیگر منتقل نمی‌شود: برای مدت جدید یا دامنه جدید به کلید جدید نیاز دارید (در حساب کاربری خریداری و یک‌بار فعال می‌شود).

08. فایل config.php — همه تنظیمات پنل

همه پارامترهای اصلی پنل در یک فایل config.php در ریشه (کنار پوشه public/) با ثابت‌های معمول define() تعریف شده‌اند. این فایل هنگام نصب ساخته می‌شود؛ ویرایش دستی آن به‌ندرت لازم است — بیشتر هنگام تغییر دامنه، انتقال یا اتصال به پایگاه‌داده دیگر. پس از هر ویرایش PHP-FPM را دوباره راه‌اندازی کنید (وگرنه به‌دلیل OPcache تغییرات اعمال نمی‌شوند).

مقادیر خود را در جاهای مشخص‌شده جایگزین کنید؛ بقیه را همان‌طور رها کنید:

// --- پایگاه‌داده --- define('DB_HOST', 'localhost'); // رها کنید define('DB_NAME', 'db_name'); // آنچه هنگام ساخت پایگاه‌داده تعیین کردید define('DB_USER', 'user'); // آنچه هنگام ساخت پایگاه‌داده تعیین کردید define('DB_PASS', 'db_password'); // آنچه هنگام ساخت پایگاه‌داده تعیین کردید define('DB_CHARSET', 'utf8mb4'); // رها کنید // --- برنامه --- define('APP_URL', 'https://monitor.example.com'); // آدرس پنل، بدون اسلش در انتها define('TIMEZONE', 'Asia/Tehran'); // منطقه زمانی شما // --- مدت نشست --- define('SESSION_LIFETIME', 28800); // بی‌کاری تا ورود مجدد، ثانیه (28800 = ۸ ساعت)

پایگاه‌داده. مشخصات اتصال به MySQL/MariaDB:

  • DB_HOST — میزبان سامانه پایگاه‌داده، تقریباً همیشه localhost؛
  • DB_NAME — نام پایگاه‌داده پنل؛
  • DB_USER — کاربر پایگاه‌داده (فقط به پایگاه‌داده خودش دسترسی دارد)؛
  • DB_PASS — گذرواژه این کاربر؛
  • DB_CHARSET — رمزگذاری اتصال، utf8mb4 را رها کنید.

برنامه.

  • APP_URL — آدرس کامل پنل (مثلاً https://monitor.example.com). باید با دامنه‌ای که لایسنس روی آن فعال شده مطابقت داشته باشد — وگرنه کلید رد می‌شود (بخش «لایسنس» را ببینید)؛
  • TIMEZONE — منطقه زمانی PHP: فقط بر نحوه نمایش تاریخ و زمان در پنل اثر می‌گذارد. بر زمان اجرای وظایف cron اثری ندارد — آنجا منطقه زمانی سیستم عمل می‌کند (بخش «همه وظایف cron» را ببینید).

مدت نشست. SESSION_LIFETIME — مهلت بی‌کاری نشست به ثانیه (لغزان: با فعالیت به‌روز می‌شود). به‌طور پیش‌فرض 28800 = ۸ ساعت؛ پس از این مدت بی‌کاری پنل ورود مجدد را می‌خواهد. برای مثال 3600 = ۱ ساعت، 86400 = یک شبانه‌روز.

ثبت خطاها. خطاها هرگز به بازدیدکنندگان نشان داده نمی‌شوند، بلکه در logs/php_errors.log نوشته می‌شوند — در صفحه «لاگ‌های برنامه» دیده می‌شوند. این خطوط (display_errors=0، log_errors=1، مسیر error_log) معمولاً نیازی به تغییر ندارند — تنظیمات مستقیماً در فایل تعریف شده‌اند و به php.ini وابسته نیستند.

config.php — فایلی محرمانه است. گذرواژه پایگاه‌داده در آن است. در ریشه پنل (کنار public/) قرار دارد، و ریشه وب (DocumentRoot) این پنل دقیقاً همان ریشه پنل است، نه public/. خود فایل به‌تنهایی «نشت» نمی‌کند: در .htaccess ریشه برای آن منع صریح گذاشته شده (Require all denied) — سرور 403 برمی‌گرداند. حتی بدون این قاعده هم کد منبع نشت نمی‌کرد: این PHP است — سرور آن را اجرا می‌کند، نه اینکه به‌صورت متن بدهد. برای احتیاط: آن را در مخازن عمومی قرار ندهید و با گذرواژه واقعی به پشتیبانی نفرستید. مجوز فایل — 640.
هنگام انتقال یا بازیابی دسترسی، این فایل منبع اصلی مشخصات است: نام پایگاه‌داده، کاربر و گذرواژه دقیقاً از همین‌جا گرفته می‌شوند (بخش‌های «به‌روزرسانی و انتقال پنل» و «بازیابی دسترسی» را ببینید).

09. API فقط‌خواندنی (JSON و Prometheus)

پنل پشت ورود با گذرواژه است و اگر پالایهٔ IP روشن باشد پشت آن هم. برای یک برنامه هیچ‌کدام به کار نمی‌آید، پس سامانه‌های بیرونی ورودی جداگانهٔ خود را دارند — دو نشانی فقط‌خواندنی که با توکن محافظت می‌شوند. توکن روی کارساز چیزی را تغییر نمی‌دهد: تنها می‌توان با آن شاخص‌ها را گرفت.

روشن‌کردن — «تنظیمات» ← «API فقط‌خواندنی»: کلید «روشن کردن API»؛ توکن در نخستین ذخیره خودکار ساخته می‌شود. دکمهٔ «توکن تازه» توکن دیگری صادر می‌کند و در همان لحظه دسترسی همهٔ کسانی را که از توکن پیشین استفاده می‌کردند قطع می‌کند.

  • /api/status.php — JSON: امتیاز امنیت، وضعیت ماژول‌ها، آخرین اندازه‌گیری بار همراه با سنگین‌ترین فرایندها، و رویدادهای امنیتی ۲۴ ساعت گذشته؛
  • /api/metrics.php — همان عددها در قالب متنی Prometheus (سنجه‌های arciveo_*: arciveo_security_score، arciveo_fail2ban_banned، arciveo_cpu_busy_pct و جز آن).
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

اگر کارخواه شما نمی‌تواند سرایند بگذارد، توکن به‌صورت پارامتر هم پذیرفته می‌شود — ?token=…. توجه داشته باشید که در این شکل به گزارش دسترسی کارساز وب راه می‌یابد، پس سرایند بهتر است.

Prometheus. بلوک آمادهٔ scrape_configs:

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
پاسخ ۵ دقیقه در حافظهٔ نهان می‌ماند: پشت این عددها همان گردآوری سنگینی است که پشت داشبورد قرار دارد. پس پرسش پی‌درپی هزینه‌ای برای کارساز ندارد — مقادیر تا به‌روزرسانی بعدی تکرار می‌شوند — اما فاصلهٔ کمتر از یک دقیقه هم معنایی ندارد.
۴۰۱ — توکن نادرست است یا فرستاده نشده. ۴۰۴ با متن api disabled — API در تنظیمات خاموش است. درخواست‌های ردشده همراه با IP فرستنده در گزارش برنامه نوشته می‌شوند، پس تلاش برای حدس‌زدن توکن در صفحهٔ «لاگ‌های برنامه» دیده می‌شود.

10. خلاصهٔ کارسازها در حساب کاربری

پنل روی هر کارساز جداگانه نصب می‌شود و به پرسش «حال این ماشین چطور است» خوب پاسخ می‌دهد. به پرسش «حال همهٔ ماشین‌های من چطور است» پاسخ نمی‌دهد — برای همین هر پنل می‌تواند خلاصه‌ای کوتاه به حساب کاربری بفرستد، جایی که کارسازها در یک فهرست دیده می‌شوند.

آنچه در حساب کاربری برمی‌دارید my.arciveo.com ← بخش «کارسازهای من»: آنجا یک توکن (به شکل fk_…) صادر می‌شود و نشانی گیرنده https://my.arciveo.com/api/fleet.php آورده شده است. توکن برای هر حساب یکی است — برای همهٔ کارسازهای شما همان یکی.

آنچه در پنل تنظیم می‌کنید — «تنظیمات» ← «خلاصهٔ کارسازها»: نشانی گیرنده، توکن، نام کارساز (نامی که در فهرست می‌آید؛ خالی بگذارید، دامنهٔ پنل برداشته می‌شود) و کلید «فرستادن خلاصه». دکمهٔ «همین حالا بفرست» ارتباط را بی‌درنگ و بدون انتظار برای زمان‌بندی می‌آزماید.

پس از آن خلاصه با cron هر ۱۵ دقیقه فرستاده می‌شود:

# هر ۱۵ دقیقه — خلاصهٔ کارساز به حساب کاربری 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

دقیقاً چه چیزی فرستاده می‌شود: نام و دامنهٔ کارساز، سیستم‌عامل، مدت کارکرد، نسخهٔ PHP؛ امتیاز امنیت؛ وضعیت ماژول‌ها (UFW، شمار jailهای Fail2ban، مسدودسازی‌های امروز، اجرا بودن CrowdSec و Suricata و Falco، تهدیدهای ClamAV، تغییرات AIDE، شاخص Lynis، به‌روزرسانی‌های در انتظار، نیاز به راه‌اندازی دوباره، روزهای باقی‌مانده تا انقضای گواهی، شمار بندرهای باز خطرناک)؛ آخرین اندازه‌گیری بار (load، CPU، RAM، دیسک، inodes)؛ شمارنده‌های رویداد ۲۴ ساعت گذشته و آخرین رویداد بحرانی. در خلاصه نه گزارشی هست، نه گذرواژه‌ای و نه محتوای فایلی.

فرستنده کارساز است و حساب کاربری چیزی نمی‌پرسد. از این رو لازم نیست هیچ ورودی‌ای از بیرون به شبکهٔ شما باز شود، و پنلی که پشت پالایهٔ IP بسته است هم گزارش می‌دهد. گره از روی دامنهٔ پنل شناخته می‌شود، پس فهرست پس از راه‌اندازی دوباره یا تغییر IP دوتایی نمی‌شود.
اگر کارسازی در حساب کاربری «خاموش» نشان داده شود، یعنی خلاصه مدتی است نرسیده. به ترتیب بررسی کنید: کلید «فرستادن خلاصه»، سطر cron (sudo crontab -l) و پاسخ دکمهٔ «همین حالا بفرست». خطاهای فرستادن با نشان FLEET در گزارش برنامه نوشته می‌شوند — همان‌جا پیداست که توکن رد شده (HTTP 401) یا اصلاً ارتباط برقرار نشده است.

ابزارهای امنیتی

11. فایروال UFW

UFW (Uncomplicated Firewall) رابطی ساده برای nftables/iptables است. همه پورت‌های ورودی را جز مواردی که صراحتاً مجاز شده‌اند می‌بندد. صفحه «فایروال UFW» وضعیت و قوانین را نشان می‌دهد.

sudo apt install ufw # مجاز کردن SSH (حتماً پیش از فعال‌سازی!) و وب sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # بستن دسترسی خارجی به پایگاه‌داده (فقط دسترسی محلی) sudo ufw deny 3306 # فعال‌سازی و بررسی sudo ufw enable sudo ufw status verbose
پیش از ufw enable حتماً SSH را مجاز کنید (ufw allow OpenSSH)، وگرنه دسترسی به سرور را از دست می‌دهید.
«مواجهه خارجی» در داشبورد UFW را در نظر می‌گیرد: پورتی که با قانون deny بسته شده، از بیرون قابل‌دسترس محسوب نمی‌شود.
Skipping adding existing rule خطا نیست. UFW با این پیام اعلام می‌کند که دقیقاً همین قانون از قبل وجود دارد و آن را دوباره اضافه نمی‌کند. هنگام اجرای مجدد پیکربندی خودکار (که خودتکرارپذیر است) این پیام عادی است — نیازی به واکنش نیست.

12. نصب Fail2ban

پس از عبور از تعداد تلاش‌های ناموفق ورود، به‌طور خودکار IP را مسدود می‌کند. لاگ‌های SSH، nginx، Apache و دیگر سرویس‌ها را تحلیل می‌کند.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # بررسی وضعیت: sudo fail2ban-client status
پیکربندی آماده (jail.local با ده‌ها jail و مسدودسازی خودکار از ipsum) — در بخش بعدی.

13. پیکربندی عملی Fail2ban + ipsum

نصب پایه بالاتر آمده است. اینجا پیکربندی عملی ارائه می‌شود که ده‌ها jail فعال و هزاران بلاک ایجاد می‌کند: تنظیمات عمومی، jailهای کلیدی و بن خودکار IPهای مخرب از فهرست ipsum.

فایل /etc/fail2ban/jail.local — تنظیمات عمومی و مهم‌ترین jailها:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # بن تصاعدی: هر تکرار — طولانی‌تر bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # بن دائمی برای بروت‌فورس SSH findtime = 3600 # مکررها: هر که چند بار بن خورده — برای همیشه بن می‌شود [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # سرویس‌های وب (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … و بقیه jailها بر اساس سرویس (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
در ignoreip حتماً IP خود و شبکه‌های مورد اعتماد را وارد کنید، وگرنه ممکن است خودتان را بن کنید. پس از ویرایش: sudo fail2ban-client reload.

بارگذاری خودکار بلاک‌لیست ipsum — در کرون root (sudo crontab -e): level 1 (بیش از ۱۰۰ هزار IP) در ست ipsum بارگذاری می‌شود که روی فایروال مسدود می‌گردد (جزئیات بیشتر — در بخش «بلاک‌لیست IPset»):

# 04:00 — به‌روزرسانی ipset ipsum (level 1، حداکثر پوشش): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
نام ست باید ipsum باشد — دقیقاً همین را داشبورد می‌خواند (کارت «IPset ipsum»). سطوح: levels/1.txt — حداکثر پوشش، levels/3.txt — دقیق‌تر (۳ منبع یا بیشتر).

چرا «مانیتور امنیت» به دو ناحیه تقسیم شده است. حفاظت در دو سطح کار می‌کند و داشبورد آن‌ها را با هم مخلوط نمی‌کند:

  • حملات واقعی (واکنشی) — هرچه fail2ban گرفته است: تلاش‌های زندهٔ نفوذ (jailهای sshd، apache-*، nginx-* و غیره) و مکررهای بدخیم (jail recidive — کسانی که قبلاً چند بار بن شده‌اند). این‌ها IPهایی هستند که واقعاً به شما نفوذ کرده‌اند — روی نقشهٔ حملات و «خط زمانی» دیده می‌شوند.
  • بلاک پیشگیرانه (فعال) — بلاک‌لیست عمومی IPهای مخرب شناخته‌شده ipset ipsum که روی فایروال با قانون DROP مسدود می‌شود. این آدرس‌ها در بیشتر موارد اصلاً به سرور شما نزدیک نشده‌اند — از پیش مسدود می‌شوند؛ شمارندهٔ «IPset ipsum» نشان می‌دهد چند مورد پیشگیرانه مسدود شده است.

تفاوت ساده است: واکنشی — «این‌ها حمله کردند و بن شدند»، پیشگیرانه — «این‌ها پیش از تلاش مسدود شدند». پیش‌تر در recidive به‌صورت مصنوعی list-3 ipsum بارگذاری می‌شد (از همین‌جا تقسیم قدیمی «recidiveهای فهرستی» آمده بود)؛ اکنون recidive فقط مکررهای واقعی است و پیشگیری کاملاً روی فایروال انجام می‌شود.

14. فهرست انسداد IPset (ipsum)

ipsum — فهرست عمومی IPهای مخرب که روزانه به‌روزرسانی می‌شود. Monitor تعداد آدرس‌های بارگذاری‌شده را روی داشبورد و نقشهٔ حملات نشان می‌دهد و آن را در امتیاز امنیتی لحاظ می‌کند (−۱۰ اگر مجموعه بارگذاری نشده باشد).

ساده‌ترین گزینه بدون fail2ban — یک مجموعهٔ جداگانهٔ ipsum با انسداد از طریق iptables:

# ساخت مجموعه (یک‌بار): sudo ipset create ipsum hash:ip # اسکریپت به‌روزرسانی /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron (sudo crontab -e, روزانه در 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
گزینهٔ پیشرفته با fail2ban-recidive — در بخش «پیکربندی کاری Fail2ban + ipsum».
ipset در حافظه نگهداری می‌شود و با راه‌اندازی مجدد از بین می‌رود. تنها یک کرون روزانه، مجموعه را از لحظهٔ ریبوت تا اجرای بعدی خالی می‌گذارد (داشبورد ۰ نشان می‌دهد). مجموعه را هنگام راه‌اندازی هم بارگذاری کنید — بارگذاری را در یک اسکریپت قرار دهید و به @reboot ببندید. ضمناً فرمان create … -exist حد maxelem 300000 را تعیین می‌کند (پیش‌فرض ۶۵۵۳۶ است — level 1 جا نمی‌شود و خطای «Hash is full» می‌دهد):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — روزانه در 04:00 و هنگام هر راه‌اندازی: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
نحوهٔ کار در نصب خودکار. اسکریپت فهرست کامل level 1 (بیش از ۱۰۰ هزار IP) را در مجموعهٔ ipsum بارگذاری می‌کند و اگر مدیریت فایروال با نصب‌کننده باشد (VPS تازه — پروفایل‌های «کامل»/«سبک»)، مجموعه را با قاعدهٔ DROP به UFW متصل می‌کند — ترافیک این IPها واقعاً مسدود می‌شود. این قاعده بعد از ESTABLISHED,RELATED قرار دارد، پس اتصالات فعلی (از جمله SSH شما) قطع نمی‌شوند — فقط اتصالات جدید از فهرست بریده می‌شوند. مجموعه هنگام بارگذاری سرویس ipsum-load.service پیش از فایروال بازیابی می‌شود (وگرنه UFW بالا نمی‌آمد) و با کرون در 04:00 به‌روزرسانی می‌شود. روی سروری که از پیش پیکربندی شده (پنل، فایروال اختصاصی) نصب‌کننده به فایروال دست نمی‌زند — آنجا ipsum فقط فهرستی برای داشبورد و نقشهٔ حملات می‌ماند و قاعدهٔ DROP در صورت تمایل به‌صورت دستی افزوده می‌شود (گزینهٔ ساده با iptables … --match-set ipsum … -j DROP — بالاتر). در نصب خودکار نیازی به انجام دستی کاری نیست.

15. نصب CrowdSec

جایگزین امروزی Fail2ban با هوش تهدید جمعی: بلاک‌های جامعه به‌علاوه‌ی قواعد اختصاصی خودتان. برای اعمال بلاک‌ها روی فایروال به یک bouncer جداگانه نیاز دارد.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer برای iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # بررسی وضعیت: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
وضعیت «اجرا نشده» در پنل = سرویس نصب شده، اما فعال نیست (مانیتور آن را از طریق systemctl is-active crowdsec بررسی می‌کند). راه‌اندازی: sudo systemctl enable --now crowdsec؛ در صورت خرابی به sudo journalctl -u crowdsec -n 30 نگاه کنید. همین قاعده برای هر سرویسی در وضعیت «اجرا نشده» صادق است (Suricata، Falco، Monit، MySQL).
«۰ سناریو» یا «۰ bouncer» در داشبورد. CrowdSec به‌صورت پیش‌فرض تقریباً خالی می‌آید — بدون کالکشن‌ها چیزی را تشخیص نمی‌دهد و بدون bouncer ثبت‌شده هم بلاک‌ها روی فایروال اعمال نمی‌شوند. کالکشن‌های پایه را نصب کنید و مطمئن شوید bouncer در فهرست هست:
# کالکشن‌های پایه (Linux + SSH + وب‌سرور): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer باید در فهرست و با وضعیت اتصال فعال باشد: sudo cscli bouncers list
در لاگ bouncer عبارت stream halted / بلاک‌ها اعمال نمی‌شوند. این یک کلید api یتیم است: bouncer از cscli bouncers list حذف شده، اما کلید قدیمی‌اش در /etc/crowdsec/bouncers/*.yaml باقی مانده است. bouncer را دوباره ثبت کنید و کلید تازه را وارد کنید:
sudo cscli bouncers add fw-bouncer # کلید api_key جدید را نمایش می‌دهد # این کلید را در api_key: در /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml وارد کنید sudo systemctl restart crowdsec-firewall-bouncer
نصب خودکار (پروفایل «حفاظت کامل») خودش کالکشن‌ها را نصب و firewall-bouncer را ثبت می‌کند — انجام دستی آن فقط هنگام نصب دستی یا پس از دخالت دستی در CrowdSec لازم است.

16. نصب AIDE

AIDE (Advanced Intrusion Detection Environment) از سیستم فایل یک عکس فوری می‌گیرد و در هر بررسی تغییرات /etc، /bin، /usr را گزارش می‌دهد. پس از نصب، مقداردهی اولیهٔ پایگاه‌داده (aideinit) الزامی است.

sudo apt install aide # مقداردهی اولیهٔ پایگاه‌داده (۵ تا ۱۵ دقیقه): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: پوشهٔ /var/lib/aide با حالت 700 ساخته می‌شود (مالک _aide)، # و داشبورد (www-data) پایگاه‌داده را نمی‌بیند ← «مقداردهی نشده» نشان می‌دهد. # باز کردن پوشه برای عبور (فایل‌های پایگاه‌داده روی 600 می‌مانند): sudo chmod 755 /var/lib/aide # اولین بررسی با نوشتن در لاگی که مانیتور می‌خواند. # روی Ubuntu/Debian دستور aide به config صریح نیاز دارد (وگرنه «missing configuration»؛ # باینری aide.wrapper در نسخه‌های جدید ارائه نمی‌شود): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
در حین aideinit ترمینال ۵ تا ۱۵ دقیقه روی خط Running aide --init... می‌ماند — این طبیعی است (هش کردن کل سیستم فایل، بار روی دیسک). با Ctrl+C آن را قطع نکنید. اگر فرایند «معلق» است اما چیزی نمی‌نویسد — شاید منتظر پاسخ به یک پرسش پنهان Overwrite existing aide.db.new [Yn]? است (کلید Y را بزنید). بررسی فعالیت از نشست دیگر: pgrep -af aide.
خطای aideinit: «21_aide_spamassassin … printf: invalid number» (return code 20) — یک باگ شناخته‌شدهٔ قطعهٔ پیکربندی AIDE در Ubuntu 22.04. پایگاه‌داده ساخته نمی‌شود. قطعهٔ خراب را جدا کنید و دوباره تلاش کنید:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
وضعیت‌ها در داشبورد. «مقداردهی نشده» = پنل فایل پایگاه‌داده را نمی‌بیند: یا aideinit اجرا نشده، یا (Ubuntu 24.04) پوشهٔ /var/lib/aide با حالت 700 ساخته شده و برای www-data دسترس‌پذیر نیست — با sudo chmod 755 /var/lib/aide برطرف می‌شود (بلوک بالا را ببینید). «بررسی‌ای انجام نشده» = پایگاه‌داده هست اما هنوز بررسی اجرا نشده — این خطا نیست. مانیتور نتایج را از /var/log/aide/aide.log می‌خواند.
بررسی منظم ← لاگ برای پنل. فایل استاندارد /etc/cron.daily/aide در Ubuntu/Debian جدید ممکن است /var/log/aide/aide.log را به شکل لازم ننویسد (و aide.wrapper نیز دیگر در آن‌ها نیست). مطمئن‌تر است که کرون خودتان را با --config صریح اضافه کنید — لاگ را از root با حالت 644 می‌نویسد و مانیتور بدون گروه‌های اضافی آن را می‌خواند:
# sudo crontab -e — بررسی روزانه در ساعت 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # اجرای فوری، بدون انتظار برای زمان‌بندی: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
نصب خودکار همهٔ این کارها را انجام می‌دهد: chmod 755 /var/lib/aide و کرون بررسی در ساعت 02:00 — نیازی به کار دستی نیست.
مقداردهی اولیه را روی سرور تمیز انجام دهید — پیش از نصب برنامه‌های وب. پس از تغییرات مجاز، پایگاه‌داده را بازسازی کنید: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

17. نصب ClamAV

اسکنر آنتی‌ویروس برای Linux. به‌ویژه برای بررسی /var/www از نظر شل‌های PHP و کد مخرب مفید است.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # به‌روزرسانی پایگاه امضاها: sudo freshclam # اسکن دستی یک پوشه: sudo clamscan -r /var/www --infected
دیمِن clamd بعد از enable --now «غیرفعال» نشان می‌دهد؟ سه علت رایج:

1. خط Example در فایل پیکربندی باقی مانده است — تا وقتی این خط باشد، clamd از راه‌اندازی خودداری می‌کند:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. پایگاه امضاها دانلود نشده است — clamd بدون آن راه‌اندازی نمی‌شود:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. فقط در حال بارگذاری است — clamd حدود ۸ میلیون امضا را در ۳۰ تا ۶۰ ثانیه در حافظه بارگذاری می‌کند. صبر کنید و بررسی کنید: systemctl is-active clamav-daemon (وضعیت activating ← هنوز در حال بارگذاری است).

عیب‌یابی: sudo journalctl -u clamav-daemon -n 30 --no-pager.
روی داشبورد «فایل‌های بررسی‌شده: 0» / «آخرین اسکن: —» می‌بینید؟ دیمِن clamd فقط امضاها را در حافظه نگه می‌دارد و خودش به‌صورت زمان‌بندی‌شده چیزی اسکن نمی‌کند. پنل نتایج اسکن زمان‌بندی‌شده را نشان می‌دهد، پس به یک cron نیاز است که اسکن کند و لاگ بنویسد. نصب خودکار یک پوشش /usr/local/bin/clamav-scan.sh و یک cron در ساعت 01:30 قرار می‌دهد — پس از اولین اجرا، «فایل‌های بررسی‌شده» و «آخرین اسکن» پر می‌شوند. برای اجرای فوری بدون انتظار برای زمان‌بندی: sudo /usr/local/bin/clamav-scan.sh.

18. نصب Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — اسکنر بدافزار برای تهدیدهای وب: شل‌های PHP، بک‌دورهای وب، دانلودرها. از موتور ClamAV استفاده می‌کند و آن را با امضاهای اختصاصی خود تکمیل می‌کند.

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # به‌روزرسانی امضاها: sudo maldet -u # اسکن /var/www: sudo maldet -a /var/www
LMD و ClamAV در کنار هم خوب کار می‌کنند. آخرین گزارش: maldet --report.
هنگام نصب ممکن است خط update-rc.d: error: unable to read /etc/init.d/maldet ظاهر شود — این بی‌خطر است. maldet از init.d استفاده نمی‌کند؛ به‌روزرسانی امضاها و اسکن‌ها از طریق /etc/cron.daily/maldet اجرا می‌شوند. اگر پایین‌تر installation completed دیده شود — همه‌چیز نصب شده است.
در صفحه LMD «نصب‌نشده» نمایش داده می‌شود اما نصب است؟ maldet از طریق apt نصب نمی‌شود، بلکه در /usr/local/maldetect قرار می‌گیرد و با فعال بودن open_basedir وجود آن از طریق shell بررسی می‌شود — به بخش «صفحه خالی است، هرچند داده‌ها روی سرور موجودند» مراجعه کنید.

19. نصب Suricata

سیستم تشخیص نفوذ شبکه: ترافیک را در سطح بسته‌ها تحلیل می‌کند و هزاران امضای حمله را می‌شناسد. مکمل ModSecurity است (آن در سطح HTTP کار می‌کند و Suricata در سطح TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # دانلود قوانین به‌روز: sudo suricata-update sudo systemctl enable --now suricata
Suricata «فعال» است، اما داشبورد هشداری نشان نمی‌دهد / تعداد رویدادها ۰ است؟ Suricata فایل /var/log/suricata/eve.json را با کاربر root و حالت ۷۵۰ روی دایرکتوری می‌نویسد و وب‌سرور (www-data) نمی‌تواند آن را بخواند. دایرکتوری را برای عبور باز کنید — فایل‌های داخل آن محافظت‌شده باقی می‌مانند:
sudo chmod o+rx /var/log/suricata
نصب خودکار این کار را خودش انجام می‌دهد — به‌صورت دستی نیازی نیست.

20. نصب Falco

فراخوانی‌های سیستمی را از طریق eBPF/kernel module رهگیری می‌کند و ناهنجاری‌ها را در لحظه تشخیص می‌دهد: اجرای shell از nginx، خواندن /etc/passwd توسط پردازهٔ وب، نوشتن در /bin و غیره.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
مانیتور رویدادهای Falco را از طریق journalctl -u falco می‌خواند (بدون sudo — از طریق گروه systemd-journal). مطمئن شوید که www-data در این گروه است — به «پیکربندی sudo» (بند ۲) در صفحهٔ نصب دستی مراجعه کنید.
«۰ رویداد در ۲۴ ساعت» عادی است، نه خطا. Falco رویدادمحور است: تا وقتی همه‌چیز درست باشد ساکت می‌ماند و فقط هنگام ناهنجاری رویداد ثبت می‌کند (اجرای shell از پردازهٔ وب، خواندن /etc/passwd، نوشتن در دایرکتوری‌های سیستمی). صفر رویداد بحرانی در یک شبانه‌روز روی یک سرور آرام یعنی وضعیت سالم.
برای داشبورد، خروجی فایلی مطمئن‌تر است. خواندن از طریق journalctl نیازمند دسترسی به ژورنال است؛ برای اینکه داشبورد رویدادها را پایدار ببیند، نصب خودکار در Falco گزینهٔ file_output → /var/log/falco/falco.log را فعال می‌کند و برای سرویس UMask=0022 تنظیم می‌کند (لاگ توسط وب‌سرور خوانده می‌شود). در نصب تازه نیازی به تنظیم دستی این مورد نیست.

21. نصب ModSecurity (WAF)

ModSecurity — یک فایروال وب (WAF) برای Apache یا Nginx. حملات در سطح برنامه را مسدود می‌کند: تزریق SQL، XSS، پیمایش مسیر و اسکنرها.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # مجموعه قوانین OWASP Core Rule Set: sudo apt install modsecurity-crs # الزامی: بدون این فایل موتور قوانین خاموش است sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # بررسی: باید ۴۰۳ برگرداند curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
نصب بسته به‌تنهایی از چیزی محافظت نمی‌کند. Apache کانفیگ‌ها را با خط IncludeOptional /etc/modsecurity/*.conf بارگذاری می‌کند، اما بسته فقط modsecurity.conf-recommended را می‌گذارد که با الگوی *.conf مطابقت ندارد. اگر آن را در modsecurity.conf کپی نکنید، SecRuleEngine روی Off می‌ماند: ماژول بارگذاری شده، قوانین CRS بارگذاری شده، اما ترافیک بررسی نمی‌شود و لاگ ممیزی ساخته نمی‌شود. حالت میانی DetectionOnly فقط رویدادها را در لاگ می‌نویسد بدون مسدودکردن درخواست‌ها — داشبورد آن را زرد نشان می‌دهد.

دسترسی داشبورد به لاگ ممیزی. لاگ /var/log/apache2/modsec_audit.log متعلق به root است (مجوز 640) و کاربر وب آن را نمی‌خواند. داشبورد داده‌ها را از طریق یک پوشش می‌گیرد — آن را بسازید:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # در /etc/sudoers.d/monitor (کاربر = همان کاربری که PHP-FPM با آن کار می‌کند): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
پوشش آخرین دستور SecRuleEngine بدون تورفتگی را می‌گیرد: خطوط دارای تورفتگی داخل بلوک‌های <LocationMatch>/<Directory> هستند (مثلاً غیرفعال‌کردن WAF برای phpMyAdmin) و حالت سراسری را تعیین نمی‌کنند.
کاربر در sudoers باید با کاربر استخر FPM یکی باشد: در Apache/Debian معمولی این www-data است، در HestiaCP استخر سایت با مالک سایت کار می‌کند (مثلاً admin) — با grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf بررسی کنید.
اگر سایت پشت پراکسی Nginx باشد (HestiaCP)، Apache خودِ پراکسی را به‌عنوان کلاینت می‌بیند — داشبورد IP واقعی مهاجم را از هدر X-Forwarded-For می‌گیرد. تنها تراکنش‌هایی که قانونی روی آن‌ها فعال شده وارد آمار می‌شوند: دستور SecAuditLogRelevantStatus هر پاسخ 4xx/5xx را در لاگ ممیزی می‌نویسد، بنابراین 403/500‌های معمولی هم آنجا می‌آیند — داشبورد آن‌ها را رویداد WAF به‌حساب نمی‌آورد.
بلوک ---RULES--- برای بخش «همه قوانین فعال» لازم است — داشبورد نه‌فقط قوانین فعال‌شده، بلکه همه قوانین بارگذاری‌شده CRS + سفارشی را نشان می‌دهد. سه مسیر در حلقه for f in … جاهای معمول قوانین CRS و افزوده‌های محلی هستند؛ اگر چیدمان شما متفاوت است (بسته فایل‌ها را در دایرکتوری خودش می‌گذارد، یا قوانین سفارشی در /etc/modsecurity/custom-rules.conf نیستند)، مسیرهای واقعی را با فرمان sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null پیدا کرده و در فهرست جایگزین کنید. اگر پوشش قدیمی است (بدون این بخش) — بخش فقط هشدار «در دسترس نیست» را نشان می‌دهد و بقیه صفحه مثل قبل کار می‌کند.

22. نصب Auditd

Auditd (Linux Audit Daemon) فراخوانی‌های سیستمی را در سطح هسته ثبت می‌کند: ورودها و خروج‌ها، دستورهای sudo، تلاش‌های ناموفق احراز هویت و تغییر فایل‌ها. مانیتور ورودها، تلاش‌های ناموفق و دستورهای sudo امروز را نمایش می‌دهد.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # بررسی وضعیت و رویدادها: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
مانیتور رویدادها را از طریق ausearch (/usr/sbin/ausearch) و در صورت نیاز از /var/log/audit/audit.log با دستور tail می‌خواند. هر دو باید در sudoers باشند.

23. نصب Monit

سرویس‌ها (nginx، php-fpm، mysql و غیره) را زیر نظر می‌گیرد و در صورت از کار افتادن دوباره راه‌اندازی می‌کند. می‌تواند هشدارها را به ایمیل ارسال کند.

sudo apt install monit sudo systemctl enable --now monit # فایل‌های پیکربندی: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
مانیتور فهرست سرویس‌ها را از طریق monit status دریافت می‌کند. در /etc/monit/monitrc باید رابط HTTP فعال باشد (بلوک set httpd با allow localhost)، وگرنه monit status خطا برمی‌گرداند.
روی داشبورد «۰ سرویس زیر نظر»؟ دو دلیل دارد. (۱) رابط HTTP خاموش است — در monitrc خط set httpd کامنت شده (به‌طور پیش‌فرض به‌صورت # set httpd port 2812 … است). بلوک را از کامنت خارج کنید و به localhost اجازه دهید. (۲) httpd فعال به‌تنهایی چیزی را زیر نظر نمی‌گیرد — Monit فقط چیزهایی را می‌شمارد که با استانزای check تعریف شده‌اند؛ بدون آن‌ها فهرست حتی با رابط فعال خالی است. حداقل پیکربندی کارآمد:
# /etc/monit/conf.d/00-httpd — رابط HTTP برای localhost: set httpd port 2812 use address localhost allow localhost # نمونه‌های استانزای check (چه چیزی زیر نظر باشد): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # بررسی نحو (Control file syntax OK) sudo systemctl reload monit sudo monit status
نصب خودکار یک conf.d آماده با httpd روی ۲۸۱۲ و مجموعه‌ای از بررسی‌ها قرار می‌دهد — در نصب جدید نیازی به پیکربندی دستی نیست.
سرویس در وضعیت «با خطا»؟ مانیتور فقط وضعیت را نمایش می‌دهد و عمداً سرویس‌ها را از پنل وب دوباره راه‌اندازی نمی‌کند (این کار به‌معنای اجرای از راه دور دستورهای root در یک پنل امنیتی می‌بود). عیب‌یابی و راه‌اندازی مجدد — از طریق SSH با Monit:
sudo monit status <service> # علت خطا sudo monit restart <service> # راه‌اندازی مجدد از طریق Monit # اگر Monit سرویس را بالا نیاورد — یونیت خودش را ببینید: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. نصب PSAD (تشخیص اسکن پورت)

PSAD گزارش iptables را تحلیل می‌کند و اسکن پورت و حملات شبکه‌ای را شناسایی می‌کند و به هر منبع سطح تهدید (۱ تا ۵) می‌دهد. مکمل fail2ban و Suricata است.

sudo apt install psad # PSAD گزارش iptables را می‌خواند — باید ثبت گزارش فعال باشد (UFW خودش این کار را می‌کند). # برای iptables خالص، قواعد LOG را به زنجیره‌های INPUT/FORWARD اضافه کنید. sudo psad --sig-update sudo systemctl enable --now psad
Monitor داده‌ها را از طریق psad --Status می‌خواند (نیاز به sudoers دارد). بدون ثبت گزارش iptables صفحه خالی خواهد بود — این طبیعی است تا زمانی که اسکنی انجام نشده باشد.

25. AppArmor / SELinux (کنترل دسترسی)

Mandatory Access Control محدود می‌کند که یک برنامه به چه فایل‌ها و منابعی می‌تواند دسترسی داشته باشد، حتی اگر هک شده باشد. در Ubuntu/Debian به‌طور پیش‌فرض از AppArmor استفاده می‌شود (معمولاً از پیش نصب و فعال است).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # بررسی پروفایل‌ها
مانیتور وضعیت را از طریق aa-status می‌خواند (باید در sudoers باشد). تعداد پروفایل‌های در حالت enforce/complain و فرایندهای بدون پروفایل را نمایش می‌دهد.

«پروفایل‌های بارگذاری‌شده» بیشتر از enforce + complain — این عادی است. در AppArmor 4.x (Ubuntu 24.04 و جدیدتر) حالت unconfined اضافه شده است: پروفایل در هسته بارگذاری می‌شود اما چیزی را محدود نمی‌کند. Ubuntu ده‌ها پروفایل را برای برنامه‌هایی که از user namespaces استفاده می‌کنند (مرورگرها، کلاینت‌های torrent و مانند آن) به این شکل علامت‌گذاری می‌کند. وقتی چنین پروفایل‌هایی وجود دارند، کارت «پروفایل‌های بارگذاری‌شده» کهربایی می‌شود و تعدادشان را نمایش می‌دهد — برای مثال unconfined: 90 با ۱۲۰ پروفایل بارگذاری‌شده و ۲۶ در enforce. تنها پروفایل‌های در enforce واقعاً محافظت می‌کنند؛ در Ubuntu 22.04 (AppArmor 3.x) این حالت وجود ندارد و اعداد همیشه با هم می‌خوانند.

sudo aa-status | grep -E "profiles are" # تفکیک بر اساس حالت sudo aa-enforce /etc/apparmor.d/profile-name # انتقال پروفایل به enforce
انتقال پروفایل‌هایی که Ubuntu عمداً در unconfined گذاشته به enforce فقط باید آگاهانه انجام شود: آن‌ها به‌اشتباه غیرفعال نشده‌اند، بلکه چون در غیر این صورت کارکرد خودِ برنامه‌ها مختل می‌شود. پروفایل‌های در complain موضوع دیگری هستند: در آنجا قوانین از پیش نوشته شده‌اند و فقط اعمال نمی‌شوند.

26. نصب debsums (یکپارچگی بسته‌ها)

debsums بررسی می‌کند که فایل‌های بسته‌های نصب‌شده با چک‌سام‌های مخزن مطابقت دارند — باینری‌های سیستمی دستکاری‌شده را شناسایی می‌کند (مکمل AIDE). بررسی کامل ۱ تا ۲ دقیقه طول می‌کشد، بنابراین با cron اجرا می‌شود و پنل نتیجه را از data/debsums/debsums.log می‌خواند و خودش آن را دسته‌بندی می‌کند (فقط باینری‌ها و کتابخانه‌ها مهم‌اند).

وظیفه در cron کاربر root قرار می‌گیرد (sudo crontab -e). پوشش آماده debsums-scan.sh در /usr/local/bin/ قرار می‌گیرد (chmod +x؛ به خلاصه وظایف cron مراجعه کنید) و خودش گزارش را در data/debsums/ پنل می‌نویسد.

sudo apt install debsums # خط cron (روزانه ۴:۳۰): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

پوشش debsums-scan.sh خودش data/ پنل را پیدا می‌کند — نیازی به نوشتن مسیر نیست.

تغییرات در /etc/ (فایل‌های پیکربندی) و /usr/share/ (منابع) روی سرور معمولاً عادی است — پنل آن‌ها را با رنگ جداگانه مشخص می‌کند. تغییر باینری‌ها و کتابخانه‌ها (/bin، /sbin، /usr/lib و غیره) هشداردهنده است — کارت «باینری‌ها / کتابخانه‌ها» دقیقاً همین‌ها را نشان می‌دهد.

27. پیکربندی گزارش‌های Lynis

Lynis به‌صورت دستی یا با cron اجرا می‌شود. گزارش باید در پوشهٔ data/lynis/ پروژه ذخیره شود — مانیتور فایل lynis-report.dat را می‌خواند.

# اجرای یک‌باره (مسیر ریشهٔ پنل خود را جایگزین کنید): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # ممیزی روزانه — خط cron (پوشش آمادهٔ lynis-scan.sh در /usr/local/bin/، به خلاصه مراجعه کنید): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
پس از نخستین اجرا، صفحهٔ «ممیزی Lynis» بلافاصله hardening index، هشدارها و توصیه‌ها را نشان می‌دهد.
دکمهٔ «اجرای ممیزی» در صفحهٔ Lynis. این دکمه lynis-scan.sh را مستقیم از پنل و در پس‌زمینه اجرا می‌کند (بدون انتظار برای cron): «در حال اسکن…» را نشان می‌دهد و پس از پایان، خودش گزارش را به‌روزرسانی می‌کند. برای این کار، کاربر وب به یک خط sudoers برای اجرای اسکریپت نیاز دارد — نصب‌کننده آن را به‌صورت خودکار به /etc/sudoers.d/monitor اضافه می‌کند. اگر پنل به‌صورت دستی/پیش‌تر نصب شده، آن را با همان کاربری که در فایل مشخص شده اضافه کنید:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

28. پیکربندی گزارش‌های Logwatch

Logwatch باید گزارش‌های روزانه را در پوشهٔ data/logwatch/ پروژه با قالب .txt ذخیره کند. Monitor آخرین گزارش و بایگانی را نمایش می‌دهد.

# روزانه (۶:۰۰) — خط cron (پوشش آمادهٔ logwatch_daily.sh در /usr/local/bin/، به خلاصه مراجعه کنید): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

ماژول‌های پنل

29. مانیتور شبکه (توکار)

مانیتور شبکه به نصب نیاز ندارد — این یک صفحهٔ توکار در پنل است. وضعیت شبکهٔ سرور را از منابع محلی نمایش می‌دهد:

  • رابط‌ها و ترافیک — از /proc/net/dev؛
  • وضعیت لینک‌ها (UP/DOWN) و IP — از طریق ip؛
  • اتصال‌ها و پورت‌های شنونده — از طریق ss؛
  • رویدادهای شبکه‌ای هسته در ۲۴ ساعت — از طریق journalctl -k.

سه منبع نخست بدون sudo کار می‌کنند، بنابراین رابط‌ها، ترافیک، اتصال‌ها و پورت‌ها بی‌درنگ دیده می‌شوند. بخش «رویدادهای هسته» از journalctl -k استفاده می‌کند — این از طریق گروه systemd-journal خوانده می‌شود («پیکربندی sudo»، بند ۲) و به sudo نیاز نیست. برای بررسی اینکه همه‌چیز برای کاربر وب در دسترس است:

# بررسی از طرف www-data (PHP زیر آن اجرا می‌شود): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
بخش «رویدادهای شبکه‌ای هسته» رویدادهای پشتهٔ شبکهٔ هسته را نمایش می‌دهد (تغییر لینک up/down، خطاهای حامل، «network unreachable»). رکوردهای فایروال UFW BLOCK اینجا نمی‌آیند — آن‌ها در صفحه‌های «فایروال UFW» و «نقشهٔ حملات» هستند. بخش خالی با تیک سبز = در ۲۴ ساعت هیچ اختلال شبکه‌ای رخ نداده است.

30. دیسک و SMART

صفحه‌ی داخلی سه چیز را نشان می‌دهد:

  • سیستم‌های فایل — پرشدن پارتیشن‌ها (df)؛ نوار در ≥۹۰٪ قرمز می‌شود؛
  • دستگاه‌های ذخیره‌سازی — فهرست دیسک‌ها (lsblk)، فقط واقعی‌ها (loop/snap پنهان‌اند)؛
  • سلامت (SMART) — وضعیت دیسک و صفت‌ها (smartctl).

فضا و فهرست دستگاه‌ها بدون تنظیمات بلافاصله کار می‌کنند. برای SMART به بسته‌ی smartmontools نیاز است. فرایند وب دسترسی مستقیم به دستگاه‌های دیسک ندارد، بنابراین SMART با cron در فایل data/disk/smart.txt گرفته می‌شود و پنل آن را می‌خواند.

وظیفه در cron کاربر root قرار می‌گیرد (sudo crontab -e). پوشش آماده‌ی smart-scan.sh در /usr/local/bin/ گذاشته می‌شود (chmod +x؛ به خلاصه‌ی وظایف cron نگاه کنید) و خودش در data/disk/ پنل می‌نویسد.

sudo apt install smartmontools # خط Cron (هر ۳۰ دقیقه): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

پوشش smart-scan.sh خودش data/ پنل را پیدا می‌کند — لازم نیست مسیر را بنویسید. در داخل، lsblk -e7,11 مورد loop/cdrom را حذف می‌کند.

روی دیسک‌های مجازی (QEMU/KVM و مشابه) معمولاً فقط وضعیت کلی «سلامت: OK» در دسترس است و دما، ساعت‌های کارکرد و سکتورهای بازتخصیص‌یافته ممکن است خالی باشند — این طبیعی است. روی سرور فیزیکی همه‌ی صفت‌ها نمایش داده می‌شوند.

31. کارایی (CPU/RAM/شبکه/دیسک)

این صفحه تاریخچه بار سرور در ۲۴ ساعت گذشته را نشان می‌دهد — Load Average، اشغال CPU و انتظار I/O، RAM/Swap، ترافیک شبکه (دریافت/ارسال)، I/O دیسک (خواندن/نوشتن)، پرشدگی دیسک و inodeها، توصیف‌گرهای فایل باز و اتصالات MySQL، به‌علاوه تعداد فعلی اتصالات TCP و فرایندها. بازهٔ پیش‌فرض ۲۴ ساعت است: دکمه‌های بالا آن را به ۷ روز یا ۳۰ روز تغییر می‌دهند.

داده‌ها را cron/collect_metrics.php جمع می‌کند — هر ۵ دقیقه یک عکس «خام» از شمارنده‌ها (/proc/loadavg، /proc/meminfo، /proc/stat، /proc/net/dev، /proc/diskstats، df/df -i، /proc/sys/fs/file-nr، SHOW GLOBAL STATUS LIKE 'Threads_connected') در جدول پایگاه‌داده system_metrics می‌نویسد؛ درصدها و سرعت‌ها را خود صفحه از تفاوت میان عکس‌های مجاور محاسبه می‌کند (پرشدگی دیسک/inode/توصیف‌گر/اتصالات MySQL مقادیر لحظه‌ای‌اند، بدون محاسبهٔ مجدد). به sudo نیاز نیست — منابع بدون دسترسی root خوانده می‌شوند.

دو ژرفای نگهداری. نماهای لحظه‌ای «خام» پنج‌دقیقه‌ای یک شبانه‌روز می‌مانند (به‌علاوهٔ یک ساعت ذخیره، تا کهنه‌ترین نقطهٔ نمودار هم نقطهٔ پیشین برای محاسبه داشته باشد). هر ساعت که به پایان می‌رسد در یک سطر تا می‌شود؛ این سری ساعتی ۳۰ روز نگه داشته می‌شود و نماهای «۷ روز» و «۳۰ روز» دقیقاً همین را می‌خوانند. به این ترتیب یک ماه تاریخچه به‌جای حدود ۸۶۰۰ سطر، حدود ۷۲۰ سطر هزینه دارد.

در بازه‌های ۷ و ۳۰ روز میانگین‌های ساعتی نشان داده می‌شود، نه اندازه‌گیری‌های تک‌تک — جهش کوتاه پنج‌دقیقه‌ای در این مقیاس هموار می‌شود و صفحه هم این را یادآوری می‌کند. برای Load Average سطر ساعتی هم میانگین و هم اوج آن ساعت را نگه می‌دارد، از این رو نمودار دو خط نشان می‌دهد. بلافاصله پس از به‌روزرسانی، بازه‌های بلند خالی‌اند («هنوز تاریخچه‌ای برای این بازه نیست») — سری ساعتی از همان لحظه انباشته می‌شود.
# خط Cron (هر ۵ دقیقه): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

پوشش collect-metrics-all.sh (رجوع به خلاصهٔ وظایف cron) خودش همهٔ نمونه‌های نصب‌شدهٔ پنل روی سرور را پیدا می‌کند و cron/collect_metrics.php هر کدام را از نام مالک سایت اجرا می‌کند.

تا زمانی که جمع‌آورنده دست‌کم دوبار اجرا نشده باشد (حدود ۱۰ دقیقهٔ نخست پس از نصب)، صفحه «داده‌ها در حال جمع‌آوری است» را نشان می‌دهد — نمودارها برای محاسبهٔ سرعت‌ها و درصدها دست‌کم به یک جفت نقطهٔ مجاور نیاز دارند.

سنگین‌ترین فرایندها. زیر نمودارها دو جدول هست که هرکدام پنج فرایند پرمصرف‌تر از نظر CPU را نشان می‌دهد:

  • «فرایندها در آخرین اندازه‌گیری» — در لحظهٔ آخرین نمای لحظه‌ای چه چیزی کارساز را مشغول کرده بود (زمانش در سرتیتر کارت نوشته شده است)؛
  • «فرایندها در اوج شبانه‌روز» — همان پنج فرایند، اما برگرفته از اندازه‌گیری‌ای که بالاترین Load Average را در ۲۴ ساعت گذشته داشته است. وقتی جهش روی نمودار مدت‌هاست گذشته، پاسخ پرسش «آن شب چه بود» همین‌جاست.

هر دو جدول همیشه ۲۴ ساعت گذشته را توصیف می‌کنند، حتی وقتی نمودارها یک ماه را نشان می‌دهند: نمای لحظه‌ای فرایندها تنها در سطرهای پنج‌دقیقه‌ای وجود دارد و آن سطرها بیش از یک شبانه‌روز نگهداری نمی‌شوند.

فرایندهای خودِ اندازه‌گیری (ps، اسکریپت PHP گردآورنده و زنجیرهٔ cron/sudo بالای آن) از فهرست کنار گذاشته می‌شوند — وگرنه در هر نما صدرنشین می‌شدند: ps در لحظهٔ اجرا نزدیک به ۱۰۰ ٪ CPU نشان می‌دهد. اما دیگر کارهای cron که در همان دقیقه اجرا شده‌اند نمایش داده می‌شوند و جدول دقیقاً برای همین است.

هشدارهای بار (بخش «تنظیمات» ← «هشدارهای بار») — با گذشتن از آستانهٔ CPU/RAM/دیسک/inodes پنل از همهٔ کانال‌های روشن اعلان می‌فرستد (Telegram، ایمیل، webhook، اعلان فوری — همان کانال‌هایی که گزارش روزانه از آنها می‌رود؛ برای هشدارها لازم نیست چیزی جداگانه روشن شود) و یکی دیگر هم وقتی که مقدار به حالت عادی بازگردد. تا وقتی آستانه رد شده باقی بماند، پیام تکرار نمی‌شود: اعلان بعدی تنها پس از چرخهٔ «به حالت عادی بازگشت ← دوباره از آستانه گذشت» می‌آید.

آستانه‌ها را همان collect_metrics.php در هر اجرا (هر ۵ دقیقه) بررسی می‌کند — به cron جداگانه نیاز نیست. وضعیت «قبلاً اطلاع داده شده / هنوز نه» در data/alerts_state.json ذخیره می‌شود، آستانه‌ها در تنظیمات پنل.

32. رویدادهای امنیتی (حسگری هر ۵ دقیقه)

گزارش روزانه به این پرسش پاسخ می‌دهد که «حال کارساز چطور است». حسگر به پرسشی دیگر پاسخ می‌دهد — «همین حالا چه رخ داد». هر ۵ دقیقه از منابع سریع می‌پرسد و تنها آنچه را که از بررسی پیشین تغییر کرده ثبت می‌کند؛ صفحهٔ «رویدادهای امنیتی» در فهرست، یافته‌ها را نشان می‌دهد.

بررسی‌های کند — Lynis، debsums، به‌روزرسانی بسته‌ها، اعتبار SSL — را حسگر دست نمی‌زند: عددهایشان به‌هرحال روزی یک بار عوض می‌شود و در حوزهٔ گزارش روزانه می‌ماند.

چه چیزهایی را می‌بیند — نُه منبع:

  • Fail2ban — مسدودسازی‌های تازه (فهرست نشانی‌های مسدود مقایسه می‌شود، بنابراین رفع مسدودی رویداد به شمار نمی‌رود و مسدودی برقرار دقیقاً یک بار گزارش می‌شود)؛
  • CrowdSec — تصمیم‌های تازه؛
  • Falco — رویدادهای سطح Emergency، Alert و Critical؛
  • Suricata — هشدارهای تازه؛ نشان [Priority: 1] یافته را به سطح بحرانی می‌برد؛
  • ClamAV — سطرهای تازهٔ FOUND در گزارش پویش؛
  • AIDE — تغییر فایل‌ها بر پایهٔ آخرین بررسی (یعنی نه بیشتر از آنچه خودِ آن بررسی طبق زمان‌بندی‌اش اجرا می‌شود)؛
  • SSH — ورود موفق با root (بحرانی) و ورود از نشانی‌ای که پیش‌تر دیده نشده بود (هشدار)؛
  • سرویس‌های حفاظتی — UFW، Fail2ban، CrowdSec، Suricata، Falco: یک پیام وقتی سرویسی می‌ایستد و یکی وقتی دوباره کار می‌کند؛
  • بندرها — بندری که از بیرون در دسترس شده است؛ اگر پشت آن پایگاه داده یا حافظهٔ نهان باشد، بحرانی است.

ابزاری که نصب نشده یا پنل نمی‌تواند گزارشش را بخواند، تنها خاموش می‌ماند — این نه خطاست و نه رویداد.

نخستین بررسی چیزی نمی‌فرستد. هر منبع نشانی از «تا کجا خوانده شده» نگه می‌دارد (فایل data/secwatch_state.json) و اجرای نخست تنها همین نشان را می‌نویسد. وگرنه به‌محض روشن‌کردن پایش، اعلانی دربارهٔ هر آنچه ماه‌ها در گزارش‌ها انباشته شده به دستتان می‌رسید.

تنظیم — «تنظیمات» ← «پایش رویدادها»:

  • روشن کردن پایش — تا وقتی خاموش است، کار cron بیهوده اجرا می‌شود و فهرست پر نمی‌شود؛
  • اعلان از — «هشدار» یا «بحرانی». یافته‌های پایین‌تر از آستانه باز هم به فهرست می‌روند، اما به اعلان‌ها نه؛
  • مکث، دقیقه — کمترین فاصله میان دو اعلان، به‌طور پیش‌فرض ۱۵؛
  • ساعات سکوت — بازه‌ای به شکل 23-7 که در آن هشدارها فرستاده نمی‌شوند.
یافته‌های بحرانی بی‌درنگ می‌روند — نه مکث نگهشان می‌دارد و نه ساعات سکوت: حسگر پنج‌دقیقه‌ای اصلاً برای همین‌هاست. با این حال فیوزی هم هست: دست‌بالا ۶ اعلان در ساعت — برای آن هنگام که منبعی شروع به «چشمک‌زدن» کند.

اعلان‌ها از همان کانال‌های گزارش روزانه می‌روند (Telegram، ایمیل، webhook، اعلان فوری) — لازم نیست چیزی جداگانه روشن شود. همهٔ یافته‌ها صرف‌نظر از اینکه اعلانی رفته باشد یا نه در فهرست ثبت می‌شوند و ۹۰ روز نگه داشته می‌شوند.

فهرست رویدادها. پالایه‌ها: بازه (۲۴ ساعت، ۷، ۳۰ یا ۹۰ روز)، اهمیت و منبع؛ ۵۰۰ رکورد آخر نمایش داده می‌شود. دکمهٔ «همین حالا بررسی کن» دقیقاً همان بررسی‌ای را اجرا می‌کند که cron اجرا می‌کند — درست پس از تنظیم به کار می‌آید تا پنج دقیقه منتظر نمانید؛ و اگر اعلانی نرفته باشد دلیلش را هم می‌گوید («پایین‌تر از آستانهٔ اهمیت»، «ساعات سکوت»، «هیچ کانال اعلانی تنظیم نشده است»). شمارندهٔ قرمز کنار گزینهٔ فهرست، شمار رویدادهای بحرانی ۲۴ ساعت گذشته است.

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

  • دسترسی محتمل — از این نشانی ورود موفق انجام شده و همان نشانی در سامانه‌های حفاظتی دیگر هم دیده شده است، یا بلافاصله پس از آن AIDE یا ClamAV فعال شده یا درگاه تازه‌ای باز شده است. تنها موردی که ارزش بررسی فوری دارد؛
  • ورود از این نشانی — ورود هست و چیز دیگری نیست. معمولاً مالک از جایی تازه است و به همین دلیل کارت قرمز نمی‌شود؛
  • مسدود شد — نشانی گرفته و مسدود شده است: حفاظت کار خود را کرده و کاری لازم نیست؛
  • نیازمند بررسی — شناسایی هست اما مسدودسازی نبوده است. اگر نشانی باز می‌گردد، آن را دستی ببندید.

زیر گاه‌شمار بخش در همان زمان روی سرور چه گذشت قرار دارد: رویدادهایی که اصلاً نشانی ندارند. AIDE نمی‌داند چه کسی فایل را تغییر داده و ClamAV نمی‌داند چه کسی فایل آلوده را آورده است. آن‌هایی نشان داده می‌شوند که در بازه فعالیت نشانی و شش ساعت پس از آن رخ داده‌اند. همین پیوند ورود با آنچه پس از آن آمده است که تلاش را از دسترسی واقعی جدا می‌کند.

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

زمان‌بندی. این کار در crontab کاربر root گذاشته می‌شود (sudo crontab -e) و ۲ دقیقه نسبت به گردآوری سنجه‌ها جابه‌جا شده تا هر دو هم‌زمان آغاز نشوند:

# هر ۵ دقیقه — بررسی رویدادهای امنیتی 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
بررسی را تنها از راه wrapper اجرا کنید و هرگز مستقیم php cron/security_watch.php را صدا نزنید. wrapper همهٔ نصب‌های پنل روی کارساز را می‌یابد و بررسی را به نام مالک سایت اجرا می‌کند — با همان اختیارات sudo که پنل از وب به کار می‌گیرد. فراخوانی مستقیم از crontab کاربر root، فایل وضعیت data/secwatch_state.json را با مالکیت root می‌سازد و دکمهٔ «همین حالا بررسی کن» در پنل از کار می‌افتد.
اگر کارساز را با نصب خودکار آماده کرده‌اید، این کار از پیش افزوده شده است — تنها روشن‌کردن پایش در تنظیمات می‌ماند.

33. کارساز وب: درخواست‌ها و کدهای پاسخ

صفحهٔ «کارساز وب» گزارش دسترسی را تحلیل می‌کند و نشان می‌دهد چند درخواست بوده، چه بخشی از آنها با خطا پایان یافته و چه کسی آنها را فرستاده است. ModSecurity به پرسش «چه چیزی مسدود شد» پاسخ می‌دهد و این صفحه به پرسش «اصلاً چه چیزی به سایت رسید».

آنچه دیده می‌شود: شمار درخواست‌ها، RPS، سهم خطاها، شمارندهٔ جداگانهٔ 5xx و حجم داده‌های فرستاده‌شده؛ نمودار درخواست‌ها بر پایهٔ ساعت؛ توزیع کدهای 2xx/3xx/4xx/5xx؛ نشانی‌های پرتکرار، IPهای پرتکرار، خطاهای پرتکرار (کد به‌همراه مسیر) و کارخواه‌ها (User-Agent). بازه: ۲۴ ساعت یا ۷ روز.

منبع، گزارش دسترسی در قالب combined است، از این رو هم Nginx و هم Apache مناسب‌اند و به هیچ ماژول افزوده‌ای نیاز نیست. پنل گزارش را در میان فایل‌های استاندارد جست‌وجو می‌کند:

  • /var/log/nginx/access.log
  • /var/log/apache2/access.log
  • /var/log/apache2/other_vhosts_access.log
  • /var/log/httpd/access_log

افزون بر این‌ها، پنل فایل‌های *access*.log و *requests*.log را در /var/log/nginx/، /var/log/apache2/ و /var/log/httpd/ نیز بررسی می‌کند. بدین ترتیب گزارشی هم پیدا می‌شود که سایت با رهنمود CustomLog در فایل خودش می‌نویسد — برای نمونه arciveo-monitor_requests.log که نصب‌کنندهٔ Arcivéo آن را می‌سازد.

تنها یک گزارش خوانده می‌شود: وقتی Nginx پیش از Apache قرار دارد، جمع‌بستن دو گزارش هر درخواست را دو بار می‌شمرد. پنل نخستین گزارشِ فهرست بالا را برمی‌دارد که در یک روز گذشته در آن نوشته شده است (فایل‌هایی که با الگوی نام پیدا شده‌اند پس از آن، از تازه‌ترین، آزموده می‌شوند)؛ اگر یک روز در هیچ‌کدام نوشته نشده باشد، تازه‌ترین گزارش غیرخالی برداشته می‌شود. از این رو گزارشی که logrotate خالی‌اش کرده یا از کارساز وبِ خاموش‌شده به جا مانده، دیگر گزارش فعال را نمی‌پوشاند. آخرین تکهٔ گزارش خوانده می‌شود (۳ مگابایت) و نتیجه ۵ دقیقه در حافظهٔ نهان می‌ماند — بنابراین روی سایت پرمراجعه ممکن است بازهٔ انتخاب‌شده به‌طور کامل پوشش داده نشود؛ پوشش واقعی بالای نمودار نوشته شده است.

«پنل نمی‌تواند گزارش کارساز وب را بخواند». علت تقریباً همیشه دسترسی‌هاست: گزارش‌ها و پوشهٔ آن‌ها به گروه adm تعلق دارند و کاربر وب در این گروه نیست. نصب‌کنندهٔ Arcivéo او را به این گروه می‌افزاید؛ پس از نصب دستی، یک wrapper کوچک از root که تنها یک کار برایش مجاز است — نمایش گزارش — مشکل را حل می‌کند (صفحهٔ ModSecurity هم به همین شیوه کار می‌کند). wrapper گزارش را با همان قاعده‌های پنل برمی‌گزیند. فرمان در خود صفحه نشان داده می‌شود؛ اینجا هم می‌آید. آن را یکجا بچسبانید — این یک فرمان است:
sudo tee /usr/local/bin/monitor-weblog >/dev/null <<'EOF' && sudo chmod 755 /usr/local/bin/monitor-weblog && echo 'www-data ALL=(root) NOPASSWD: /usr/local/bin/monitor-weblog' | sudo tee /etc/sudoers.d/monitor-weblog >/dev/null && sudo visudo -c #!/bin/sh L="/var/log/nginx/access.log /var/log/apache2/access.log /var/log/apache2/other_vhosts_access.log /var/log/httpd/access_log $(ls -t /var/log/nginx/*access*.log /var/log/apache2/*access*.log /var/log/apache2/*requests*.log /var/log/httpd/*access_log 2>/dev/null)" for f in $L; do [ -r "$f" ] && [ -n "$(find "$f" -size +0 -mmin -1440 2>/dev/null)" ] && exec tail -c 3000000 "$f"; done for f in $(ls -t $L 2>/dev/null); do [ -r "$f" ] && [ -s "$f" ] && exec tail -c 3000000 "$f"; done EOF

کاربر وب ممکن است با www-data فرق داشته باشد — ببینید استخر PHP-FPM سایت با کدام کاربر اجرا می‌شود (ps -o user= -C php-fpm) و همان را در سطر sudoers بگذارید. اگر نام گزارش نه access دارد و نه requests، مسیر آن را به ابتدای سطر L=… درون wrapper بیفزایید.

«برای بازهٔ انتخاب‌شده رکوردی نیست» — یعنی گزارش خوانده شد ولی سطر مناسبی در آن پیدا نشد: یا سایت واقعاً باز نشده، یا گزارش همین حالا چرخانده شده (logrotate) و از نو آغاز شده است.

تلاش برای سوءاستفاده از آسیب‌پذیری‌های شناخته‌شده. پنل درخواست‌های گزارش را با امضای حدود بیست اکسپلویت رایج مقایسه می‌کند: PHP-CGI، پیمایش پوشه‌ها در Apache، PHPUnit، Log4Shell و آسیب‌پذیری‌های روترها، دوربین‌ها و درگاه‌های VPN. برای هر تلاش، پنل بر پایهٔ واقعیت‌ها می‌گوید که آیا روی این سرور کار می‌کند: نسخه‌های نصب‌شدهٔ Apache و PHP، وجود Java و کد پاسخ سرور به درخواست فایل. فقط آنچه واقعاً ممکن است کار کند قرمز می‌شود.

سخت‌سازی وب‌سرور. پنل هر سایت روی سرور را از راه 127.0.0.1 درخواست می‌کند و بررسی می‌کند بازدیدکننده چه دریافت می‌کند: آیا نسخهٔ وب‌سرور و PHP دیده می‌شود، آیا متد TRACE فعال است و آیا هدرهای محافظ Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy و Content-Security-Policy وجود دارند. سایت‌ها از نشانی‌ای که پنل با آن باز شده و از فایل‌های میزبان مجازی، اگر پنل بتواند آن‌ها را بخواند، گرفته می‌شوند. نتیجه هر ۱۰ دقیقه به‌روز می‌شود.

اگر چیزی کم باشد، زیر جدول یک فرمان نمایش داده می‌شود. این فرمان فایل جداگانهٔ zz-arciveo-hardening.conf را می‌سازد و وب‌سرور را فقط پس از موفقیت آزمون پیکربندی دوباره بارگذاری می‌کند؛ هدرهایی که سایت خودش می‌فرستد تکرار نمی‌شوند. CSP به‌صورت یک خط پیشنهاد نمی‌شود: سیاست برای هر سایت جداگانه ساخته می‌شود، وگرنه سایت دیگر اسکریپت‌ها، استایل‌ها و فونت‌های خودش را بارگذاری نمی‌کند. برای بازگرداندن تغییر در Apache:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
اگر نسخهٔ وب‌سرور از پیش پنهان باشد، امضای صفحه‌های خطا (ServerSignature) علامت‌گذاری نمی‌شود: بدون نسخه، فقط نام سرور و نام میزبان را نشان می‌دهد.

34. نقشه حملات (GeoIP)

صفحه «نقشه حملات» کشور را از روی IP با فرمان geoiplookup تشخیص می‌دهد. بدون بسته GeoIP کشورها شناسایی نمی‌شوند و نقطه‌ای روی نقشه ظاهر نخواهد شد:

sudo apt install geoip-bin geoip-database # بررسی: geoiplookup 8.8.8.8
نیازی به sudo نیست — پایگاه‌داده /usr/share/GeoIP/GeoIP.dat برای همه خواندنی است و نتایج در tmp/geoip_cache.json کش می‌شوند. خود نقشه (Leaflet + کاشی‌های OpenStreetMap) در مرورگر بارگذاری می‌شود — پس رایانه‌ای که داشبورد در آن باز است به اینترنت نیاز دارد.

35. در معرض بودن خارجی، به‌روزرسانی‌ها و به‌روزرسانی خودکار

دو کارت داخلی داشبورد که نه «روشن/خاموش» بودن ابزار، بلکه امنیت واقعی سرور را نشان می‌دهند. نیازی به نصب ندارند و بدون sudo به‌صورت محلی خوانده می‌شوند.

در معرض بودن خارجی — اینکه چند سرویس روی همه رابط‌ها (0.0.0.0/[::]) گوش می‌دهند و از بیرون در دسترس‌اند. اگر پایگاه‌داده یا کش (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) به بیرون باز باشد آن را قرمز نشان می‌دهد — یک حفره مستقیم (−۱۰ از امتیاز امنیتی). منبع: ss -tuln.

اگر کارت قرمز است — پایگاه‌داده را از دنیای بیرون ببندید: آن را به 127.0.0.1 مقید کنید (bind-address در پیکربندی MySQL/PostgreSQL، bind 127.0.0.1 در Redis) یا پورت را در UFW ببندید.
«پورت باز» ≠ «در دسترس از بیرون». سرویسی که روی 127.0.0.1 (loopback) گوش می‌دهد فقط برای خود سرور دیده می‌شود — حتی اگر پورت «باز» باشد، از بیرون به آن نمی‌توان رسید. به همین دلیل Postfix روی پورت ۲۵ که به loopback مقید شده امن است: پیکربندی خودکار inet_interfaces = loopback-only را تنظیم می‌کند (به‌علاوه smtpd_banner خنثی — که هشدار Lynis MAIL-8818 درباره افشای نسخه را برطرف می‌کند). کارت «در معرض بودن خارجی» فقط چیزی را که روی 0.0.0.0/[::] گوش می‌دهد به‌عنوان بیرونی می‌شمارد؛ سرویس‌های loopback در آن نمی‌آیند.
Lynis MAIL-8818 به‌صورت دستی (اگر خودتان ایمیل را راه‌اندازی کرده‌اید): در /etc/postfix/main.cf مقدار smtpd_banner = $myhostname ESMTP (بدون نسخه و سیستم‌عامل) و inet_interfaces = loopback-only را تنظیم کنید، سپس sudo systemctl restart postfix.

به‌روزرسانی‌های امنیتی — اینکه چند وصله امنیتی منتظر نصب‌اند و آیا پس از به‌روزرسانی هسته نیاز به راه‌اندازی مجدد است (−۵ از امتیاز امنیتی در صورت وجود وصله). منبع: /usr/lib/update-notifier/apt-check، فایل /var/run/reboot-required. فهرست کامل — در صفحه «به‌روزرسانی‌های امنیتی».

# نصب به‌روزرسانی‌ها: sudo apt update && sudo apt upgrade # بررسی اینکه چه چیزی به بیرون گوش می‌دهد: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
کارت به‌روزرسانی‌ها روی Ubuntu/Debian کار می‌کند (update-notifier-common). اگر apt-check نبود — مانیتور وصله‌ها را از طریق apt-get -s upgrade می‌شمارد.

به‌روزرسانی خودکار امنیتی (unattended-upgrades) — در صفحه «به‌روزرسانی‌های امنیتی» کارتی جداگانه نشان می‌دهد که آیا نصب خودکار وصله‌های امنیتی فعال است و آخرین بار کِی اجرا شده. نیازی به sudo نیست — وضعیت از طریق apt-config dump خوانده می‌شود.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # فعال‌سازی # بررسی اینکه چه چیزی فعال است: apt-config dump | grep Unattended-Upgrade

نگهداری

36. پشتیبان‌گیری

پشتیبان مهم‌ترین بیمه است: از دست رفتن داده‌ها از هر نفوذی خطرناک‌تر است. به دو چیز نیاز دارید — پشتیبان سرور/سایت‌ها و جداگانه پشتیبان پایگاه‌داده پنل (کاربران، کلیدهای WebAuthn، تنظیمات و لایسنس آنجا هستند).

گزینه A — HestiaCP: برگهٔ Backup در کاربر ← دکمهٔ ساخت پشتیبان (یا زمان‌بندی‌شده در تنظیمات سرور). پشتیبان شامل سایت‌ها و پایگاه‌دادهٔ آن‌هاست.

گزینه B — دستی (cron): دامپ پایگاه‌داده + آرشیو پوشهٔ data/ پنل:

# کرون root (sudo crontab -e) — پشتیبان روزانه در ساعت ۲:۳۰ (نام‌ها/مسیرهای خود را جایگزین کنید): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # حذف آرشیوهای قدیمی‌تر از ۱۴ روز: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
پشتیبان روی همان سرور از اشتباهات نجاتتان می‌دهد، اما از دست رفتن خود سرور را جبران نمی‌کند. آرشیوها را روی فضای ذخیره‌سازی بیرونی (سرور دیگر، S3، rclone به فضای ابری) کپی کنید. مطمئن شوید که بازیابی واقعاً کار می‌کند.

37. به‌روزرسانی و انتقال پنل

به‌روزرسانی به نسخهٔ جدید. ابتدا یک نسخهٔ پشتیبان بگیرید. سپس فایل‌های کد را دوباره بارگذاری کنید و داده‌های خود را حفظ کنید:

  • بازنویسی (کد): public/، includes/، assets/، cron/، database/، و همچنین .htaccess ریشه (کنترلر جلویی — مسیریابی را نباید از نسخهٔ قدیمی نگه داشت)، manifest.json، sw.js؛
  • دست نزنید: config.php (اطلاعات پایگاه داده)، data/ (گزارش‌ها)، logs/، tmp/ (نشست‌ها و کش).
# پس از بارگذاری — کش PHP را پاک کنید (اگر opcache فعال است): sudo systemctl reload php*-fpm
FileZilla پیام SSH_FX_PERMISSION_DENIED — Permission denied می‌دهد. فایل‌های پنل متعلق به www-data هستند (هنگام نصب این‌گونه تنظیم شده‌اند)، در حالی که کلاینت SFTP با کاربر خودتان وصل می‌شود که دسترسی نوشتن ندارد. سپردن کل پنل به www-data «تا کار کند» دقیقاً همان چیزی است که به این خطا منجر می‌شود؛ در ادامه سه روش آمده که هرکدام مشکل را حل می‌کند.
# گزینهٔ A (توصیه‌شده) — جدا کردن مالکان: کد مال شما، پوشه‌های کاری مال وب‌سرور. # وب‌سرور اصلاً دسترسی نوشتن روی کد پنل پیدا نمی‌کند: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # گزینهٔ B — ACL روی مالکان فعلی (چیزی جابه‌جا نمی‌شود): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # گزینهٔ C — از طریق گروه www-data. ساده‌تر، اما دسترسی نوشتن روی فایل‌های # پنل به وب‌سرور هم می‌رسد (با آسیب‌پذیری در PHP کد قابل جایگزینی است): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
چرا گزینهٔ A امن است. پنل فقط در سه پوشه می‌نویسد — data/ (گزارش‌ها)، tmp/ (نشست‌ها و کش)، logs/؛ این‌ها همچنان در اختیار www-data می‌مانند. بقیه کد است و وب‌سرور فقط برای خواندن به آن نیاز دارد، که گروه www-data با دسترسی 644 آن را فراهم می‌کند. سود جانبی: با آسیب‌پذیری در PHP دیگر نمی‌توان فایل‌های پنل را بازنویسی کرد. در پنل‌های میزبانی (HestiaCP و مشابه) گزینهٔ A لازم نیست: آنجا فایل‌های سایت به‌هرحال متعلق به همان اکانتی هستند که با SFTP وارد می‌شوید، و وب‌سرور آن‌ها را از طریق گروه می‌خواند.
دام گزینهٔ B: هر chmod بعدی روی فایل‌ها ماسک ACL را بازنشانی می‌کند و دسترسی بی‌صدا از بین می‌رود. اگر پس از «مرتب کردن دسترسی‌ها» بارگذاری دوباره به Permission denied برخورد کرد — هر دو دستور setfacl را تکرار کنید.
بیت 2 در گزینهٔ C همان setgid است: فایل‌های بارگذاری‌شده با SFTP در گروه www-data باقی می‌مانند، وگرنه پنل نمی‌تواند آن‌ها را بازنویسی کند. پس از گزینهٔ C در FileZilla دوباره وصل شوید — گروه جدید فقط با ورود تازه اعمال می‌شود. بررسی: id deploy (باید گروه www-data ظاهر شود) و ls -ld /path/to/monitor (drwxrwsr-x — حرف s یعنی setgid تنظیم شده است).

انتقال به سرور دیگر:

  1. روی سرور جدید سایت + HTTPS را راه‌اندازی کنید (به صفحهٔ نصب دستی مراجعه کنید).
  2. همهٔ فایل‌های پنل را همراه با config.php، data/ کپی کنید.
  3. پایگاه داده را منتقل کنید: mysqldump روی سرور قدیمی ← وارد کردن در سرور جدید؛ اطلاعات پایگاه داده را در config.php اصلاح کنید.
  4. روی سرور جدید تکرار کنید: sudoers، عضویت در گروه adm، وظایف cron.
  5. لایسنس به دامنه گره خورده است — اگر دامنه یکسان باشد، کلید همچنان کار خواهد کرد.

38. سایت دوم روی همین سرور (یک دامنه دیگر)

پنل از قبل کار می‌کند و روی همین سرور به یک سایت دیگر نیاز است — با دامنهٔ خودش در کنار مانیتور. پس از راه‌اندازی خودکار، سرور یک Apache + PHP-FPM معمولی است، بنابراین سایت به سه چیز نیاز دارد: پوشهٔ خودش، vhost خودش و گواهی خودش. پنل دست‌نخورده می‌ماند: کل سرور را زیر نظر دارد و نسخهٔ دوم برای دامنهٔ دوم لازم نیست.

این بخش برای سروری است که پنل میزبانی ندارد. اگر HestiaCP یا پنلی مشابه نصب است، دامنه را در خود پنل اضافه کنید نه دستی. و اگر قصد دارید سایت‌های زیادی (به‌علاوهٔ ایمیل) میزبانی کنید، پنل را پیش از راه‌اندازی خودکار نصب کنید: اسکریپت آن را تشخیص می‌دهد و به حالت افزایشی می‌رود، در حالی که پنلی که بعداً نصب شود پیکربندی Apache، فایروال و ایمیل را از نو می‌سازد.
در فرمان‌ها چه چیزی را جایگزین کنید. site2.example.com نشانی سایت جدید است. می‌تواند هم دامنهٔ جداگانه (mycompany.com) باشد و هم زیردامنه (shop.mycompany.com) — برای Apache تفاوتی ندارد و فقط رکورد DNS (گام ۱) و صدور گواهی (گام ۵) فرق می‌کنند. site2 فقط یک نام کوتاه برای سایت است: پوشهٔ /var/www/site2 و فایل پیکربندی site2.conf با همین نام ساخته می‌شوند. این نام هیچ ارتباطی با دامنه ندارد؛ Apache نشانی را تنها از ServerName می‌گیرد و نام می‌تواند هر چیزی باشد — مثلاً برای shop.mycompany.com استفاده از shop راحت است: پوشهٔ /var/www/shop، پیکربندی shop.conf.

۱. DNS. در پنل DNS نزد ثبت‌کنندهٔ دامنه یک رکورد A به IP این سرور بسازید:

# زیردامنه (shop.mycompany.com): نوع: A نام: shop مقدار: 203.0.113.10 TTL: 3600 # دامنهٔ جداگانه (mycompany.com) — دو رکورد، تا نشانی با www هم کار کند: نوع: A نام: @ مقدار: 203.0.113.10 TTL: 3600 نوع: A نام: www مقدار: 203.0.113.10 TTL: 3600

پس از چند دقیقه بررسی کنید: dig +short site2.example.com باید IP سرور را برگرداند.

۲. پوشهٔ سایت.

sudo mkdir -p /var/www/site2 sudo chown -R www-data:www-data /var/www/site2

۳. vhost. بلوک را یکجا در ترمینال بچسبانید — مسیر سوکت PHP-FPM خودکار تشخیص داده می‌شود:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # تشخیص خودکار سوکت PHP-FPM sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # دامنهٔ جداگانه: علامت # را از خط زیر بردارید تا سایت با www هم باز شود. # برای زیردامنه این خط لازم نیست. #ServerAlias www.site2.example.com DocumentRoot /var/www/site2 <Directory /var/www/site2> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/site2_error.log CustomLog ${APACHE_LOG_DIR}/site2_access.log combined </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/site2.conf

۴. سایت را فعال کنید و پیش از بارگذاری مجدد، پیکربندی را بررسی کنید:

sudo a2ensite site2.conf sudo apache2ctl configtest # نتیجهٔ مورد انتظار: Syntax OK sudo systemctl reload apache2

۵. SSL. certbot را راه‌اندازی خودکار از قبل نصب کرده است؛ در این مرحله DNS باید به سرور اشاره کند:

# زیردامنه — یک نشانی: sudo certbot --apache -d site2.example.com # دامنهٔ جداگانه — همان ابتدا همراه با www (هر دو رکورد A از گام ۱ باید به سرور اشاره کنند # و www باید در ServerAlias باشد، وگرنه certbot نمی‌تواند نشانی دوم را بررسی کند): sudo certbot --apache -d site2.example.com -d www.site2.example.com

دامنهٔ سوم و دامنه‌های بعدی هم به همین شکل اضافه می‌شوند — گام‌های ۱ تا ۵ را با دامنه و پوشهٔ دیگر تکرار کنید.

چه کسی به درخواست‌های «بیگانه» پاسخ می‌دهد. اگر Host با هیچ ServerName مطابقت نکند (ورود با IP یا دامنهٔ شخص دیگری که به سرور شما اشاره می‌کند)، Apache نخستین سایت به ترتیب الفبا را از /etc/apache2/sites-enabled/ ارائه می‌کند. سایت پیش‌فرض 000-default را راه‌اندازی خودکار غیرفعال می‌کند، بنابراین این نقش «کشیک» نزد vhost پنل (arciveo-monitor.conf) می‌ماند — و بر سایت‌هایی که با نام خودشان باز می‌شوند اثری ندارد. اگر می‌خواهید با IP سایت دیگری باز شود، نام فایل آن را طوری بگذارید که در الفبا زودتر بیاید (مثلاً 000-site2.conf).
محافظت در سطح کل سرور است: UFW، Fail2ban، CrowdSec، ModSecurity و Suricata از پیش کار می‌کنند و سایت جدید را خودکار پوشش می‌دهند و نیازی به تنظیم جداگانه نیست. پورت‌های ۸۰/۴۴۳ نیز از قبل باز هستند. سایت لاگ‌های خودش را دارد: site2_error.log و site2_access.log در /var/log/apache2/.
پوشهٔ سایت جدید را داخل پوشهٔ پنل نگذارید — ریشهٔ وب پنل با .htaccess خودش اداره می‌شود (کنترلر جلویی) و سایت تودرتو چیزی غیر از انتظار شما ارائه می‌دهد. سایت‌ها را کنار هم نگه دارید: /var/www/monitor و /var/www/site2.

39. بازیابی دسترسی (کلید، رمز عبور یا IP مسدودشده)

اگر نمی‌توانید وارد شوید — همه‌چیز مستقیماً در پایگاه‌داده روی سرور قابل تعمیر است. پایگاه‌داده را باز کنید (نام آن در config.php است):

sudo mysql MY_DB

کلید WebAuthn گم شده (عامل دوم رد می‌شود) — 2FA را غیرفعال کنید، با رمز عبور وارد شوید و کلید جدید ثبت کنید:

UPDATE users SET webauthn_enabled = 0;

رمز عبور را فراموش کرده‌اید — هش جدیدی تنظیم کنید (آن را روی سرور بسازید و جایگزین کنید):

# ساخت هش رمز عبور جدید: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # در پایگاه‌داده (هش به‌دست‌آمده را وارد کنید): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

با فیلتر IP خودتان را مسدود کرده‌اید — محدودیت را غیرفعال کنید:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
دسترسی به پایگاه‌داده همیشه هست: sudo mysql روی سرور، یا phpMyAdmin / بخش پایگاه‌داده در پنل هاست. پس از بازیابی، دوباره WebAuthn و فیلتر IP را فعال کنید.

40. همه‌ی cron-jobها در یک جا

خلاصه‌ی وظایف در root-cron سرور قرار می‌گیرد (از طریق sudo crontab -e افزوده می‌شوند). فقط سطرهای ابزارهایی را که استفاده می‌کنید نگه دارید؛ مسیرها را متناسب با سرور خود اصلاح کنید.

# cron سرور برای مانیتور (root) — از طریق این افزوده شود: sudo crontab -e # 01:30 — اسکن ClamAV روی مسیرهای خطرناک (web, home, temp) ← کارت‌های «فایل‌های بررسی‌شده» و «آخرین اسکن» 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — بررسی یکپارچگی فایل‌ها با AIDE (نیازمند --config صریح) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # هنگام راه‌اندازی — بازیابی دسترسی‌های /var/lib/aide (فایل tmpfiles بسته‌ی # aide-common.conf آن‌ها را به 0700 بازمی‌گرداند و پنل دیگر پایگاه‌داده را نمی‌بیند) @reboot chmod 755 /var/lib/aide # 03:00 — ممیزی امنیتی Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — به‌روزرسانی فهرست مسدودسازی ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # مجموعه‌ی ipsum هنگام راه‌اندازی توسط سرویس ipsum-load.service بارگذاری می‌شود (پیش از فایروال، وگرنه # UFW مجموعه را در before.rules نمی‌بیند) — این cron نیست. اینجا فقط رفرش روزانه‌ی بالا است. # 06:00 — گزارش Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # هر ۳۰ دقیقه — بررسی SMART دیسک‌ها */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — یکپارچگی بسته‌ها با debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — گزارش زمان‌بندی‌شده به Email و Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # هر ساعت — به‌روزرسانی فهرست بسته‌ها (برای کارت «به‌روزرسانی‌های امنیتی») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # هر ۵ دقیقه — نمونه‌گیری منابع (CPU/RAM/شبکه/دیسک) برای صفحه‌ی «کارایی» */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # هر ۵ دقیقه، با جابه‌جایی نسبت به نمای لحظه‌ای بالا — حسگر رویدادهای امنیتی: # مسدودسازی‌های تازه، هشدارهای IDS، سرویس حفاظتی متوقف‌شده، بندر باز شده. # تا وقتی پایش در تنظیمات روشن نشود، این کار هیچ نمی‌کند. 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # هر ۱۵ دقیقه — خلاصهٔ کارساز به حساب کاربری، تا چند کارساز در یک فهرست دیده شوند. # تا وقتی خلاصه روشن نشود، این کار هیچ نمی‌کند. 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
جزئیات هر مورد در بخش مربوطه است. وظایف بکاپ (بخش قبل) به همین cron افزوده می‌شوند. پس از ویرایش بررسی کنید: sudo crontab -l و اینکه سرویس cron فعال باشد.
زمان cron = منطقه‌ی زمانی سرور، نه TIMEZONE در config.php. ثابت TIMEZONE فقط بر PHP اثر می‌گذارد (نحوه‌ی نمایش تاریخ‌ها در پنل)، اما دیمن cron وظایف را بر اساس زمان سیستم‌عامل اجرا می‌کند. اگر منطقه‌ی زمانی سرور با شما یکی نباشد، گزارش «08:00» در زمان دیگری می‌رسد. مثال: سرور در منطقه‌ی دیگری است (Europe/Berlin، UTC+2) و شما در تهران هستید (UTC+3:30) ← گزارش «08:00» به وقت شما ساعت 09:30 می‌رسد. منطقه‌ی زمانی سیستم را بررسی و در صورت نیاز با منطقه‌ی خود هماهنگ کنید:
# بررسی منطقه‌ی زمانی فعلی سرور: timedatectl # تنظیم منطقه‌ی زمانی خود (نمونه) و راه‌اندازی مجدد cron: sudo timedatectl set-timezone Asia/Tehran sudo systemctl restart cron
پس از این، سطر 0 8 * * * در ساعت 08:00 به وقت محلی اجرا می‌شود. در غیر این صورت باید خودِ cron را جابه‌جا می‌کردید، اما با تغییر به ساعت زمستانی/تابستانی این جابه‌جایی دوباره به هم می‌ریزد — پس تنظیم منطقه‌ی زمانی سیستم درست‌تر است.
اسکریپت‌های پوششی آماده. نسخه‌های کاری آن‌ها و نمونه‌ی crontab (crontab.txt) در پوشه‌ی system/ کنار پروژه، بیرون از public_html قرار دارند. این‌ها بخشی از سایت نیستند — نیازی به آپلود در ریشه‌ی وب نیست؛ آن‌ها را در مسیرهای سیستمی روی سرور قرار دهید (مانند crontab بالا):
  • lynis-scan.sh ← /usr/local/bin/ (chmod +x) — lynis audit system را اجرا می‌کند، در حین اسکن پرچم /tmp/lynis-running را می‌گذارد و lynis-report.dat را در data/lynis/ پنل کپی می‌کند؛
  • logwatch_daily.sh ← /usr/local/bin/ (chmod +x) — گزارش روزانه‌ی Logwatch (sshd, fail2ban, sudo, postfix) را در data/logwatch/ می‌سازد؛
  • smart-scan.sh ← /usr/local/bin/ (chmod +x) — وضعیت دیسک‌ها (smartctl) را در data/disk/ ثبت می‌کند؛
  • debsums-scan.sh ← /usr/local/bin/ (chmod +x) — یکپارچگی بسته‌ها (debsums) را در data/debsums/ بررسی می‌کند؛
  • clamav-scan.sh ← /usr/local/bin/ (chmod +x) — اسکن آنتی‌ویروس ClamAV روی مسیرهای خطرناک (web, home, temp)؛ خلاصه را در /var/log/clamav/scan.log می‌نویسد که صفحه‌ی ClamAV از آنجا می‌خواند (سطر 01:30 در crontab بالا)؛
  • load-ipsum.sh ← /usr/local/bin/ (chmod +x) — مجموعه‌ی ipset با نام ipsum (level 1) را در جای خود به‌روزرسانی می‌کند، بدون شکستن قواعد فعال فایروال (سطر 04:00 در crontab بالا)؛
  • daily-report-all.sh ← /usr/local/bin/ (chmod +x) — گزارش cron/daily_report.php پنل را اجرا می‌کند (سطر 08:00 در crontab بالا)؛
  • daily_report.php — از قبل در پنل هست (cron/daily_report.php)، از طریق daily-report-all.sh اجرا می‌شود و جداگانه نصب نمی‌شود؛
  • collect-metrics-all.sh ← /usr/local/bin/ (chmod +x) — cron/collect_metrics.php پنل را اجرا می‌کند (صفحه‌ی «کارایی»، سطر */5 در crontab بالا)؛ collect_metrics.php از قبل در پنل هست و جداگانه نصب نمی‌شود؛
  • security-watch-all.sh ← /usr/local/bin/ (chmod +x) — فایل cron/security_watch.php پنل را به نام مالک سایت اجرا می‌کند (صفحهٔ «رویدادهای امنیتی»، سطر 2-57/5 در crontab بالا)؛ security_watch.php همراه خود پنل می‌آید و نصب جداگانه نمی‌خواهد؛
  • fleet-push-all.sh ← /usr/local/bin/ (chmod +x) — خلاصهٔ کارساز را از راه cron/fleet_push.php پنل به حساب کاربری می‌فرستد (سطر 3,18,33,48 بالا)؛
  • crontab.txt (system/cron/) — نمونه‌ی وظایف؛ سطرهای لازم را از طریق sudo crontab -e وارد کنید.
مسیر اسکریپت در crontab باید با جایی که آن را قرار داده‌اید یکی باشد.
چگونه اسکریپت را در /usr/local/bin/ قرار دهیم. نمی‌توان مستقیماً از FileZilla آنجا نوشت — این پوشه متعلق به root است و کلاینت SFTP خطای SSH_FX_PERMISSION_DENIED می‌گیرد. روش کار چنین است: نخست فایل را در /tmp بارگذاری کنید (همه در آنجا می‌نویسند)، سپس با یک فرمان آن را به محل خود منتقل کنید:
# در FileZilla: در فیلد «سایت راه دور» عبارت /tmp را وارد کنید و اسکریپت را آنجا آپلود کنید، # سپس از طریق SSH (فرمان install بی‌درنگ مالک و دسترسی‌ها را تنظیم می‌کند، به chown/chmod نیازی نیست): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # بررسی: فایل سر جای خود، دسترسی rwxr-xr-x، نحو سالم bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
پوشه‌ها را اشتباه نگیرید: به /tmp در ریشه‌ی سرور نیاز است — نه /var/tmp و نه tmp/ درون خودِ پنل (این آخری متعلق به www-data است و از دسترس کاربر شما بسته است). در درخت FileZilla، /tmp شاخه‌ای در بالاترین سطح است، کنار var و نه درون آن.
سرور را با تنظیم خودکار راه‌اندازی کردید؟ این پوشش‌ها و وظایف cron آن‌ها از قبل نصب شده‌اند توسط اسکریپت (در /usr/local/bin/، لاگ — /var/log/arciveo-cron.log) — نیازی به کار دستی نیست.
اسکریپت‌ها پنل را کجا جست‌وجو می‌کنند. پوشش‌ها مستقل از دامنه‌اند: نصب‌های پنل را با پیمایش /home/*/web/*/public_html و /var/www/* پیدا می‌کنند و گزارش‌ها را در data/ آن‌ها قرار می‌دهند. اگر پنل در مسیر دیگری است — آن را به سطر for app in … درون اسکریپت‌ها بیفزایید، وگرنه گزارش‌های Lynis/SMART/debsums/Logwatch به پنل نمی‌رسند.
cron.log و دسترسی‌ها. فایل logs/cron.log را نخست root-cron می‌سازد — پس متعلق به root خواهد بود و زبانه‌ی «گزارش cron» در پنل نمی‌تواند آن را بخواند یا پاک کند. فایل را از پیش با کاربر وب بسازید (مالک پوشه‌ی سایت؛ در HestiaCP این حساب است، مثلاً admin) — آنگاه root-cron فقط به آن می‌افزاید و مالک را تغییر نمی‌دهد:
# ساخت از پیش با کاربر وب (پیش از افزودن سطرهای cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # اگر cron.log از قبل توسط root-cron ساخته شده — به کاربر وب واگذار کنید: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
یافتن مالک پوشه: stat -c %U /path/to/monitor.
مدیریت از پنل. در بخش «سیستم» صفحه‌ی «Crontab» وجود دارد — می‌توانید بدون SSH وظایف را ببینید و بیفزایید. پنل فقط وظایفی را ویرایش می‌کند که خودش افزوده است (بلوکی جدا در root-crontab که با کامنت‌های سرویسی نشانه‌گذاری شده)؛ هر چه از قبل در crontab است (فهرست بالا) آنجا به‌صورت فهرست فقط‌خواندنی «سایر وظایف سرور» با دکمه‌ی «کپی به ویرایشگر» نمایش داده می‌شود — این دکمه فقط زمان‌بندی/فرمان را به فرم افزودن منتقل می‌کند و سطر اصلی را دست نمی‌زند. برای «انتقال» یک وظیفه‌ی موجود به مدیریت پنل — آن را در ویرایشگر کپی و ذخیره کنید، سپس سطر قدیمی را دستی حذف کنید (sudo crontab -e)، وگرنه دوبار اجرا می‌شود.
تنظیم یک‌باره روی سرور. این صفحه به یک اسکریپت پوششی با دسترسی بالا نیاز دارد — نه sudo crontab خام (که برای هر کسی که به نشست پنل دست یابد، ارتقاء مستقیم به root می‌بود)، بلکه اسکریپتی محدود با دو فرمان (list/set) که تنها بلوک خود را میان کامنت‌های سرویسی دست می‌زند. یک‌بار نصب کنید:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
کاربر وب ممکن است با www-data فرق کند — بررسی کنید pool PHP-FPM سایت با چه کاربری کار می‌کند (ps -o user= -C php-fpm) و همان را در سطر sudoers جایگزین کنید.
فایل جدید با مالک اشتباه آپلود شده — صفحه «Access denied.» می‌دهد. اگر فایل public/crontab_monitor.php از طریق FTP/SFTP با کاربر سیستمی دیگری (مثلاً root) نسبت به بقیه‌ی فایل‌های سایت آپلود شده باشد، وب‌سرور نمی‌تواند آن را بخواند. مالک و دسترسی‌ها را با فایل مجاور مقایسه و هماهنگ کنید:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

عیب‌یابی

41. ابزار نصب شده اما «نصب‌نشده» نشان می‌دهد

مانیتور وجود ابزارها را از طریق dpkg-query — پایگاه بسته‌های APT — تشخیص می‌دهد. اگر ابزار از طریق apt نصب نشده باشد (به‌صورت دستی، از snap یا از سورس)، dpkg آن را نمی‌بیند.

# بررسی از طریق dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # یافتن مسیر باینری: which ufw fail2ban-client auditctl # تست sudo از www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. رفع مشکلات (خطای ۵۰۰، نبود داده)

خطای ۵۰۰ — لاگ‌های PHP، nginx و خودِ مانیتور را بررسی کنید:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # لاگ‌های مانیتور: tail -50 logs/monitor_$(date +%Y-%m-%d).log # دسترسی پوشه‌ها: ls -la data/ tmp/ logs/
داشبورد روی پنل هاستینگ (HestiaCP، ISPmanager، cPanel)؟ آنجا PHP نه با www-data بلکه با حساب کاربری اجرا می‌شود (مثلاً admin — مالک دایرکتوری سایت). همهٔ قواعد sudo و عضویت در گروه‌ها (adm، systemd-journal) باید برای همین کاربر تعریف شوند، وگرنه با وجود اجرای سرویس‌ها ماژول‌ها «غیرفعال / ۰» نشان می‌دهند. کاربر واقعی PHP را ببینید: ps -o user= -C php-fpm | sort -u یا مالک دایرکتوری سایت stat -c '%U' /path/to/monitor. سپس در همهٔ فرمان‌های زیر آن را به‌جای www-data بگذارید. نصب خودکار وب‌کاربر را خودش تشخیص می‌دهد و sudoers را برای او می‌نویسد.

داده‌ها نمایش داده نمی‌شوند — تقریباً همیشه به‌دلیل تعریف‌نشدن دسترسی‌های sudo است. فرمان موردنظر را با هویت وب‌کاربر بررسی کنید (www-data را با کاربر خودتان جایگزین کنید). پرچم -n = بدون رمز، مانند PHP — اگر رمز خواست، یعنی قاعده‌ای در sudoers نیست:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
ماژول «غیرفعال» / «۰» می‌نویسد، هرچند ابزار کار می‌کند (مثلاً sudo aa-status در ترمینال پروفایل‌ها را نشان می‌دهد، اما صفحهٔ «AppArmor» — «غیرفعال»). دلیل: وب‌کاربر روی فرمانِ همین ماژول دسترسی sudo ندارد. آن را از فهرست بالا بررسی کنید: اگر رمز خواست — خط جامانده را به /etc/sudoers.d/monitor بیفزایید («تنظیم sudo»). فرمان‌های «جدید» پرتکرار: /usr/sbin/aa-status (MAC)، /usr/sbin/psad --Status (PSAD).
اگر صفحه‌ای مشخص (Falco، ModSecurity، Auditd، پورت‌های باز UFW) خالی بود — با فهرست موجود در بخش sudo مقایسه کنید: احتمالاً apache2ctl، ausearch، aa-status یا ss مجاز نشده، یا وب‌کاربر در گروه‌های adm/systemd-journal نیست (لاگ‌های fail2ban/auth/modsec و journalctl — Falco و رویدادهای هسته از آنجا خوانده می‌شوند).

43. صفحه خالی است، هرچند داده‌ها روی سرور وجود دارند

نشانه: داده‌ها روی سرور موجودند (از طریق shell دیده می‌شوند)، اما صفحه «داده‌ای نیست» یا وضعیت نادرست نشان می‌دهد — مثلاً AIDE می‌نویسد «مقداردهی اولیه نشده»، هرچند پایگاه ساخته شده است.

علت open_basedir است: بسیاری از پنل‌ها و هاست‌ها پول PHP-FPM را به دایرکتوری دامنه محدود می‌کنند، بنابراین توابع PHP یعنی file_exists()، file_get_contents()، filemtime() روی مسیرهای سیستمی (/var/lib/aide، /var/log، /proc…) مسدود می‌شوند. مانیتور این محدودیت را با خواندن چنین مسیرهایی از طریق دستورهای سیستمی استاندارد (cat، test، stat) دور می‌زند.

# آیا فایل از طریق shell دیده می‌شود (مانیتور به همین شکل می‌خواند): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # مقدار فعلی open_basedir برای پول دامنه: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
اگر shell فایل را «می‌بیند» (VISIBLE) اما صفحه نه — علت open_basedir است. راه‌حل درست، خواندن از طریق دستورهای سیستمی است (که برای AIDE و مانیتور شبکه انجام شده). گسترش open_basedir به /var و /proc لازم نیست و امنیت کمتری دارد.

44. صفحه SSL کار نمی‌کند

مانیتور گواهی‌ها را با اتصال مستقیم به دامنه‌ها از طریق پورت 443 بررسی می‌کند. اگر دامنه از خود سرور در دسترس نباشد یا پورت با فایروال بسته باشد، بررسی انجام نمی‌شود.

# بررسی دستی گواهی: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # بررسی در دسترس بودن: curl -I https://monitor.example.com
مانیتور دامنه‌ها را به‌طور خودکار از پیکربندی‌های nginx (/etc/nginx/sites-enabled/، /etc/nginx/conf.d/) و Apache (/etc/apache2/sites-enabled/) به‌علاوه هاست فعلی از HTTP_HOST برمی‌دارد.
تشخیص خودکار زیردامنه‌ها. زیردامنه‌ها به‌طور خودکار از لاگ‌های عمومی Certificate Transparency تشخیص داده می‌شوند و از طریق شبکه بررسی می‌گردند — حتی اگر روی سرورهای دیگر میزبانی شده باشند. نیازی به افزودن دستی چیزی نیست.

45. فقط یکی از چند پایگاه‌داده دیده می‌شود

مانیتور با کاربری از config.php به MySQL متصل می‌شود که فقط به پایگاه‌داده خودش دسترسی دارد. MySQL در information_schema تنها پایگاه‌داده‌هایی را که کاربر روی آن‌ها دسترسی دارد نشان می‌دهد — به همین دلیل بقیه دیده نمی‌شوند.

برای اینکه مانیتور همه پایگاه‌داده‌ها را ببیند، به این کاربر دسترسی فقط‌خواندنی بدهید (یک بار با کاربر root؛ نام کاربر را از config.php جایگزین کنید):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* تنها دسترسی خواندن می‌دهد — امکان تغییر، حذف یا ایجاد چیزی وجود ندارد و برای مانیتورینگ امن است.
بدون این GRANT، داشبورد فقط پایگاه‌داده خودش را می‌بیند — این خطا نیست، بلکه محدودیت دسترسی است. داشبورد از هیچ sudo mysql استفاده نمی‌کند: فهرست پایگاه‌داده‌ها از طریق اتصال PDO خودش گرفته می‌شود.

46. PostgreSQL در صفحه «پایگاه داده» نمایش داده نمی‌شود

PostgreSQL به دسترسی در سطح کاربر postgres نیاز دارد که کاربر وب پنل آن را ندارد. باز کردن sudo psql گسترده از داخل PHP ناامن است — به‌جای آن، پنل یک پوشش محدود و بدون پارامتر را فرا می‌خواند که فقط نسخه، تعداد اتصال‌ها و فهرست پایگاه‌های داده به‌همراه اندازه‌شان را چاپ می‌کند. آن را بسازید:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Security Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # در /etc/sudoers.d/monitor (کاربر = همان کاربری که PHP-FPM با آن اجرا می‌شود): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
اگر از PostgreSQL استفاده نمی‌کنید — خط monitor-pgstat را از sudoers حذف کنید (گام ۱۳ نصب دستی) و خود اسکریپت را نسازید: کارت PostgreSQL صرفاً غیرفعال باقی می‌ماند.

47. هشدار فعال شد — چه باید کرد

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

  • نقشه حملات / بن‌های فراوان fail2ban — این برای هر سروری در اینترنت عادی است (ربات‌ها دائماً SSH/وب را امتحان می‌کنند). مهم این است که بن‌ها فعال شوند. مطمئن شوید ورود SSH فقط با کلید است (رمز عبور غیرفعال) و IP شما در ignoreip است.
  • ModSecurity درخواست‌ها را مسدود کرد — WAF حملات به سایت را دفع می‌کند، این کار اوست. اگر ترافیک مجاز شما مسدود می‌شود (فعال‌سازی اشتباه) — rule id را در جزئیات پیدا کنید و استثنایی به پیکربندی CRS اضافه کنید.
  • AIDE: فایل‌ها تغییر کردند — فهرست را با کارهایی که کرده‌اید مقایسه کنید (به‌روزرسانی بسته‌ها، ویرایش پیکربندی‌ها عادی است). تغییر باینری‌های سیستمی که شما آن‌ها را لمس نکرده‌اید جای نگرانی دارد. پس از تغییرات مجاز، پایگاه AIDE را به‌روز کنید.
  • debsums: باینری‌ها/کتابخانه‌ها تغییر کردند (خارج از /etc، خارج از /usr/share) — احتمال دستکاری. بسته را بررسی کنید: debsums PACKAGE_NAME، در صورت تردید آن را دوباره نصب کنید (apt install --reinstall).
  • ClamAV / maldet: تهدید یافت شد — فایل قرنطینه‌شده را بررسی کنید، آن را باز نکنید. اگر این یک وب‌شل در پوشه سایت است — سرور را ایزوله کنید و نقطه ورود را بیابید (افزونه آسیب‌پذیر، نشت دسترسی‌ها).
  • Falco: رویدادهای بحرانی (اجرای shell در کانتینر، دسترسی به فایل‌های حساس) — رویداد را بررسی کنید: فرایند چه کسی، چه چیزی را اجرا کرد. اغلب این فعالیت مجاز مدیریتی است.
  • مواجهه بیرونی: پایگاه‌داده/کش قرمز — فوراً ببندید: سرویس را به 127.0.0.1 مقید کنید یا پورت را در UFW ببندید. این یک حفره واقعی است.
  • SSL در حال انقضا / منقضی شده — گواهی را تمدید کنید (Let's Encrypt خودش تمدید می‌شود؛ اگر نه — certbot renew یا تنظیمات پنل را بررسی کنید).
  • به‌روزرسانی‌های امنیتی در انتظارند — نصب کنید: sudo apt update && sudo apt upgrade؛ پس از به‌روزرسانی هسته، سرور را راه‌اندازی مجدد کنید.
نشانه‌های نفوذ واقعی (فرایندها/کاربران ناشناخته، باینری‌های تغییریافته، اسپم خروجی، وظایف cron ناشناخته): سرور را از دسترسی بیرونی جدا کنید، برای تحلیل پشتیبان بگیرید و اگر داده‌ها حیاتی‌اند، سرور تمیزی از یک پشتیبان مورد اعتماد برپا کنید — پاک‌سازی مطمئن یک روت‌کیت دشوار است.