นี่คือคู่มือการติดตั้ง ตั้งค่า และดูแลรักษา Arcivéo Monitor เนื้อหาแบ่งเป็นกลุ่ม ได้แก่ ภาพรวมทั่วไป การติดตั้งแดชบอร์ด การเชื่อมต่อเครื่องมือ ด้านความปลอดภัย โมดูลในตัว และการวินิจฉัยปัญหา คุณคัดลอกคำสั่งได้ด้วยปุ่มทางขวา
การติดตั้งแดชบอร์ดอยู่ในหน้าคู่มือทีละขั้นตอนแยกต่างหาก เลือกวิธี:
Arcivéo Monitor — แดชบอร์ดความปลอดภัยของเซิร์ฟเวอร์ รวบรวมข้อมูลจากเครื่องมือที่ติดตั้งไว้ (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco และอื่น ๆ) และแสดงผลในอินเทอร์เฟซเดียว พร้อมแดชบอร์ด แผนที่การโจมตี และหน้ารายละเอียดของแต่ละเครื่องมือ
Monitor ไม่ใช่เครื่องมือป้องกันเชิงรุก — ตัวมันเองไม่ได้บล็อกการโจมตี หน้าที่ของมันคือรวบรวมข้อมูลจากเครื่องมือที่ทำงานอยู่แล้วและนำเสนอในรูปแบบที่เข้าใจง่าย
มอนิเตอร์ทำงานในเครื่องเท่านั้น — ต้องติดตั้งบนเซิร์ฟเวอร์เครื่องเดียวกับที่ตรวจสอบ ไม่มีการใช้ SSH หรือ API ระยะไกลใด ๆ
คำสั่งทั้งหมด (fail2ban-client, ufw status, ipset list ฯลฯ) แดชบอร์ดจะรันในนามผู้ใช้ของเว็บเซิร์ฟเวอร์ (โดยทั่วไปคือ www-data ส่วนบนแผงควบคุมโฮสติ้งจะเป็นบัญชีของเว็บไซต์) ด้วยชุดสิทธิ์ที่จำกัดของ sudo — เฉพาะยูทิลิตีที่ระบุเท่านั้น ไม่มีสิทธิ์ root แบบทั่วไป ผลลัพธ์จะถูกแยกวิเคราะห์และแสดงในเบราว์เซอร์
คะแนนเริ่มต้นที่ค่าสูงสุดและลดลงตามแต่ละปัญหาที่ตรวจพบ:
PermitRootLogin yes) — −20สรุป: 80+ = ปลอดภัย, 60–79 = ควรระวัง, <60 = เสี่ยง
WebAuthn — มาตรฐานการยืนยันตัวตนโดยไม่ใช้รหัสผ่านผ่านคีย์ฮาร์ดแวร์ รองรับ YubiKey, Touch ID, Face ID, Windows Hello, Passkey
หลังเข้าสู่ระบบด้วยรหัสผ่าน ระบบจะขอการยืนยันผ่านคีย์ที่ลงทะเบียนไว้ แม้รหัสผ่านจะรั่วไหล ก็เข้าสู่ระบบไม่ได้หากไม่มีคีย์กายภาพหรือไบโอเมตริก
ในการตั้งค่า ให้เปิด คีย์ WebAuthn ในเมนูด้านข้าง แล้วกด “ลงทะเบียนคีย์” ควรลงทะเบียนคีย์สองอันพร้อมกัน: หากคีย์เพียงอันเดียวสูญหายหรือเสีย จะไม่สามารถเข้าสู่แดชบอร์ดด้วยคีย์นั้นได้
แดชบอร์ดสามารถส่งรายงานความปลอดภัยไปยัง Telegram และอีเมลได้ (ทั้งด้วยปุ่มและตามกำหนดเวลา) ตั้งค่าได้ในส่วน “การตั้งค่า”
Telegram. ต้องใช้ โทเคนบอท และ chat id:
@BotFather → /newbot → รับ โทเคน รูปแบบ 123456:ABC...@userinfobot หรือเปิด https://api.telegram.org/bot<TOKEN>/getUpdates แล้วหา "chat":{"id":...}Email. เลือกได้สองวิธีใน “การตั้งค่า” → Email:
re_...) และโดเมนผู้ส่งที่ยืนยันแล้วสถานะรายงาน: “เตือน” หรือ “ปกติ”. หัวข้อจะกลายเป็น “เตือน” เฉพาะเมื่อมีปัญหาจริงหรือมีการดำเนินการที่รออยู่: พบภัยคุกคามจาก ClamAV, ไฟล์เปลี่ยนแปลงใน AIDE, เหตุการณ์วิกฤติจาก Falco (Emergency/Alert/Critical ใน 24 ชม.ล่าสุด), บริการล่มใน Monit, ต้องรีบูต, SSL ใกล้หมดอายุ (≤14 วัน) หรือมีการอัปเดตความปลอดภัยรออยู่ ส่วนสัญญาณรบกวนพื้นหลัง — การเดารหัส SSH ของบอท, IP ที่ fail2ban แบนไว้, การแจ้งเตือนของ Suricata, คำเตือนของ Lynis และคำขอที่ ModSecurity สกัดไปแล้ว — จะไม่ ยกระดับสถานะ ดังนั้นตัวเลขเหล่านี้ในรายงานจึงไม่ได้หมายถึง “เตือน” ด้วยตัวมันเอง
โมดูลตรวจสอบแบบละเอียด (Lynis, UFW, ModSecurity, แผนที่การโจมตี, AIDE, ClamAV และอื่น ๆ) จะเปิดใช้งานได้เมื่อมีลิขสิทธิ์ที่ใช้งานได้ หากไม่มี แดชบอร์ด การตั้งค่า และบัญชีผู้ใช้จะยังทำงานได้ แต่โมดูลจะแสดงการ์ด “ต้องมีลิขสิทธิ์”
หลังจากซื้อในบัญชีผู้ใช้ คุณจะได้รับรหัสเปิดใช้งานรูปแบบ ARCIVEO-XXXX-XXXX-XXXX-XXXX คุณต้อง “เปิดใช้งาน” กับโดเมนของแผงควบคุมคุณ ซึ่งจะแปลงรหัสเป็นไฟล์ลิขสิทธิ์ที่ลงลายเซ็น (บล็อก [license]) เพื่อนำไปวางในแผงควบคุม
วิธีเปิดใช้งาน (3 ขั้นตอน):
my.arciveo.com → หัวข้อ “ลิขสิทธิ์” / “เปิดใช้งานลิขสิทธิ์” — คัดลอกรหัส ARCIVEO-…monitor.example.com) กดเปิดใช้งาน — ระบบจะสร้างไฟล์ลิขสิทธิ์ที่ผูกกับโดเมนนี้ และแสดงในช่องพร้อมปุ่ม “คัดลอก”แผงควบคุมจะตรวจสอบคีย์ด้วยการเข้ารหัส: ลายเซ็น การผูกกับโดเมน และวันหมดอายุ
APP_URL ใน config.php แล้วกรอกเฉพาะชื่อโฮสต์ — ไม่ต้องมี https:// และไม่ต้องมีคำนำหน้า www การเปิดใช้งานทำได้ครั้งเดียว: รหัสจะถูกแปลงเป็นลิขสิทธิ์สำหรับโดเมนที่กรอก และเปิดใช้งานซ้ำไม่ได้ — หากกรอกโดเมนผิด คีย์จะใช้กับแผงควบคุมของคุณไม่ได้ และรหัสจะถูกใช้ไปแล้ว จึงควรกรอกโดเมนอย่างระมัดระวัง
พารามิเตอร์หลักทั้งหมดของแดชบอร์ดถูกกำหนดไว้ในไฟล์เดียว config.php ที่อยู่ในรูท (ข้างโฟลเดอร์ public/) ในรูปค่าคงที่ define() ทั่วไป ไฟล์นี้ถูกสร้างขึ้นตอนติดตั้ง จำเป็นต้องแก้ด้วยมือน้อยครั้ง — ส่วนใหญ่เมื่อเปลี่ยนโดเมน ย้ายเซิร์ฟเวอร์ หรือเชื่อมต่อฐานข้อมูลใหม่ หลังแก้ไขทุกครั้งให้ รีสตาร์ท PHP-FPM (มิฉะนั้นการเปลี่ยนแปลงจะไม่มีผลเพราะ OPcache)
ใส่ค่าของคุณลงในจุดที่ไฮไลต์ไว้ ส่วนที่เหลือให้คงเดิม:
ฐานข้อมูล. ข้อมูลการเชื่อมต่อ 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.
public/) และแดชบอร์ดนี้มีเว็บรูท (DocumentRoot) เป็นรูทของแดชบอร์ดเอง ไม่ใช่ public/ ตัวไฟล์เองไม่ “รั่ว”: ใน .htaccess ที่รูทมีการห้ามเข้าถึงไฟล์นี้อย่างชัดเจน (Require all denied) — เซิร์ฟเวอร์จะตอบ 403 แม้ไม่มีกฎนี้ ซอร์สก็ไม่รั่วอยู่ดี: นี่คือ PHP — เซิร์ฟเวอร์จะประมวลผลมัน ไม่ใช่ส่งออกเป็นข้อความ เผื่อไว้: อย่าอัปโหลดขึ้น repository สาธารณะ และอย่าส่งให้ฝ่ายสนับสนุนพร้อมรหัสผ่านจริง สิทธิ์ของไฟล์คือ 640.
UFW (Uncomplicated Firewall) — อินเทอร์เฟซแบบง่ายสำหรับ nftables/iptables ปิดพอร์ตขาเข้าทั้งหมด ยกเว้นที่อนุญาตไว้อย่างชัดเจน หน้า “ไฟร์วอลล์ UFW” แสดงสถานะและกฎต่าง ๆ
ufw enable ต้องอนุญาต SSH (ufw allow OpenSSH) ก่อนเสมอ ไม่เช่นนั้นคุณจะเข้าถึงเซิร์ฟเวอร์ไม่ได้
deny จะไม่ถือว่าเข้าถึงได้จากภายนอก
Skipping adding existing rule — ไม่ใช่ข้อผิดพลาด UFW แจ้งแบบนี้เพื่อบอกว่ามีกฎเดียวกันนี้อยู่แล้ว จึงไม่เพิ่มซ้ำ เมื่อรันการตั้งค่าอัตโนมัติซ้ำ (ซึ่งเป็นแบบ idempotent) ข้อความนี้เป็นเรื่องปกติ — ไม่ต้องดำเนินการใด ๆ
บล็อก IP โดยอัตโนมัติเมื่อจำนวนครั้งที่พยายามเข้าสู่ระบบล้มเหลวเกินกำหนด วิเคราะห์ล็อกของ SSH, nginx, Apache และบริการอื่น ๆ
การติดตั้งพื้นฐานอยู่ด้านบน ตรงนี้คือคอนฟิกใช้งานจริงที่ให้ jail ทำงานหลายสิบตัวและบล็อกได้หลายพันรายการ ได้แก่ การตั้งค่าทั่วไป jail สำคัญ ๆ และการแบน IP อันตรายอัตโนมัติจากรายการ ipsum
ไฟล์ /etc/fail2ban/jail.local — การตั้งค่าทั่วไปและ jail ที่สำคัญที่สุด:
ignoreip ต้องใส่ IP ของคุณและเครือข่ายที่เชื่อถือได้ ไม่เช่นนั้นอาจแบนตัวเองได้ หลังแก้ไข: sudo fail2ban-client reload
โหลดบล็อกลิสต์ ipsum อัตโนมัติผ่าน root-cron (sudo crontab -e): level 1 (IP กว่า 100,000 รายการ) จะโหลดเข้าเซ็ต ipsum ซึ่งถูกตัดที่ไฟร์วอลล์ (รายละเอียดอยู่ในหัวข้อ “บล็อกลิสต์ IPset”):
ipsum เพราะเป็นชื่อที่แดชบอร์ดอ่าน (การ์ด “IPset ipsum”) ระดับ: levels/1.txt — ครอบคลุมสูงสุด, levels/3.txt — แม่นยำกว่า (3+ แหล่งข้อมูล)
ทำไม “Monitor ความปลอดภัย” จึงแบ่งเป็นสองโซน การป้องกันทำงานสองระดับ และแดชบอร์ดไม่ปนกัน:
sshd, apache-*, nginx-* ฯลฯ) และ ผู้กระทำผิดซ้ำร้ายแรง (jail recidive — ผู้ที่โดนแบนมาแล้วหลายครั้ง) นี่คือ IP ที่บุกเข้ามาหาคุณ จริง ๆ — อยู่บนแผนที่การโจมตีและ “ไทม์ไลน์”ipset ipsum ตัดทิ้ง ที่ไฟร์วอลล์ ด้วยกฎ DROP ที่อยู่เหล่านี้ส่วนใหญ่ไม่เคยแตะเซิร์ฟเวอร์ของคุณเลย — ถูกตัดล่วงหน้า ตัวนับ “IPset ipsum” แสดงจำนวนที่ตัดไปเชิงป้องกันความต่างง่าย ๆ: เชิงรับ — “พวกนี้โจมตีแล้วโดนแบน”, เชิงรุก — “พวกนี้ถูกบล็อกก่อนจะได้ลงมือ” เมื่อก่อนใน recidive จะยัด list-3 ipsum เข้าไปแบบเทียม (จึงมีการแบ่งแบบเก่า “recidive จากรายการ”); ตอนนี้ recidive มีเฉพาะผู้กระทำผิดซ้ำจริง ๆ ส่วนเชิงรุกอยู่ที่ไฟร์วอลล์ทั้งหมด
ipsum — รายการ IP อันตรายสาธารณะที่อัปเดตทุกวัน มอนิเตอร์จะแสดงจำนวนที่อยู่ที่โหลดมาบนแดชบอร์ดและแผนที่การโจมตี และนำมาคิดในคะแนนความปลอดภัย (−10 หากยังไม่ได้โหลดชุดนี้)
ตัวเลือกขั้นต่ำที่ไม่ใช้ fail2ban — ชุด ipsum แยกต่างหากพร้อมการบล็อกผ่าน iptables:
@reboot พร้อมกันนั้นคำสั่ง create … -exist จะกำหนดลิมิต maxelem 300000 (ค่าเริ่มต้น 65536 — level 1 ใส่ไม่พอ จะขึ้น “Hash is full”):
ipsum และหากไฟร์วอลล์ถูกจัดการโดยตัวติดตั้ง (VPS ใหม่ — โปรไฟล์ “เต็มรูปแบบ”/“แบบเบา”) ก็จะเชื่อมชุดนี้เข้ากับ UFW ด้วยกฎ DROP — ทราฟฟิกจาก IP เหล่านี้จะถูกบล็อกจริง กฎนี้อยู่ หลัง ESTABLISHED,RELATED ดังนั้นการเชื่อมต่อปัจจุบัน (รวมถึง SSH ของคุณ) จะไม่ขาด — จะตัดเฉพาะการเชื่อมต่อใหม่จากรายการเท่านั้น ชุดจะถูกกู้คืนเมื่อบริการ ipsum-load.service โหลด ก่อน ไฟร์วอลล์ (มิฉะนั้น UFW จะไม่ทำงาน) และอัปเดตด้วยครอนเวลา 04:00 บน เซิร์ฟเวอร์ที่ตั้งค่าไว้แล้ว (มีแผงควบคุม, ไฟร์วอลล์ของตัวเอง) ตัวติดตั้งจะไม่ยุ่งกับไฟร์วอลล์ — ตรงนั้น ipsum จะยังเป็นเพียงรายการสำหรับแดชบอร์ดและแผนที่การโจมตี ส่วนกฎ DROP หากต้องการก็เพิ่มเองได้ (ตัวเลือกขั้นต่ำด้วย iptables … --match-set ipsum … -j DROP — ด้านบน) เมื่อติดตั้งอัตโนมัติไม่ต้องทำอะไรเองเลย
ตัวแทน Fail2ban สมัยใหม่ที่มาพร้อม threat intelligence แบบรวมกลุ่ม: ทั้งการบล็อกจากชุมชนและกฎของคุณเอง ต้องใช้ bouncer แยกต่างหากเพื่อนำการบล็อกไปใช้กับไฟร์วอลล์
systemctl is-active crowdsec) เริ่มด้วย: sudo systemctl enable --now crowdsec; หากล้มให้ดู sudo journalctl -u crowdsec -n 30 ใช้กฎเดียวกันนี้กับทุกเซอร์วิสที่มีสถานะ “ไม่ทำงาน” (Suricata, Falco, Monit, MySQL)
stream halted / การบล็อกไม่ถูกนำไปใช้ นี่คือ api-key ที่ไร้เจ้าของ: bouncer ถูกลบออกจาก cscli bouncers list แล้ว แต่คีย์เก่าของมันยังค้างอยู่ใน /etc/crowdsec/bouncers/*.yaml ให้ลงทะเบียน bouncer ใหม่และใส่คีย์ล่าสุด:
AIDE (Advanced Intrusion Detection Environment) จะสร้างสแนปช็อตของระบบไฟล์และรายงานการเปลี่ยนแปลงใน /etc, /bin, /usr ในทุกครั้งที่ตรวจสอบ หลังการติดตั้งต้องเริ่มต้นฐานข้อมูล (aideinit) เสมอ
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 ฐานข้อมูลจะไม่ถูกสร้าง ให้ย้ายสนิปเปตที่เสียออกแล้วลองใหม่:
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 และมอนิเตอร์อ่านได้โดยไม่ต้องเพิ่มกลุ่ม:
chmod 755 /var/lib/aide และ cron ตรวจสอบเวลา 02:00 — ไม่ต้องทำอะไรด้วยตนเอง
sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
โปรแกรมสแกนไวรัสสำหรับ Linux มีประโยชน์อย่างยิ่งในการตรวจสอบ /var/www เพื่อหา PHP-shell และโค้ดอันตราย
enable --now หรือไม่? สาเหตุที่พบบ่อยสามข้อ:
1. ยังมีบรรทัด Example ค้างอยู่ในไฟล์ตั้งค่า — clamd จะไม่ยอมเริ่มทำงานตราบใดที่ยังมีบรรทัดนี้:
2. ยังไม่ได้ดาวน์โหลดฐานข้อมูลลายเซ็น — clamd จะไม่เริ่มทำงานหากไม่มีมัน:
3. กำลังโหลดอยู่ — clamd โหลดลายเซ็นราว 8 ล้านรายการเข้าหน่วยความจำใช้เวลา 30–60 วินาที รอสักครู่แล้วตรวจสอบ: systemctl is-active clamav-daemon (สถานะ activating → ยังโหลดอยู่)
sudo journalctl -u clamav-daemon -n 30 --no-pager
clamd เพียงเก็บลายเซ็นไว้ในหน่วยความจำเท่านั้น ตัวมันเองไม่ได้สแกนอะไรตามกำหนดเวลา แดชบอร์ดแสดงผลลัพธ์ของการสแกนตามกำหนด จึงต้องมี cron ที่คอยสแกนและเขียนบันทึก การติดตั้งอัตโนมัติจะติดตั้งตัวห่อ /usr/local/bin/clamav-scan.sh และ cron เวลา 01:30 — หลังจากรันครั้งแรก “ไฟล์ที่ตรวจสอบ” และ “สแกนล่าสุด” จะมีข้อมูล รันทันทีโดยไม่ต้องรอกำหนดเวลา: sudo /usr/local/bin/clamav-scan.sh
Linux Malware Detect (LMD) — โปรแกรมสแกนมัลแวร์ที่เน้นภัยคุกคามบนเว็บ: PHP shell, เว็บแบ็คดอร์, ตัวดาวน์โหลด ใช้เอนจินของ ClamAV และเสริมด้วยลายเซ็นของตัวเอง
maldet --report
update-rc.d: error: unable to read /etc/init.d/maldet — ซึ่งไม่เป็นอันตราย maldet ไม่ได้ใช้ init.d การอัปเดตลายเซ็นและการสแกนจะทำงานผ่าน /etc/cron.daily/maldet หากด้านล่างเห็น installation completed แสดงว่าติดตั้งเรียบร้อยแล้ว
apt แต่อยู่ที่ /usr/local/maldetect และเมื่อเปิด open_basedir การตรวจสอบว่ามีอยู่จะทำผ่าน shell — ดูหัวข้อ “หน้าเว็บว่างเปล่าทั้งที่มีข้อมูลบนเซิร์ฟเวอร์”
ระบบตรวจจับการบุกรุกบนเครือข่าย: วิเคราะห์ทราฟฟิกในระดับแพ็กเก็ตและรู้จักลายเซ็นการโจมตีนับพันรูปแบบ เสริมการทำงานของ ModSecurity (ตัวนั้นทำงานที่ระดับ HTTP ส่วน Suricata ทำงานที่ระดับ TCP/IP)
/var/log/suricata/eve.json ในสิทธิ์ root โดยตั้งไดเรกทอรีเป็นโหมด 750 ทำให้เว็บเซิร์ฟเวอร์ (www-data) อ่านไม่ได้ ให้เปิดสิทธิ์เข้าถึงไดเรกทอรี ส่วนไฟล์ภายในยังคงถูกป้องกันอยู่:
ดักจับ system call ผ่าน eBPF/kernel module และตรวจจับความผิดปกติแบบเรียลไทม์: shell จาก nginx, การอ่าน /etc/passwd โดยเว็บโปรเซส, การเขียนลง /bin เป็นต้น
journalctl -u falco (ไม่ต้องใช้ sudo — ผ่านกลุ่ม systemd-journal) ตรวจสอบว่า www-data อยู่ในกลุ่มนี้ — ดู “การตั้งค่า sudo” (ข้อ 2) ในหน้าการติดตั้งด้วยตนเอง
/etc/passwd, การเขียนลงไดเรกทอรีระบบ) ศูนย์เหตุการณ์วิกฤตในหนึ่งวันบนเซิร์ฟเวอร์ที่สงบ คือสภาวะที่ปกติดี
journalctl ต้องมีสิทธิ์เข้าถึง journal; เพื่อให้แดชบอร์ดเห็นเหตุการณ์ได้อย่างเสถียร การติดตั้งอัตโนมัติจะเปิด file_output ของ Falco → /var/log/falco/falco.log และตั้งค่า UMask=0022 ให้บริการ (เว็บเซิร์ฟเวอร์อ่านล็อกได้) สำหรับการติดตั้งใหม่ ไม่จำเป็นต้องตั้งค่านี้ด้วยตนเอง
ModSecurity — เว็บไฟร์วอลล์ (WAF) สำหรับ Apache หรือ Nginx บล็อกการโจมตีในระดับแอปพลิเคชัน: SQL injection, XSS, การเดินย้อนพาธ, สแกนเนอร์
IncludeOptional /etc/modsecurity/*.conf แต่แพ็กเกจวางไว้เฉพาะ modsecurity.conf-recommended เท่านั้น ซึ่งไม่เข้าเงื่อนไข *.conf หากไม่คัดลอกไปเป็น modsecurity.conf ค่า SecRuleEngine จะยังคงเป็น Off: โมดูลถูกโหลด กฎ CRS ถูกโหลด แต่ทราฟฟิกไม่ถูกตรวจสอบและไม่มีการสร้างล็อกการตรวจสอบ โหมดกลาง DetectionOnly จะเพียงบันทึกเหตุการณ์ลงล็อกโดยไม่บล็อกคำขอ — แดชบอร์ดจะแสดงเป็นสีเหลือง
การเข้าถึงล็อกการตรวจสอบของแดชบอร์ด ล็อก /var/log/apache2/modsec_audit.log เป็นของ root (สิทธิ์ 640) เว็บยูสเซอร์อ่านไม่ได้ แดชบอร์ดดึงข้อมูลผ่านตัวห่อ (wrapper) — ให้สร้างมันขึ้นมา:
SecRuleEngine ตัวสุดท้าย ที่ไม่มีการเยื้อง: บรรทัดที่เยื้องอยู่ภายในบล็อก <LocationMatch>/<Directory> (เช่น การปิด WAF สำหรับ phpMyAdmin) และไม่ได้กำหนดโหมดโดยรวมwww-data ส่วนใน HestiaCP พูลของไซต์ทำงานจากเจ้าของไซต์ (เช่น admin) — ตรวจสอบด้วย grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.confX-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 แล้วเติมลงในรายการ หากตัวห่อเป็นเวอร์ชันเก่า (ไม่มีส่วนนี้) — ส่วนดังกล่าวจะแสดงเพียงคำเตือนว่า “ไม่พร้อมใช้งาน” ส่วนที่เหลือของหน้ายังทำงานเหมือนเดิม
Auditd (Linux Audit Daemon) บันทึกการเรียกใช้ระบบในระดับเคอร์เนล: การเข้าและออกจากระบบ, คำสั่ง sudo, ความพยายามยืนยันตัวตนที่ล้มเหลว, การเปลี่ยนแปลงไฟล์ Monitor จะแสดงการเข้าสู่ระบบ, ความพยายามที่ล้มเหลว และคำสั่ง sudo ของวันนี้
ausearch (/usr/sbin/ausearch) และเมื่อจำเป็นจะอ่านจาก /var/log/audit/audit.log ด้วยคำสั่ง tail ทั้งสองต้องอยู่ใน sudoers
เฝ้าดูบริการต่าง ๆ (nginx, php-fpm, mysql ฯลฯ) และรีสตาร์ตให้เมื่อบริการล่ม สามารถส่งการแจ้งเตือนทางอีเมลได้
monit status ใน /etc/monit/monitrc ต้องเปิดใช้อินเทอร์เฟซ HTTP (บล็อก set httpd พร้อม allow localhost) มิฉะนั้น monit status จะคืนค่าเป็นข้อผิดพลาด
monitrc บรรทัด set httpd ถูกคอมเมนต์ไว้ (ค่าเริ่มต้นมาเป็น # set httpd port 2812 …) ให้ยกเลิกคอมเมนต์บล็อกนี้และอนุญาต localhost (2) การเปิด httpd เพียงอย่างเดียวไม่ได้เฝ้าดูอะไรเลย — Monit จะนับเฉพาะสิ่งที่ระบุด้วยสแตนซา check เท่านั้น หากไม่มี รายการจะว่างเปล่าแม้อินเทอร์เฟซจะทำงานอยู่ก็ตาม คอนฟิกขั้นต่ำที่ใช้งานได้:
conf.d ที่พร้อมใช้พร้อม httpd บนพอร์ต 2812 และชุดการตรวจสอบไว้ให้ — บนการติดตั้งใหม่ไม่ต้องตั้งค่าเองด้วยมือ
PSAD วิเคราะห์ล็อกของ iptables และตรวจจับการสแกนพอร์ตกับการโจมตีเครือข่าย โดยกำหนดระดับภัยคุกคาม (1–5) ให้แต่ละแหล่งที่มา เสริมการทำงานของ fail2ban และ Suricata
psad --Status (ต้องอยู่ใน sudoers) หากไม่เปิดการบันทึกล็อกของ iptables หน้านี้จะว่างเปล่า — ถือเป็นเรื่องปกติตราบใดที่ยังไม่มีการสแกน
Mandatory Access Control จำกัดว่าโปรแกรมสามารถเข้าถึงไฟล์และทรัพยากรใดได้บ้าง แม้จะถูกเจาะระบบไปแล้วก็ตาม โดยใน Ubuntu/Debian จะใช้ AppArmor เป็นค่าเริ่มต้น (โดยทั่วไปติดตั้งและเปิดใช้งานอยู่แล้ว)
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) ไม่มีโหมดนี้ ตัวเลขจึงตรงกันเสมอ
unconfined ให้เป็น enforce ควรทำอย่างรู้ตัวเท่านั้น เพราะมันถูกปิดไว้ไม่ใช่เพราะข้อผิดพลาด แต่เพราะไม่เช่นนั้นตัวโปรแกรมเองจะทำงานผิดพลาด ส่วนโปรไฟล์ใน complain เป็นอีกเรื่องหนึ่ง คือมีการเขียนกฎไว้แล้วเพียงแต่ยังไม่ได้บังคับใช้
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/ ของแดชบอร์ดเอง
สคริปต์ครอบ debsums-scan.sh จะค้นหา data/ ของแดชบอร์ดเอง — ไม่ต้องระบุพาธ
/etc/ (คอนฟิก) และ /usr/share/ (ทรัพยากร) บนเซิร์ฟเวอร์มักเป็นเรื่องปกติ — แดชบอร์ดจะทำเครื่องหมายด้วยสีแยกต่างหาก สิ่งที่น่ากังวลคือการเปลี่ยนแปลงไบนารีและไลบรารี (/bin, /sbin, /usr/lib เป็นต้น) — การ์ด “ไบนารี / ไลบรารี” จะแสดงเฉพาะรายการเหล่านั้น
Lynis รันแบบแมนนวลหรือผ่าน cron รายงานต้องบันทึกไว้ในโฟลเดอร์ data/lynis/ ของโปรเจกต์ — มอนิเตอร์จะอ่านไฟล์ lynis-report.dat
lynis-scan.sh ในเบื้องหลังจากแดชบอร์ดโดยตรง (ไม่ต้องรอ cron): แสดง “กำลังสแกน…” และอัปเดตรายงานเองเมื่อเสร็จ ในการนี้ผู้ใช้เว็บต้องมีบรรทัด sudoers สำหรับรันสคริปต์ — ตัวติดตั้งจะเพิ่มให้อัตโนมัติใน /etc/sudoers.d/monitor หากติดตั้งแดชบอร์ดแบบแมนนวล/ก่อนหน้านี้ ให้เพิ่มบรรทัดด้วยผู้ใช้เดียวกับที่ระบุไว้ในไฟล์อยู่แล้ว:
Logwatch ต้องบันทึกรายงานประจำวันลงในโฟลเดอร์ data/logwatch/ ของโปรเจกต์ในรูปแบบ .txt โดย Monitor จะแสดงรายงานล่าสุดและคลังรายงาน
การเฝ้าระวังเครือข่ายไม่ต้องติดตั้ง — เป็นหน้าในตัวของแดชบอร์ด แสดงสถานะเครือข่ายของเซิร์ฟเวอร์จากแหล่งข้อมูลในเครื่อง:
/proc/net/dev;ip;ss;journalctl -kแหล่งข้อมูลสามอย่างแรกทำงาน โดยไม่ต้องใช้ sudo จึงเห็นอินเทอร์เฟซ ทราฟฟิก การเชื่อมต่อ และพอร์ตได้ทันที ส่วนบล็อก “เหตุการณ์เคอร์เนล” ใช้ journalctl -k ซึ่งอ่านผ่านกลุ่ม systemd-journal (“การตั้งค่า sudo” ข้อ 2) ไม่ต้องใช้ sudo ตรวจสอบว่าผู้ใช้เว็บเข้าถึงได้ครบ:
UFW BLOCK จะไม่ปรากฏที่นี่ — อยู่ในหน้า “ไฟร์วอลล์ UFW” และ “แผนที่การโจมตี” บล็อกว่างพร้อมเครื่องหมายถูกสีเขียว = ในรอบวันไม่มีความขัดข้องของเครือข่าย
หน้านี้แสดงข้อมูลสามอย่าง:
df); แถบจะเป็นสีแดงเมื่อ ≥90%;lsblk) เฉพาะที่มีอยู่จริง (ซ่อน loop/snap);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/ ของแดชบอร์ดเอง
สคริปต์ห่อ smart-scan.sh จะค้นหา data/ ของแดชบอร์ดเอง ไม่ต้องระบุพาธ ภายในนั้น lsblk -e7,11 จะยกเว้น loop/cdrom
หน้านี้แสดงประวัติโหลดของเซิร์ฟเวอร์ในช่วง 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 ชั่วโมงจะถูกลบอัตโนมัติทุกครั้งที่มีการบันทึก
ตัวห่อ collect-metrics-all.sh (ดูสรุปงาน cron) จะค้นหาอินสแตนซ์ทั้งหมดของแดชบอร์ดที่ติดตั้งบนเซิร์ฟเวอร์เอง แล้วรัน cron/collect_metrics.php ของแต่ละอินสแตนซ์ในนามของเจ้าของเว็บไซต์
การแจ้งเตือนโหลด (หัวข้อ “การตั้งค่า” → “การแจ้งเตือนโหลด”) — เมื่อ CPU/RAM/ดิสก์/inodes เกินเกณฑ์ แดชบอร์ดจะส่งการแจ้งเตือนไปยัง Telegram/Email (ช่องทางเดียวกับรายงานประจำวัน — ไม่ต้องเปิดใช้แยกสำหรับการแจ้งเตือน) และส่งอีกครั้งเมื่อค่ากลับสู่ปกติ จะไม่ส่งซ้ำระหว่างที่ค่ายังเกินเกณฑ์อยู่: การแจ้งเตือนครั้งถัดไปจะมาหลังจากรอบ “กลับสู่ปกติ → เกินเกณฑ์อีกครั้ง” เท่านั้น
collect_metrics.php ตัวเดียวกันทุกครั้งที่รัน (ทุก 5 นาที) — ไม่ต้องใช้ cron แยก สถานะ “แจ้งเตือนไปแล้ว / ยังไม่แจ้ง” เก็บไว้ใน data/alerts_state.json ส่วนเกณฑ์อยู่ในการตั้งค่าของแดชบอร์ด
หน้า “แผนที่การโจมตี” จะระบุประเทศจาก IP ด้วยคำสั่ง geoiplookup หากไม่มีแพ็กเกจ GeoIP จะไม่สามารถระบุประเทศได้และจะไม่มีจุดปรากฏบนแผนที่:
/usr/share/GeoIP/GeoIP.dat อ่านได้ทุกคน ผลลัพธ์จะถูกแคชไว้ใน tmp/geoip_cache.json ตัวแผนที่เอง (Leaflet + ไทล์ OpenStreetMap) จะโหลดในเบราว์เซอร์ — จึงต้องมีอินเทอร์เน็ตบนเครื่องที่เปิดแดชบอร์ด
การ์ดในตัวสองใบบนแดชบอร์ด ที่แสดงไม่ใช่แค่ “เปิด/ปิด” ของเครื่องมือ แต่แสดงความปลอดภัยจริงของเซิร์ฟเวอร์ ไม่ต้องติดตั้งเพิ่ม อ่านค่าในเครื่องได้โดยไม่ต้องใช้ 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 จะไม่ถูกนับ
/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 รายการโดยละเอียดดูได้ที่หน้า “การอัปเดตความปลอดภัย”
update-notifier-common) หากไม่มี apt-check — มอนิเตอร์จะนับแพตช์ผ่าน apt-get -s upgrade
การอัปเดตความปลอดภัยอัตโนมัติ (unattended-upgrades) — ที่หน้า “การอัปเดตความปลอดภัย” มีการ์ดแยกที่แสดงว่าการติดตั้งแพตช์ความปลอดภัยอัตโนมัติเปิดใช้งานอยู่หรือไม่ และทำงานล่าสุดเมื่อใด ไม่ต้องใช้ sudo — สถานะอ่านผ่าน apt-config dump
การสำรองข้อมูลคือหลักประกันสำคัญที่สุด เพราะการสูญเสียข้อมูลร้ายแรงกว่าการถูกเจาะระบบใดๆ คุณต้องมีสองอย่าง — สำรองข้อมูลเซิร์ฟเวอร์/เว็บไซต์ และแยกต่างหากคือ สำรองฐานข้อมูลของแดชบอร์ด (ที่นั่นมีผู้ใช้ คีย์ WebAuthn การตั้งค่า และใบอนุญาต)
ตัวเลือก A — HestiaCP: แท็บ Backup ของผู้ใช้ → ปุ่มสร้างการสำรองข้อมูล (หรือตามกำหนดเวลาในการตั้งค่าเซิร์ฟเวอร์) การสำรองข้อมูลจะรวมเว็บไซต์และฐานข้อมูลของเว็บไซต์
ตัวเลือก B — ด้วยตนเอง (cron): ดัมป์ฐานข้อมูล + บีบอัดไดเรกทอรี data/ ของแดชบอร์ด:
การอัปเดตเป็นเวอร์ชันใหม่ ก่อนอื่นให้สำรองข้อมูล จากนั้นอัปโหลดไฟล์โค้ดทับ โดยคงข้อมูลของคุณไว้:
public/, includes/, assets/, cron/, database/ รวมถึง .htaccess ที่รูท (front controller — ห้ามคงการกำหนดเส้นทางจากเวอร์ชันเก่า), manifest.json, sw.js;config.php (ข้อมูล DB), data/ (รายงาน), logs/, tmp/ (เซสชันและแคช)SSH_FX_PERMISSION_DENIED — Permission denied ไฟล์ของแผงควบคุมเป็นของ www-data (ตั้งไว้เช่นนี้ตอนติดตั้ง) ขณะที่ไคลเอนต์ SFTP เชื่อมต่อด้วยผู้ใช้ของคุณเองซึ่งไม่มีสิทธิ์เขียน การมอบทั้งแผงควบคุมให้ www-data “เพื่อให้ใช้งานได้” คือสิ่งที่นำไปสู่ข้อผิดพลาดนี้พอดี ด้านล่างมีสามวิธี วิธีใดก็แก้ปัญหาได้
data/ (รายงาน), tmp/ (เซสชันและแคช), logs/; ซึ่งยังคงเป็นของ www-data ส่วนที่เหลือคือโค้ด และเว็บเซิร์ฟเวอร์ต้องการเพียงสิทธิ์อ่านเท่านั้น ซึ่งได้จากกลุ่ม www-data ด้วยสิทธิ์ 644 ผลพลอยได้: หากมีช่องโหว่ใน PHP ก็ไม่สามารถเขียนทับไฟล์ของแผงควบคุมได้ บนแผงโฮสติ้ง (HestiaCP และที่คล้ายกัน) ไม่จำเป็นต้องใช้ทางเลือก A: ที่นั่นไฟล์เว็บไซต์เป็นของบัญชีที่คุณเข้าผ่าน SFTP อยู่แล้ว และเว็บเซิร์ฟเวอร์อ่านผ่านกลุ่ม
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 แล้ว)
การย้ายไปยังเซิร์ฟเวอร์อื่น:
config.php, data/mysqldump บนเครื่องเก่า → นำเข้าบนเครื่องใหม่ แล้วแก้ไขข้อมูล DB ใน config.phpadm, งาน cronหากคุณเข้าสู่ระบบไม่ได้ — แก้ไขได้โดยตรงในฐานข้อมูลจากเซิร์ฟเวอร์ เปิดฐานข้อมูล (ชื่อดูได้จาก config.php):
สูญเสียคีย์ WebAuthn (ผ่านการยืนยันปัจจัยที่สองไม่ได้) — ปิด 2FA เข้าสู่ระบบด้วยรหัสผ่าน แล้วลงทะเบียนคีย์ใหม่:
ลืมรหัสผ่าน — กำหนดแฮชใหม่ (สร้างบนเซิร์ฟเวอร์แล้วนำมาใส่):
บล็อกตัวเองด้วยตัวกรอง IP — ปิดการจำกัด:
sudo mysql บนเซิร์ฟเวอร์ หรือ phpMyAdmin / ส่วนฐานข้อมูลในแผงควบคุมของโฮสติ้ง หลังกู้คืนแล้วให้เปิด WebAuthn และตัวกรอง IP อีกครั้ง
สรุปรายการงานอยู่ใน root-cron ของเซิร์ฟเวอร์ (เพิ่มผ่าน sudo crontab -e) เก็บไว้เฉพาะบรรทัดของเครื่องมือที่คุณใช้จริง และแก้ไขพาธให้ตรงกับเซิร์ฟเวอร์ของคุณ
sudo crontab -l และตรวจว่าเซอร์วิส cron ทำงานอยู่
TIMEZONE จาก config.php ค่าคงที่ TIMEZONE ส่งผลต่อ PHP เท่านั้น (วิธีที่แดชบอร์ดแสดงวันที่) แต่ cron-daemon จะรันงานตามเวลาระบบของ OS หากโซนเวลาของเซิร์ฟเวอร์ไม่ตรงกับของคุณ รายงาน “08:00” จะมาผิดเวลา ตัวอย่าง: เซิร์ฟเวอร์อยู่คนละโซน (Asia/Dhaka, UTC+6) ส่วนคุณอยู่ในกรุงเทพฯ (UTC+7) → รายงาน “08:00” จะมาถึงเวลา 09:00 ตามเวลาคุณ ให้ตรวจสอบและปรับโซนเวลาระบบให้เป็นของคุณเมื่อจำเป็น:
0 8 * * * จะทำงานเวลา 08:00 ตามเวลาท้องถิ่น มิฉะนั้นต้องเลื่อน cron เอง แต่เมื่อเปลี่ยนเวลาฤดูหนาว/ฤดูร้อน ค่าที่เลื่อนจะคลาดเคลื่อนอีก — จึงควรตั้งโซนเวลาระบบให้ถูกต้องดีกว่า
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./usr/local/bin/ ไม่สามารถเขียนตรงจาก FileZilla ได้ — ไดเรกทอรีเป็นของ root และไคลเอนต์ SFTP จะได้รับ SSH_FX_PERMISSION_DENIED ลำดับขั้นตอนคือ: อัปโหลดไฟล์ไปยัง /tmp ก่อน (ที่นั่นทุกคนเขียนได้) แล้วย้ายไปยังตำแหน่งที่ต้องการด้วยคำสั่งเดียว:
/tmp ที่รากของเซิร์ฟเวอร์ — ไม่ใช่ /var/tmp และไม่ใช่ tmp/ ภายในตัวแดชบอร์ดเอง (อันหลังเป็นของ www-data และปิดจากผู้ใช้ของคุณ) ในแผนผัง FileZilla /tmp เป็นสาขาระดับบนสุด อยู่ข้าง var ไม่ใช่อยู่ภายในมัน
/usr/local/bin/ ล็อก — /var/log/arciveo-cron.log) — ไม่ต้องทำอะไรด้วยตนเอง
/home/*/web/*/public_html และ /var/www/* แล้ววางรายงานลงใน data/ ของแต่ละแห่ง หากแดชบอร์ดอยู่ที่พาธอื่น — ให้เพิ่มพาธนั้นในบรรทัด for app in … ภายในสคริปต์ มิฉะนั้นรายงาน Lynis/SMART/debsums/Logwatch จะไม่เข้าสู่แดชบอร์ด
logs/cron.log จะถูกสร้างครั้งแรกโดย root-cron — ไฟล์จะเป็นของ root และแท็บ “บันทึก cron” ในแดชบอร์ดจะไม่สามารถอ่านหรือล้างมันได้ ให้สร้างไฟล์ไว้ล่วงหน้าในนามของเว็บยูสเซอร์ (เจ้าของไดเรกทอรีเว็บไซต์ บน HestiaCP คือบัญชี เช่น admin) — จากนั้น root-cron จะเพียงเขียนต่อท้ายโดยไม่เปลี่ยนเจ้าของ:
stat -c %U /path/to/monitor.
sudo crontab -e) มิฉะนั้นมันจะทำงานซ้ำสองครั้ง
sudo crontab เปล่า ๆ (นั่นจะเป็นการยกระดับสิทธิ์เป็น root โดยตรงสำหรับใครก็ตามที่เข้าถึงเซสชันของแดชบอร์ดได้) แต่เป็นสคริปต์แคบ ๆ ที่มีสองคำสั่ง (list/set) ซึ่งแตะเฉพาะบล็อกของตัวเองระหว่างคอมเมนต์เฉพาะ ติดตั้งเพียงครั้งเดียว:
www-data — ตรวจว่า PHP-FPM pool ของเว็บไซต์ทำงานภายใต้ผู้ใช้ใด (ps -o user= -C php-fpm) แล้วใส่ค่านั้นในบรรทัด sudoers
public/crontab_monitor.php ถูกอัปโหลดผ่าน FTP/SFTP ด้วยผู้ใช้ระบบคนอื่น (เช่น root) ที่ต่างจากไฟล์อื่นของเว็บไซต์ เว็บเซิร์ฟเวอร์จะอ่านมันไม่ได้ ให้เทียบเจ้าของและสิทธิ์กับไฟล์ข้างเคียงแล้วปรับให้ตรงกัน:
Monitor ตรวจหาเครื่องมือผ่าน dpkg-query — ฐานข้อมูลแพ็กเกจของ APT หากติดตั้งเครื่องมือโดยไม่ผ่าน apt (ติดตั้งเอง จาก snap หรือจากซอร์ส) dpkg จะมองไม่เห็น
ข้อผิดพลาด 500 — ตรวจสอบล็อกของ PHP, nginx และตัวมอนิเตอร์เอง:
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 aa-status ในเทอร์มินัลแสดงโปรไฟล์ แต่หน้า “AppArmor” แสดง “ไม่ทำงาน”) สาเหตุ: เว็บผู้ใช้ไม่มีสิทธิ์ sudo สำหรับคำสั่งของโมดูลนั้น ตรวจสอบจากรายการด้านบน: หากถามรหัสผ่าน — เพิ่มบรรทัดที่ขาดใน /etc/sudoers.d/monitor (“การตั้งค่า sudo”) คำสั่ง “ใหม่” ที่พบบ่อย: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD)
apache2ctl, ausearch, aa-status หรือ ss หรือเว็บผู้ใช้ไม่ได้อยู่ในกลุ่ม adm/systemd-journal (เป็นที่อ่านล็อกของ fail2ban/auth/modsec และ journalctl — Falco และเหตุการณ์ของเคอร์เนล)
อาการ: บนเซิร์ฟเวอร์มีข้อมูลอยู่ (มองเห็นผ่าน shell) แต่หน้าเว็บแสดง “ไม่มีข้อมูล” หรือสถานะไม่ถูกต้อง เช่น AIDE ระบุว่า “ยังไม่ได้เริ่มต้นใช้งาน” ทั้งที่สร้างฐานข้อมูลแล้ว
สาเหตุคือ open_basedir: แผงควบคุมและโฮสติ้งจำนวนมากจำกัดพูล PHP-FPM ไว้เฉพาะไดเรกทอรีของโดเมน ทำให้ฟังก์ชัน PHP อย่าง file_exists(), file_get_contents(), filemtime() บนพาธของระบบ (/var/lib/aide, /var/log, /proc…) ถูกบล็อก Monitor แก้ปัญหานี้ด้วยการอ่านพาธเหล่านั้นผ่านคำสั่งของระบบตามปกติ (cat, test, stat)
open_basedir วิธีแก้ที่ถูกต้องคือการอ่านผ่านคำสั่งของระบบ (ทำไว้แล้วสำหรับ AIDE และ Network Monitor) ไม่จำเป็นต้องขยาย open_basedir ไปยัง /var, /proc และยังปลอดภัยน้อยกว่าด้วย
Monitor ตรวจสอบใบรับรองโดยเชื่อมต่อกับโดเมนโดยตรงผ่านพอร์ต 443 หากไม่สามารถเข้าถึงโดเมนจากตัวเซิร์ฟเวอร์เอง หรือพอร์ตถูกไฟร์วอลล์ปิดกั้น การตรวจสอบจะไม่สำเร็จ
/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) และ Apache (/etc/apache2/sites-enabled/) พร้อมกับโฮสต์ปัจจุบันจาก HTTP_HOST
Monitor เชื่อมต่อกับ MySQL ด้วยผู้ใช้จาก config.php ซึ่งมีสิทธิ์เข้าถึงเฉพาะฐานข้อมูลของตัวเอง MySQL จะแสดงใน information_schema เฉพาะฐานข้อมูลที่มีสิทธิ์เท่านั้น จึงมองไม่เห็นฐานอื่น
เพื่อให้ monitor เห็น ฐานข้อมูลทั้งหมด ให้มอบสิทธิ์อ่านอย่างเดียวแก่ผู้ใช้รายนี้ (ทำครั้งเดียวจาก root แล้วแทนชื่อผู้ใช้จาก config.php):
sudo mysql ใด ๆ รายการฐานข้อมูลดึงผ่านการเชื่อมต่อ PDO ของตัวเอง
PostgreSQL ต้องใช้สิทธิ์ระดับผู้ใช้ postgres ซึ่งเว็บผู้ใช้ของแดชบอร์ดไม่มี การเปิด sudo psql แบบกว้างจาก PHP นั้นไม่ปลอดภัย แดชบอร์ดจึงเรียกใช้ตัวห่อหุ้มแบบจำกัดที่ไม่รับพารามิเตอร์แทน ซึ่งแสดงเพียงเวอร์ชัน จำนวนการเชื่อมต่อ และรายการฐานข้อมูลพร้อมขนาด สร้างมันขึ้นมา:
monitor-pgstat ออกจาก sudoers (ขั้นตอนที่ 13 ของการติดตั้งด้วยตนเอง) และไม่ต้องสร้างสคริปต์: การ์ด PostgreSQL จะยังคงไม่ทำงานเท่านั้น
แดชบอร์ดแสดงสิ่งที่กำลังเกิดขึ้น ด้านล่างคือสิ่งที่ต้องทำในสถานการณ์ทั่วไป หลักการโดยรวม: อย่าตื่นตระหนก ตรวจสอบว่าเป็นกิจกรรมที่ถูกต้องหรือไม่ (การกระทำของคุณ การอัปเดต การสำรองข้อมูล) แล้วตอบสนองตามระดับความรุนแรง
ignoreip/etc, นอก /usr/share) — อาจถูกสับเปลี่ยน ตรวจสอบแพ็กเกจ: debsums PACKAGE_NAME หากไม่แน่ใจให้ติดตั้งใหม่ (apt install --reinstall)127.0.0.1 หรือปิดพอร์ตใน UFW นี่คือช่องโหว่จริงcertbot renew หรือการตั้งค่าในแดชบอร์ด)sudo apt update && sudo apt upgrade หลังอัปเดตเคอร์เนลให้รีบูตเซิร์ฟเวอร์