FAQ

นี่คือคู่มือการติดตั้ง ตั้งค่า และดูแลรักษา Arcivéo Monitor เนื้อหาแบ่งเป็นกลุ่ม ได้แก่ ภาพรวมทั่วไป การติดตั้งแดชบอร์ด การเชื่อมต่อเครื่องมือ ด้านความปลอดภัย โมดูลในตัว และการวินิจฉัยปัญหา คุณคัดลอกคำสั่งได้ด้วยปุ่มทางขวา

เริ่มต้นใช้งาน

01. ติดตั้งแดชบอร์ด — เลือกวิธี

การติดตั้งแดชบอร์ดอยู่ในหน้าคู่มือทีละขั้นตอนแยกต่างหาก เลือกวิธี:

ไม่แน่ใจ — เลือกแบบอัตโนมัติ คู่มือนี้ยังคงเป็นแหล่งข้อมูลหลักเดียวเรื่อง SSL, เครื่องมือ, cron และการวินิจฉัยปัญหา — หน้าติดตั้งจะลิงก์มายังหัวข้อในนี้ โดยไม่ทำซ้ำ

ภาพรวม

02. Arcivéo Monitor คืออะไร

Arcivéo 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 แบบทั่วไป ผลลัพธ์จะถูกแยกวิเคราะห์และแสดงในเบราว์เซอร์

สำหรับหลายเซิร์ฟเวอร์ ให้ติดตั้งมอนิเตอร์บนแต่ละเครื่องแยกกัน โดยใช้โดเมนที่ไม่ซ้ำกัน

04. วิธีคำนวณคะแนนความปลอดภัย

คะแนนเริ่มต้นที่ค่าสูงสุดและลดลงตามแต่ละปัญหาที่ตรวจพบ:

  • UFW ไม่ทำงาน — −30
  • Fail2ban ไม่ได้เริ่มทำงาน (ไม่มี jail ที่ทำงานอยู่) — −25
  • ไม่มีคีย์ WebAuthn — −15
  • Lynis hardening index < 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
  • มีอัปเดตด้านความปลอดภัยรอการติดตั้ง — −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 และ Email

แดชบอร์ดสามารถส่งรายงานความปลอดภัยไปยัง Telegram และอีเมลได้ (ทั้งด้วยปุ่มและตามกำหนดเวลา) ตั้งค่าได้ในส่วน “การตั้งค่า”

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 แล้วกด “บันทึกและส่งทดสอบ”

Email. เลือกได้สองวิธีใน “การตั้งค่า” → Email:

  • SMTP — โฮสต์, พอร์ต (465/SSL หรือ 587/TLS), ล็อกอินและรหัสผ่านของกล่องเมลคุณ;
  • Resend — API ยุคใหม่: ระบุคีย์ API (re_...) และโดเมนผู้ส่งที่ยืนยันแล้ว
ปุ่ม “ส่งทดสอบ” จะตรวจสอบช่องทางทันที ส่วนกำหนดเวลารายงานอัตโนมัติทำผ่าน cron (ส่วน “งาน cron ทั้งหมด”): มันจะเรียกการส่ง ส่วนช่องทางดึงมาจากการตั้งค่า

สถานะรายงาน: “เตือน” หรือ “ปกติ”. หัวข้อจะกลายเป็น “เตือน” เฉพาะเมื่อมีปัญหาจริงหรือมีการดำเนินการที่รออยู่: พบภัยคุกคามจาก ClamAV, ไฟล์เปลี่ยนแปลงใน AIDE, เหตุการณ์วิกฤติจาก Falco (Emergency/Alert/Critical ใน 24 ชม.ล่าสุด), บริการล่มใน Monit, ต้องรีบูต, SSL ใกล้หมดอายุ (≤14 วัน) หรือมีการอัปเดตความปลอดภัยรออยู่ ส่วนสัญญาณรบกวนพื้นหลัง — การเดารหัส SSH ของบอท, IP ที่ fail2ban แบนไว้, การแจ้งเตือนของ Suricata, คำเตือนของ Lynis และคำขอที่ ModSecurity สกัดไปแล้ว — จะไม่ ยกระดับสถานะ ดังนั้นตัวเลขเหล่านี้ในรายงานจึงไม่ได้หมายถึง “เตือน” ด้วยตัวมันเอง

07. ลิขสิทธิ์ — การกรอกและเปิดใช้งาน

โมดูลตรวจสอบแบบละเอียด (Lynis, UFW, ModSecurity, แผนที่การโจมตี, AIDE, ClamAV และอื่น ๆ) จะเปิดใช้งานได้เมื่อมีลิขสิทธิ์ที่ใช้งานได้ หากไม่มี แดชบอร์ด การตั้งค่า และบัญชีผู้ใช้จะยังทำงานได้ แต่โมดูลจะแสดงการ์ด “ต้องมีลิขสิทธิ์”

หลังจากซื้อในบัญชีผู้ใช้ คุณจะได้รับรหัสเปิดใช้งานรูปแบบ ARCIVEO-XXXX-XXXX-XXXX-XXXX คุณต้อง “เปิดใช้งาน” กับโดเมนของแผงควบคุมคุณ ซึ่งจะแปลงรหัสเป็นไฟล์ลิขสิทธิ์ที่ลงลายเซ็น (บล็อก [license]) เพื่อนำไปวางในแผงควบคุม

วิธีเปิดใช้งาน (3 ขั้นตอน):

  1. นำรหัสเปิดใช้งานมา บัญชีผู้ใช้ my.arciveo.com → หัวข้อ “ลิขสิทธิ์” / “เปิดใช้งานลิขสิทธิ์” — คัดลอกรหัส ARCIVEO-…
  2. เปิดใช้งานรหัสกับโดเมนของคุณ ในบัญชีผู้ใช้เดียวกัน ให้เปิด “เปิดใช้งานลิขสิทธิ์” แล้วกรอก: รหัสเปิดใช้งาน อีเมลของคุณ และโดเมนของแผงควบคุม (ที่อยู่ที่ใช้เปิด monitor เช่น 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/Bangkok'); // เขตเวลาของคุณ // --- เวลาเซสชัน --- define('SESSION_LIFETIME', 28800); // เวลาว่างก่อนต้องล็อกอินใหม่ วินาที (28800 = 8 ชม.)

ฐานข้อมูล. ข้อมูลการเชื่อมต่อ MySQL/MariaDB:

  • DB_HOST — โฮสต์ของ DBMS เกือบทุกครั้งคือ localhost;
  • DB_NAME — ชื่อฐานข้อมูลของแดชบอร์ด;
  • DB_USER — ผู้ใช้ฐานข้อมูล (เข้าถึงได้เฉพาะฐานข้อมูลของตัวเอง);
  • DB_PASS — รหัสผ่านของผู้ใช้รายนี้;
  • DB_CHARSET — การเข้ารหัสของการเชื่อมต่อ ให้คงไว้ที่ utf8mb4.

แอปพลิเคชัน.

  • APP_URL — ที่อยู่เต็มของแดชบอร์ด (เช่น https://monitor.example.com) ต้องตรงกับโดเมนที่เปิดใช้งานลิขสิทธิ์ไว้ มิฉะนั้นคีย์จะถูกปฏิเสธ (ดูหัวข้อ “ลิขสิทธิ์”);
  • TIMEZONE — เขตเวลาของ PHP: มีผลเฉพาะกับวิธีที่แดชบอร์ดแสดงวันที่และเวลาเท่านั้น ไม่มีผลต่อเวลาที่รันงาน cron — ตรงนั้นใช้เขตเวลาของระบบ (ดู “งาน cron ทั้งหมด”).

เวลาเซสชัน. SESSION_LIFETIME — เวลาหมดอายุของเซสชันขณะไม่มีการใช้งาน หน่วยวินาที (แบบเลื่อน: รีเซ็ตเมื่อมีกิจกรรม) ค่าเริ่มต้น 28800 = 8 ชั่วโมง หลังจากไม่มีการใช้งานเกินเวลานี้ แดชบอร์ดจะขอให้ล็อกอินใหม่ ตัวอย่างเช่น 3600 = 1 ชั่วโมง, 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 — เซิร์ฟเวอร์จะประมวลผลมัน ไม่ใช่ส่งออกเป็นข้อความ เผื่อไว้: อย่าอัปโหลดขึ้น repository สาธารณะ และอย่าส่งให้ฝ่ายสนับสนุนพร้อมรหัสผ่านจริง สิทธิ์ของไฟล์คือ 640.
เมื่อย้ายเซิร์ฟเวอร์หรือกู้คืนการเข้าถึง ไฟล์นี้คือแหล่งข้อมูลหลักของข้อมูลการเชื่อมต่อ: ชื่อฐานข้อมูล ผู้ใช้ และรหัสผ่านล้วนมาจากที่นี่ (ดูหัวข้อ “การอัปเดตและการย้ายแดชบอร์ด” และ “การกู้คืนการเข้าถึง”).

เครื่องมือความปลอดภัย

09. ไฟร์วอลล์ 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 แจ้งแบบนี้เพื่อบอกว่ามีกฎเดียวกันนี้อยู่แล้ว จึงไม่เพิ่มซ้ำ เมื่อรันการตั้งค่าอัตโนมัติซ้ำ (ซึ่งเป็นแบบ idempotent) ข้อความนี้เป็นเรื่องปกติ — ไม่ต้องดำเนินการใด ๆ

10. การติดตั้ง Fail2ban

บล็อก IP โดยอัตโนมัติเมื่อจำนวนครั้งที่พยายามเข้าสู่ระบบล้มเหลวเกินกำหนด วิเคราะห์ล็อกของ SSH, nginx, Apache และบริการอื่น ๆ

sudo apt install fail2ban sudo systemctl enable --now fail2ban # ตรวจสอบสถานะ: sudo fail2ban-client status
คอนฟิกที่ใช้งานจริง (jail.local ที่มี jail หลายสิบตัวและออโต้แบนจาก ipsum) อยู่ในหัวข้อถัดไป

11. คอนฟิกใช้งานจริงของ 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-cron (sudo crontab -e): level 1 (IP กว่า 100,000 รายการ) จะโหลดเข้าเซ็ต 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 — แม่นยำกว่า (3+ แหล่งข้อมูล)

ทำไม “Monitor ความปลอดภัย” จึงแบ่งเป็นสองโซน การป้องกันทำงานสองระดับ และแดชบอร์ดไม่ปนกัน:

  • การโจมตีจริง (เชิงรับ) — ทุกอย่างที่ fail2ban จับได้: ความพยายามเจาะระบบจริง (jail sshd, apache-*, nginx-* ฯลฯ) และ ผู้กระทำผิดซ้ำร้ายแรง (jail recidive — ผู้ที่โดนแบนมาแล้วหลายครั้ง) นี่คือ IP ที่บุกเข้ามาหาคุณ จริง ๆ — อยู่บนแผนที่การโจมตีและ “ไทม์ไลน์”
  • บล็อกเชิงป้องกัน (เชิงรุก) — บล็อกลิสต์สาธารณะของ IP อันตรายที่รู้จักกันดี ipset ipsum ตัดทิ้ง ที่ไฟร์วอลล์ ด้วยกฎ DROP ที่อยู่เหล่านี้ส่วนใหญ่ไม่เคยแตะเซิร์ฟเวอร์ของคุณเลย — ถูกตัดล่วงหน้า ตัวนับ “IPset ipsum” แสดงจำนวนที่ตัดไปเชิงป้องกัน

ความต่างง่าย ๆ: เชิงรับ — “พวกนี้โจมตีแล้วโดนแบน”, เชิงรุก — “พวกนี้ถูกบล็อกก่อนจะได้ลงมือ” เมื่อก่อนใน recidive จะยัด list-3 ipsum เข้าไปแบบเทียม (จึงมีการแบ่งแบบเก่า “recidive จากรายการ”); ตอนนี้ recidive มีเฉพาะผู้กระทำผิดซ้ำจริง ๆ ส่วนเชิงรุกอยู่ที่ไฟร์วอลล์ทั้งหมด

12. บล็อกลิสต์ IPset (ipsum)

ipsum — รายการ IP อันตรายสาธารณะที่อัปเดตทุกวัน มอนิเตอร์จะแสดงจำนวนที่อยู่ที่โหลดมาบนแดชบอร์ดและแผนที่การโจมตี และนำมาคิดในคะแนนความปลอดภัย (−10 หากยังไม่ได้โหลดชุดนี้)

ตัวเลือกขั้นต่ำที่ไม่ใช้ 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 อยู่ในหน่วยความจำและจะหายไปเมื่อรีบูต การใช้เพียงครอนรายวันจะทำให้ชุดว่างเปล่าตั้งแต่ตอนรีบูตจนถึงการรันครั้งถัดไป (แดชบอร์ดจะแสดง 0) จึงควรโหลดชุดตอนบูตด้วย — แยกการโหลดออกเป็นสคริปต์แล้วผูกกับ @reboot พร้อมกันนั้นคำสั่ง create … -exist จะกำหนดลิมิต maxelem 300000 (ค่าเริ่มต้น 65536 — 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 ใหม่ — โปรไฟล์ “เต็มรูปแบบ”/“แบบเบา”) ก็จะเชื่อมชุดนี้เข้ากับ UFW ด้วยกฎ DROP — ทราฟฟิกจาก IP เหล่านี้จะถูกบล็อกจริง กฎนี้อยู่ หลัง ESTABLISHED,RELATED ดังนั้นการเชื่อมต่อปัจจุบัน (รวมถึง SSH ของคุณ) จะไม่ขาด — จะตัดเฉพาะการเชื่อมต่อใหม่จากรายการเท่านั้น ชุดจะถูกกู้คืนเมื่อบริการ ipsum-load.service โหลด ก่อน ไฟร์วอลล์ (มิฉะนั้น UFW จะไม่ทำงาน) และอัปเดตด้วยครอนเวลา 04:00 บน เซิร์ฟเวอร์ที่ตั้งค่าไว้แล้ว (มีแผงควบคุม, ไฟร์วอลล์ของตัวเอง) ตัวติดตั้งจะไม่ยุ่งกับไฟร์วอลล์ — ตรงนั้น ipsum จะยังเป็นเพียงรายการสำหรับแดชบอร์ดและแผนที่การโจมตี ส่วนกฎ DROP หากต้องการก็เพิ่มเองได้ (ตัวเลือกขั้นต่ำด้วย iptables … --match-set ipsum … -j DROP — ด้านบน) เมื่อติดตั้งอัตโนมัติไม่ต้องทำอะไรเองเลย

13. การติดตั้ง CrowdSec

ตัวแทน Fail2ban สมัยใหม่ที่มาพร้อม threat intelligence แบบรวมกลุ่ม: ทั้งการบล็อกจากชุมชนและกฎของคุณเอง ต้องใช้ 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)
“0 สถานการณ์” หรือ “0 bouncers” บนแดชบอร์ด 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-key ที่ไร้เจ้าของ: 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 ด้วยตนเองเท่านั้น

14. การติดตั้ง AIDE

AIDE (Advanced Intrusion Detection Environment) จะสร้างสแนปช็อตของระบบไฟล์และรายงานการเปลี่ยนแปลงใน /etc, /bin, /usr ในทุกครั้งที่ตรวจสอบ หลังการติดตั้งต้องเริ่มต้นฐานข้อมูล (aideinit) เสมอ

sudo apt install aide # เริ่มต้นฐานข้อมูล (5–15 นาที): 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... นาน 5–15 นาที — เป็นเรื่องปกติ (การแฮชทั้งระบบไฟล์ ทำให้ดิสก์ทำงานหนัก) อย่ากด Ctrl+C หยุด หากกระบวนการ “ค้าง” แต่ไม่แสดงอะไร — อาจกำลังรอคำตอบต่อคำถามที่ซ่อนอยู่ Overwrite existing aide.db.new [Yn]? (ให้กด Y) ตรวจสอบการทำงานจากอีกเซสชัน: pgrep -af aide
ข้อผิดพลาด aideinit: “21_aide_spamassassin … printf: invalid number” (return code 20) — เป็นบั๊กที่รู้จักกันของส่วน config ของ 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 ก็ไม่มีอยู่ในนั้นแล้ว) วิธีที่แน่นอนกว่าคือเพิ่ม cron ของคุณเองพร้อมระบุ --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 และ cron ตรวจสอบเวลา 02:00 — ไม่ต้องทำอะไรด้วยตนเอง
ให้ทำการเริ่มต้นครั้งแรกบนเซิร์ฟเวอร์ที่สะอาด — ก่อนติดตั้งเว็บแอปพลิเคชัน หลังจากมีการเปลี่ยนแปลงที่ถูกต้อง ให้สร้างฐานข้อมูลใหม่: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db

15. การติดตั้ง ClamAV

โปรแกรมสแกนไวรัสสำหรับ Linux มีประโยชน์อย่างยิ่งในการตรวจสอบ /var/www เพื่อหา PHP-shell และโค้ดอันตราย

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 โหลดลายเซ็นราว 8 ล้านรายการเข้าหน่วยความจำใช้เวลา 30–60 วินาที รอสักครู่แล้วตรวจสอบ: 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

16. การติดตั้ง Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — โปรแกรมสแกนมัลแวร์ที่เน้นภัยคุกคามบนเว็บ: PHP shell, เว็บแบ็คดอร์, ตัวดาวน์โหลด ใช้เอนจินของ 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 — ดูหัวข้อ “หน้าเว็บว่างเปล่าทั้งที่มีข้อมูลบนเซิร์ฟเวอร์”

17. การติดตั้ง 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 “ทำงานอยู่” แต่แดชบอร์ดไม่แสดงการแจ้งเตือน / จำนวนเหตุการณ์เป็น 0? Suricata เขียนไฟล์ /var/log/suricata/eve.json ในสิทธิ์ root โดยตั้งไดเรกทอรีเป็นโหมด 750 ทำให้เว็บเซิร์ฟเวอร์ (www-data) อ่านไม่ได้ ให้เปิดสิทธิ์เข้าถึงไดเรกทอรี ส่วนไฟล์ภายในยังคงถูกป้องกันอยู่:
sudo chmod o+rx /var/log/suricata
การติดตั้งอัตโนมัติจะจัดการให้เอง ไม่ต้องทำด้วยตนเอง

18. การติดตั้ง Falco

ดักจับ system call ผ่าน 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
Monitor อ่านเหตุการณ์ของ Falco ผ่าน journalctl -u falco (ไม่ต้องใช้ sudo — ผ่านกลุ่ม systemd-journal) ตรวจสอบว่า www-data อยู่ในกลุ่มนี้ — ดู “การตั้งค่า sudo” (ข้อ 2) ในหน้าการติดตั้งด้วยตนเอง
“0 เหตุการณ์ใน 24 ชั่วโมง” เป็นเรื่องปกติ ไม่ใช่ข้อผิดพลาด Falco ทำงานแบบ event-driven: มันจะเงียบเมื่อทุกอย่างปกติ และบันทึกเหตุการณ์เฉพาะเมื่อมีความผิดปกติ (shell จากเว็บโปรเซส, การอ่าน /etc/passwd, การเขียนลงไดเรกทอรีระบบ) ศูนย์เหตุการณ์วิกฤตในหนึ่งวันบนเซิร์ฟเวอร์ที่สงบ คือสภาวะที่ปกติดี
สำหรับแดชบอร์ด การส่งออกเป็นไฟล์เชื่อถือได้กว่า การอ่านผ่าน journalctl ต้องมีสิทธิ์เข้าถึง journal; เพื่อให้แดชบอร์ดเห็นเหตุการณ์ได้อย่างเสถียร การติดตั้งอัตโนมัติจะเปิด file_output ของ Falco → /var/log/falco/falco.log และตั้งค่า UMask=0022 ให้บริการ (เว็บเซิร์ฟเวอร์อ่านล็อกได้) สำหรับการติดตั้งใหม่ ไม่จำเป็นต้องตั้งค่านี้ด้วยตนเอง

19. การติดตั้ง ModSecurity (WAF)

ModSecurity — เว็บไฟร์วอลล์ (WAF) สำหรับ Apache หรือ Nginx บล็อกการโจมตีในระดับแอปพลิเคชัน: SQL injection, 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 # ตรวจสอบ: ควรได้ผลลัพธ์ 403 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) เว็บยูสเซอร์อ่านไม่ได้ แดชบอร์ดดึงข้อมูลผ่านตัวห่อ (wrapper) — ให้สร้างมันขึ้นมา:

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 pool: บน Apache/Debian ทั่วไปคือ www-data ส่วนใน HestiaCP พูลของไซต์ทำงานจากเจ้าของไซต์ (เช่น admin) — ตรวจสอบด้วย grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf
หากไซต์อยู่หลัง Nginx proxy (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 แล้วเติมลงในรายการ หากตัวห่อเป็นเวอร์ชันเก่า (ไม่มีส่วนนี้) — ส่วนดังกล่าวจะแสดงเพียงคำเตือนว่า “ไม่พร้อมใช้งาน” ส่วนที่เหลือของหน้ายังทำงานเหมือนเดิม

20. การติดตั้ง Auditd

Auditd (Linux Audit Daemon) บันทึกการเรียกใช้ระบบในระดับเคอร์เนล: การเข้าและออกจากระบบ, คำสั่ง sudo, ความพยายามยืนยันตัวตนที่ล้มเหลว, การเปลี่ยนแปลงไฟล์ Monitor จะแสดงการเข้าสู่ระบบ, ความพยายามที่ล้มเหลว และคำสั่ง sudo ของวันนี้

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # ตรวจสอบสถานะและเหตุการณ์: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Monitor อ่านเหตุการณ์ผ่าน ausearch (/usr/sbin/ausearch) และเมื่อจำเป็นจะอ่านจาก /var/log/audit/audit.log ด้วยคำสั่ง tail ทั้งสองต้องอยู่ใน sudoers

21. การติดตั้ง Monit

เฝ้าดูบริการต่าง ๆ (nginx, php-fpm, mysql ฯลฯ) และรีสตาร์ตให้เมื่อบริการล่ม สามารถส่งการแจ้งเตือนทางอีเมลได้

sudo apt install monit sudo systemctl enable --now monit # ไฟล์คอนฟิก: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Monitor ดึงรายการบริการผ่าน monit status ใน /etc/monit/monitrc ต้องเปิดใช้อินเทอร์เฟซ HTTP (บล็อก set httpd พร้อม allow localhost) มิฉะนั้น monit status จะคืนค่าเป็นข้อผิดพลาด
บนแดชบอร์ดขึ้น “0 บริการที่เฝ้าดู” ใช่ไหม? มีสองสาเหตุ (1) อินเทอร์เฟซ HTTP ปิดอยู่ — ใน monitrc บรรทัด set httpd ถูกคอมเมนต์ไว้ (ค่าเริ่มต้นมาเป็น # set httpd port 2812 …) ให้ยกเลิกคอมเมนต์บล็อกนี้และอนุญาต localhost (2) การเปิด 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 บนพอร์ต 2812 และชุดการตรวจสอบไว้ให้ — บนการติดตั้งใหม่ไม่ต้องตั้งค่าเองด้วยมือ
บริการอยู่ในสถานะ “มีข้อผิดพลาด” ใช่ไหม? Monitor เพียงแสดงสถานะเท่านั้น และตั้งใจไม่รีสตาร์ตบริการจากเว็บแผงควบคุม (เพราะนั่นจะเป็นการรันคำสั่ง 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

22. การติดตั้ง PSAD (ตรวจจับการสแกนพอร์ต)

PSAD วิเคราะห์ล็อกของ iptables และตรวจจับการสแกนพอร์ตกับการโจมตีเครือข่าย โดยกำหนดระดับภัยคุกคาม (1–5) ให้แต่ละแหล่งที่มา เสริมการทำงานของ 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 หน้านี้จะว่างเปล่า — ถือเป็นเรื่องปกติตราบใดที่ยังไม่มีการสแกน

23. 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 # ตรวจสอบโปรไฟล์
Monitor อ่านสถานะผ่าน aa-status (ต้องอยู่ใน sudoers) แสดงจำนวนโปรไฟล์ในโหมด enforce/complain และโปรเซสที่ไม่มีโปรไฟล์

“โปรไฟล์ที่โหลด” มากกว่า enforce + complain ถือเป็นเรื่องปกติ ใน AppArmor 4.x (Ubuntu 24.04 ขึ้นไป) มีโหมด unconfined เพิ่มเข้ามา คือโปรไฟล์ถูกโหลดเข้าเคอร์เนลแล้วแต่ไม่ได้จำกัดอะไร Ubuntu ตั้งค่าแบบนี้ให้กับโปรไฟล์นับสิบสำหรับโปรแกรมที่ใช้ user namespaces (เบราว์เซอร์ ไคลเอนต์ torrent และอื่น ๆ ทำนองนั้น) เมื่อมีโปรไฟล์แบบนี้อยู่ การ์ด “โปรไฟล์ที่โหลด” จะเปลี่ยนเป็นสีเหลืองอำพันและแสดงจำนวน — เช่น unconfined: 90 จาก 120 ที่โหลดและ 26 ที่อยู่ใน 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 เป็นอีกเรื่องหนึ่ง คือมีการเขียนกฎไว้แล้วเพียงแต่ยังไม่ได้บังคับใช้

24. การติดตั้ง debsums (ความสมบูรณ์ของแพ็กเกจ)

debsums ตรวจสอบว่าไฟล์ของแพ็กเกจที่ติดตั้งตรงกับ checksum จากรีโพซิทอรี — เพื่อตรวจจับไบนารีระบบที่ถูกแทนที่ (เสริมกับ AIDE) การตรวจสอบเต็มรูปแบบใช้เวลา 1–2 นาที จึงถูกรันผ่าน cron ส่วนแดชบอร์ดจะอ่านผลลัพธ์จาก data/debsums/debsums.log และจัดหมวดหมู่ให้เอง (สำคัญเฉพาะไบนารีและไลบรารี)

งานนี้อยู่ใน root cron (sudo crontab -e) สคริปต์ครอบสำเร็จรูป debsums-scan.sh จะวางไว้ใน /usr/local/bin/ (chmod +x; ดูสรุปงาน cron) และเขียนรายงานลงใน data/debsums/ ของแดชบอร์ดเอง

sudo apt install debsums # บรรทัด cron (ทุกวันเวลา 4:30): 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 เป็นต้น) — การ์ด “ไบนารี / ไลบรารี” จะแสดงเฉพาะรายการเหล่านั้น

25. การตั้งค่ารายงาน 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

26. การตั้งค่ารายงาน Logwatch

Logwatch ต้องบันทึกรายงานประจำวันลงในโฟลเดอร์ data/logwatch/ ของโปรเจกต์ในรูปแบบ .txt โดย Monitor จะแสดงรายงานล่าสุดและคลังรายงาน

# รายวัน (6:00) — บรรทัด cron (สคริปต์ครอบ logwatch_daily.sh พร้อมใช้งานใน /usr/local/bin/ ดูสรุป): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

โมดูลแดชบอร์ด

27. การเฝ้าระวังเครือข่าย (ในตัว)

การเฝ้าระวังเครือข่ายไม่ต้องติดตั้ง — เป็นหน้าในตัวของแดชบอร์ด แสดงสถานะเครือข่ายของเซิร์ฟเวอร์จากแหล่งข้อมูลในเครื่อง:

  • อินเทอร์เฟซและทราฟฟิก — จาก /proc/net/dev;
  • สถานะลิงก์ (UP/DOWN) และ IP — ผ่าน ip;
  • การเชื่อมต่อและพอร์ตที่รับฟัง — ผ่าน ss;
  • เหตุการณ์เครือข่ายของเคอร์เนลใน 24 ชม. — ผ่าน journalctl -k

แหล่งข้อมูลสามอย่างแรกทำงาน โดยไม่ต้องใช้ sudo จึงเห็นอินเทอร์เฟซ ทราฟฟิก การเชื่อมต่อ และพอร์ตได้ทันที ส่วนบล็อก “เหตุการณ์เคอร์เนล” ใช้ journalctl -k ซึ่งอ่านผ่านกลุ่ม systemd-journal (“การตั้งค่า sudo” ข้อ 2) ไม่ต้องใช้ 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, ข้อผิดพลาดของสัญญาณ carrier, “network unreachable”) รายการไฟร์วอลล์ UFW BLOCK จะไม่ปรากฏที่นี่ — อยู่ในหน้า “ไฟร์วอลล์ UFW” และ “แผนที่การโจมตี” บล็อกว่างพร้อมเครื่องหมายถูกสีเขียว = ในรอบวันไม่มีความขัดข้องของเครือข่าย

28. ดิสก์และ SMART

หน้านี้แสดงข้อมูลสามอย่าง:

  • ระบบไฟล์ — พื้นที่ที่ใช้ในแต่ละพาร์ทิชัน (df); แถบจะเป็นสีแดงเมื่อ ≥90%;
  • อุปกรณ์จัดเก็บข้อมูล — รายการดิสก์ (lsblk) เฉพาะที่มีอยู่จริง (ซ่อน loop/snap);
  • สุขภาพ (SMART) — สถานะและแอตทริบิวต์ของดิสก์ (smartctl)

พื้นที่และรายการอุปกรณ์ทำงานได้ทันทีโดยไม่ต้องตั้งค่า สำหรับ SMART ต้องมีแพ็กเกจ smartmontools เว็บโพรเซสไม่มีสิทธิ์เข้าถึงอุปกรณ์ดิสก์โดยตรง จึงต้องเก็บข้อมูล SMART ผ่าน cron ลงในไฟล์ data/disk/smart.txt แล้วแดชบอร์ดจะอ่านไฟล์นั้น

งานนี้อยู่ใน root cron (sudo crontab -e) สคริปต์ห่อ smart-scan.sh ที่เตรียมไว้จะวางไว้ใน /usr/local/bin/ (chmod +x; ดูสรุปงาน cron) และเขียนลงใน data/disk/ ของแดชบอร์ดเอง

sudo apt install smartmontools # บรรทัด cron (ทุก 30 นาที): */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” ส่วนอุณหภูมิ ชั่วโมงการทำงาน และเซกเตอร์ที่ถูกจัดสรรใหม่อาจว่างเปล่า ซึ่งเป็นเรื่องปกติ บนเซิร์ฟเวอร์จริงจะแสดงแอตทริบิวต์ทั้งหมด

29. ประสิทธิภาพ (CPU/RAM/เครือข่าย/ดิสก์)

หน้านี้แสดงประวัติโหลดของเซิร์ฟเวอร์ในช่วง 24 ชั่วโมงที่ผ่านมา — Load Average, การใช้งาน CPU และการรอ I/O, RAM/Swap, ทราฟฟิกเครือข่าย (รับ/ส่ง), ดิสก์ I/O (อ่าน/เขียน), พื้นที่ดิสก์และ inodes ที่ใช้ไป, file descriptor ที่เปิดอยู่และการเชื่อมต่อ MySQL รวมถึงจำนวนการเชื่อมต่อ TCP และโปรเซสในปัจจุบัน

ข้อมูลถูกเก็บโดย cron/collect_metrics.php — ทุก 5 นาทีจะบันทึกสแนปช็อต “ดิบ” ของตัวนับหนึ่งชุด (/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 ของฐานข้อมูล ส่วนเปอร์เซ็นต์และอัตราความเร็วนั้นหน้านี้คำนวณเองจากส่วนต่างระหว่างสแนปช็อตที่อยู่ติดกัน (พื้นที่ดิสก์/inodes/descriptor/การเชื่อมต่อ MySQL เป็นค่า ณ ขณะนั้น ไม่มีการคำนวณใหม่) ไม่ต้องใช้ sudo — แหล่งข้อมูลอ่านได้โดยไม่ต้องมีสิทธิ์ root จุดข้อมูลที่เก่ากว่า 24 ชั่วโมงจะถูกลบอัตโนมัติทุกครั้งที่มีการบันทึก

# บรรทัด cron (ทุก 5 นาที): */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 ของแต่ละอินสแตนซ์ในนามของเจ้าของเว็บไซต์

จนกว่าตัวเก็บข้อมูลจะทำงานอย่างน้อยสองครั้ง (~10 นาทีแรกหลังการติดตั้ง) หน้านี้จะแสดง “กำลังเก็บข้อมูล” — กราฟต้องมีจุดที่อยู่ติดกันอย่างน้อยหนึ่งคู่จึงจะคำนวณอัตราความเร็วและเปอร์เซ็นต์ได้

การแจ้งเตือนโหลด (หัวข้อ “การตั้งค่า” → “การแจ้งเตือนโหลด”) — เมื่อ CPU/RAM/ดิสก์/inodes เกินเกณฑ์ แดชบอร์ดจะส่งการแจ้งเตือนไปยัง Telegram/Email (ช่องทางเดียวกับรายงานประจำวัน — ไม่ต้องเปิดใช้แยกสำหรับการแจ้งเตือน) และส่งอีกครั้งเมื่อค่ากลับสู่ปกติ จะไม่ส่งซ้ำระหว่างที่ค่ายังเกินเกณฑ์อยู่: การแจ้งเตือนครั้งถัดไปจะมาหลังจากรอบ “กลับสู่ปกติ → เกินเกณฑ์อีกครั้ง” เท่านั้น

เกณฑ์จะถูกตรวจสอบโดย collect_metrics.php ตัวเดียวกันทุกครั้งที่รัน (ทุก 5 นาที) — ไม่ต้องใช้ cron แยก สถานะ “แจ้งเตือนไปแล้ว / ยังไม่แจ้ง” เก็บไว้ใน data/alerts_state.json ส่วนเกณฑ์อยู่ในการตั้งค่าของแดชบอร์ด

30. แผนที่การโจมตี (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) จะโหลดในเบราว์เซอร์ — จึงต้องมีอินเทอร์เน็ตบนเครื่องที่เปิดแดชบอร์ด

31. การเปิดเผยสู่ภายนอก การอัปเดต และการอัปเดตอัตโนมัติ

การ์ดในตัวสองใบบนแดชบอร์ด ที่แสดงไม่ใช่แค่ “เปิด/ปิด” ของเครื่องมือ แต่แสดงความปลอดภัยจริงของเซิร์ฟเวอร์ ไม่ต้องติดตั้งเพิ่ม อ่านค่าในเครื่องได้โดยไม่ต้องใช้ sudo

การเปิดเผยสู่ภายนอก — มีบริการกี่ตัวที่รับฟังทุกอินเทอร์เฟซ (0.0.0.0/[::]) และเข้าถึงได้จากภายนอก จะขึ้นสีแดงหากมีฐานข้อมูลหรือแคช (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) เปิดออกสู่ภายนอก — นี่คือช่องโหว่โดยตรง (−10 ต่อคะแนนความปลอดภัย) แหล่งข้อมูล: ss -tuln

หากการ์ดเป็นสีแดง — ให้ปิดฐานข้อมูลจากโลกภายนอก: ผูกไว้กับ 127.0.0.1 (bind-address ในคอนฟิก MySQL/PostgreSQL, bind 127.0.0.1 ใน Redis) หรือปิดพอร์ตใน UFW
“พอร์ตเปิด” ≠ “เข้าถึงได้จากภายนอก” บริการที่รับฟัง 127.0.0.1 (loopback) จะมองเห็นได้เฉพาะจากตัวเซิร์ฟเวอร์เอง — เข้าถึงจากภายนอกไม่ได้ ถึงแม้พอร์ตจะ “เปิด” ก็ตาม ดังนั้น Postfix บนพอร์ต 25 ที่ผูกกับ 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 (ไม่มีเวอร์ชันและ OS) และ inet_interfaces = loopback-only จากนั้น sudo systemctl restart postfix

การอัปเดตความปลอดภัย — มีแพตช์ความปลอดภัยกี่รายการที่รอการติดตั้ง และต้องรีบูตหรือไม่หลังอัปเดตเคอร์เนล (−5 ต่อคะแนนความปลอดภัยเมื่อมีแพตช์ค้าง) แหล่งข้อมูล: /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

การบำรุงรักษา

32. การสำรองข้อมูล

การสำรองข้อมูลคือหลักประกันสำคัญที่สุด เพราะการสูญเสียข้อมูลร้ายแรงกว่าการถูกเจาะระบบใดๆ คุณต้องมีสองอย่าง — สำรองข้อมูลเซิร์ฟเวอร์/เว็บไซต์ และแยกต่างหากคือ สำรองฐานข้อมูลของแดชบอร์ด (ที่นั่นมีผู้ใช้ คีย์ WebAuthn การตั้งค่า และใบอนุญาต)

ตัวเลือก A — HestiaCP: แท็บ Backup ของผู้ใช้ → ปุ่มสร้างการสำรองข้อมูล (หรือตามกำหนดเวลาในการตั้งค่าเซิร์ฟเวอร์) การสำรองข้อมูลจะรวมเว็บไซต์และฐานข้อมูลของเว็บไซต์

ตัวเลือก B — ด้วยตนเอง (cron): ดัมป์ฐานข้อมูล + บีบอัดไดเรกทอรี data/ ของแดชบอร์ด:

# root-cron (sudo crontab -e) — สำรองข้อมูลรายวันเวลา 2:30 (ใส่ชื่อ/พาธของคุณเอง): 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 # ลบไฟล์ที่เก่ากว่า 14 วัน: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
การสำรองข้อมูลบนเซิร์ฟเวอร์เดียวกันช่วยกู้จากความผิดพลาดได้ แต่ไม่ช่วยเมื่อสูญเสียทั้งเซิร์ฟเวอร์ ให้คัดลอกไฟล์สำรอง ไปยังที่จัดเก็บภายนอก (เซิร์ฟเวอร์อื่น, S3, rclone ไปยังคลาวด์) และตรวจสอบว่าการกู้คืนใช้งานได้จริง

33. การอัปเดตและการย้ายแดชบอร์ด

การอัปเดตเป็นเวอร์ชันใหม่ ก่อนอื่นให้สำรองข้อมูล จากนั้นอัปโหลดไฟล์โค้ดทับ โดยคงข้อมูลของคุณไว้:

  • เขียนทับ (โค้ด): public/, includes/, assets/, cron/, database/ รวมถึง .htaccess ที่รูท (front controller — ห้ามคงการกำหนดเส้นทางจากเวอร์ชันเก่า), manifest.json, sw.js;
  • ห้ามแตะ: config.php (ข้อมูล DB), data/ (รายงาน), logs/, tmp/ (เซสชันและแคช)
# หลังอัปโหลด — ล้างแคช PHP (หากเปิดใช้ opcache): sudo systemctl reload php*-fpm
FileZilla แจ้ง SSH_FX_PERMISSION_DENIEDPermission 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-mask และการเข้าถึงจะหายไปเงียบ ๆ หากหลังจาก “จัดระเบียบสิทธิ์” แล้วการอัปโหลดยังติด 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. ย้าย DB: mysqldump บนเครื่องเก่า → นำเข้าบนเครื่องใหม่ แล้วแก้ไขข้อมูล DB ใน config.php
  4. ทำซ้ำบนเซิร์ฟเวอร์ใหม่: sudoers, การเป็นสมาชิกกลุ่ม adm, งาน cron
  5. ใบอนุญาตผูกกับโดเมน — หากใช้โดเมนเดิม คีย์จะยังใช้งานได้ต่อไป

34. กู้คืนการเข้าถึง (สูญเสียคีย์ รหัสผ่าน หรือถูกบล็อก 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 อีกครั้ง

35. งาน cron ทั้งหมดในที่เดียว

สรุปรายการงานอยู่ใน 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 # ทุก 30 นาที — ตรวจสถานะดิสก์ 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 # ทุก 5 นาที — บันทึกสแนปช็อตทรัพยากร (CPU/RAM/เครือข่าย/ดิสก์) สำหรับหน้า “ประสิทธิภาพ” */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
รายละเอียดของแต่ละรายการอยู่ในหัวข้อที่เกี่ยวข้อง งานสำรองข้อมูล (หัวข้อก่อนหน้า) จะเพิ่มลงใน cron ชุดเดียวกันนี้ หลังแก้ไขให้ตรวจสอบด้วย: sudo crontab -l และตรวจว่าเซอร์วิส cron ทำงานอยู่
เวลา cron = โซนเวลาของเซิร์ฟเวอร์ ไม่ใช่ TIMEZONE จาก config.php ค่าคงที่ TIMEZONE ส่งผลต่อ PHP เท่านั้น (วิธีที่แดชบอร์ดแสดงวันที่) แต่ cron-daemon จะรันงานตามเวลาระบบของ OS หากโซนเวลาของเซิร์ฟเวอร์ไม่ตรงกับของคุณ รายงาน “08:00” จะมาผิดเวลา ตัวอย่าง: เซิร์ฟเวอร์อยู่คนละโซน (Asia/Dhaka, UTC+6) ส่วนคุณอยู่ในกรุงเทพฯ (UTC+7) → รายงาน “08:00” จะมาถึงเวลา 09:00 ตามเวลาคุณ ให้ตรวจสอบและปรับโซนเวลาระบบให้เป็นของคุณเมื่อจำเป็น:
# ตรวจโซนเวลาปัจจุบันของเซิร์ฟเวอร์: timedatectl # ตั้งโซนเวลาของคุณ (ตัวอย่าง) แล้วรีสตาร์ท cron: sudo timedatectl set-timezone Asia/Bangkok sudo systemctl restart cron
หลังจากนั้นบรรทัด 0 8 * * * จะทำงานเวลา 08:00 ตามเวลาท้องถิ่น มิฉะนั้นต้องเลื่อน cron เอง แต่เมื่อเปลี่ยนเวลาฤดูหนาว/ฤดูร้อน ค่าที่เลื่อนจะคลาดเคลื่อนอีก — จึงควรตั้งโซนเวลาระบบให้ถูกต้องดีกว่า
สคริปต์ครอบสำเร็จรูป สำเนาที่ใช้งานได้และตัวอย่าง crontab (crontab.txt) อยู่ในโฟลเดอร์ system/ ข้างโปรเจกต์ นอก public_html นี่ ไม่ใช่ส่วนของเว็บไซต์ — ไม่ต้องอัปโหลดเข้า web root ให้วางไว้บนเซิร์ฟเวอร์ตามพาธระบบ (เหมือน 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 มาพร้อมแดชบอร์ดอยู่แล้ว ไม่ต้องติดตั้งแยก;
  • 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 — ตรวจว่า PHP-FPM pool ของเว็บไซต์ทำงานภายใต้ผู้ใช้ใด (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

การวินิจฉัย

36. ติดตั้งเครื่องมือแล้ว แต่ระบบแจ้งว่า “ยังไม่ได้ติดตั้ง”

Monitor ตรวจหาเครื่องมือผ่าน 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

37. การแก้ปัญหา (500, ไม่มีข้อมูล)

ข้อผิดพลาด 500 — ตรวจสอบล็อกของ 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) ต้องกำหนดให้ผู้ใช้รายนี้ มิฉะนั้นโมดูลจะแสดง “ไม่ทำงาน / 0” ทั้งที่บริการทำงานอยู่ ดูผู้ใช้ 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
โมดูลแสดง “ไม่ทำงาน” / “0” ทั้งที่เครื่องมือทำงานอยู่ (เช่น 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 และเหตุการณ์ของเคอร์เนล)

38. หน้าว่างเปล่า ทั้งที่มีข้อมูลอยู่บนเซิร์ฟเวอร์

อาการ: บนเซิร์ฟเวอร์มีข้อมูลอยู่ (มองเห็นผ่าน shell) แต่หน้าเว็บแสดง “ไม่มีข้อมูล” หรือสถานะไม่ถูกต้อง เช่น AIDE ระบุว่า “ยังไม่ได้เริ่มต้นใช้งาน” ทั้งที่สร้างฐานข้อมูลแล้ว

สาเหตุคือ open_basedir: แผงควบคุมและโฮสติ้งจำนวนมากจำกัดพูล PHP-FPM ไว้เฉพาะไดเรกทอรีของโดเมน ทำให้ฟังก์ชัน PHP อย่าง file_exists(), file_get_contents(), filemtime() บนพาธของระบบ (/var/lib/aide, /var/log, /proc…) ถูกบล็อก Monitor แก้ปัญหานี้ด้วยการอ่านพาธเหล่านั้นผ่านคำสั่งของระบบตามปกติ (cat, test, stat)

# มองเห็นไฟล์ผ่าน shell หรือไม่ (Monitor อ่านแบบนี้): 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 และ Network Monitor) ไม่จำเป็นต้องขยาย open_basedir ไปยัง /var, /proc และยังปลอดภัยน้อยกว่าด้วย

39. หน้า SSL ไม่ทำงาน

Monitor ตรวจสอบใบรับรองโดยเชื่อมต่อกับโดเมนโดยตรงผ่านพอร์ต 443 หากไม่สามารถเข้าถึงโดเมนจากตัวเซิร์ฟเวอร์เอง หรือพอร์ตถูกไฟร์วอลล์ปิดกั้น การตรวจสอบจะไม่สำเร็จ

# ตรวจสอบใบรับรองด้วยตนเอง: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # ตรวจสอบการเข้าถึง: curl -I https://monitor.example.com
Monitor ดึงโดเมนมาอัตโนมัติจากไฟล์ตั้งค่าของ Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) และ Apache (/etc/apache2/sites-enabled/) พร้อมกับโฮสต์ปัจจุบันจาก HTTP_HOST
ตรวจหาซับโดเมนอัตโนมัติ ซับโดเมนจะถูกตรวจหาโดยอัตโนมัติจากบันทึกสาธารณะของ Certificate Transparency และตรวจสอบผ่านเครือข่าย แม้จะอยู่บนเซิร์ฟเวอร์อื่นก็ตาม ไม่ต้องเพิ่มด้วยตนเองแต่อย่างใด

40. เห็นฐานข้อมูลเพียงชุดเดียวจากหลายชุด

Monitor เชื่อมต่อกับ MySQL ด้วยผู้ใช้จาก config.php ซึ่งมีสิทธิ์เข้าถึงเฉพาะฐานข้อมูลของตัวเอง MySQL จะแสดงใน information_schema เฉพาะฐานข้อมูลที่มีสิทธิ์เท่านั้น จึงมองไม่เห็นฐานอื่น

เพื่อให้ monitor เห็น ฐานข้อมูลทั้งหมด ให้มอบสิทธิ์อ่านอย่างเดียวแก่ผู้ใช้รายนี้ (ทำครั้งเดียวจาก 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 ของตัวเอง

41. PostgreSQL ไม่แสดงบนหน้า “ฐานข้อมูล”

PostgreSQL ต้องใช้สิทธิ์ระดับผู้ใช้ postgres ซึ่งเว็บผู้ใช้ของแดชบอร์ดไม่มี การเปิด sudo psql แบบกว้างจาก PHP นั้นไม่ปลอดภัย แดชบอร์ดจึงเรียกใช้ตัวห่อหุ้มแบบจำกัดที่ไม่รับพารามิเตอร์แทน ซึ่งแสดงเพียงเวอร์ชัน จำนวนการเชื่อมต่อ และรายการฐานข้อมูลพร้อมขนาด สร้างมันขึ้นมา:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo 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 (ขั้นตอนที่ 13 ของการติดตั้งด้วยตนเอง) และไม่ต้องสร้างสคริปต์: การ์ด PostgreSQL จะยังคงไม่ทำงานเท่านั้น

42. เมื่อมีการแจ้งเตือน — ต้องทำอย่างไร

แดชบอร์ดแสดงสิ่งที่กำลังเกิดขึ้น ด้านล่างคือสิ่งที่ต้องทำในสถานการณ์ทั่วไป หลักการโดยรวม: อย่าตื่นตระหนก ตรวจสอบว่าเป็นกิจกรรมที่ถูกต้องหรือไม่ (การกระทำของคุณ การอัปเดต การสำรองข้อมูล) แล้วตอบสนองตามระดับความรุนแรง

  • แผนที่การโจมตี / มีการแบนจาก 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 ในคอนเทนเนอร์ การเข้าถึงไฟล์ที่อ่อนไหว) — วิเคราะห์เหตุการณ์: เป็นโปรเซสของใคร รันอะไร บ่อยครั้งเป็นกิจกรรมของแอดมินที่ถูกต้อง
  • การเปิดเผยสู่ภายนอก: DBMS/แคชขึ้นสีแดง — ปิดทันที: ผูกบริการเข้ากับ 127.0.0.1 หรือปิดพอร์ตใน UFW นี่คือช่องโหว่จริง
  • SSL กำลังจะหมดอายุ / หมดอายุแล้ว — ต่ออายุใบรับรอง (Let's Encrypt ต่ออายุเอง หากไม่ต่อ ให้ตรวจสอบ certbot renew หรือการตั้งค่าในแดชบอร์ด)
  • มีการอัปเดตความปลอดภัยรออยู่ — ติดตั้ง: sudo apt update && sudo apt upgrade หลังอัปเดตเคอร์เนลให้รีบูตเซิร์ฟเวอร์
สัญญาณของการถูกเจาะจริง (โปรเซส/ผู้ใช้ที่ไม่รู้จัก ไบนารีที่ถูกเปลี่ยนแปลง สแปมขาออก งาน cron ที่ไม่รู้จัก): ตัดเซิร์ฟเวอร์ออกจากการเข้าถึงภายนอก สำรองข้อมูลไว้เพื่อวิเคราะห์ และหากข้อมูลสำคัญมาก ให้สร้างเซิร์ฟเวอร์ใหม่ที่สะอาดจากการสำรองข้อมูลที่เชื่อถือได้ เพราะการกำจัดรูตคิตให้หมดจดนั้นทำได้ยาก
Arcivéo - Security Monitor © 2026