Fail2ban ถูกติดตั้งเป็นอย่างแรกและมักหยุดอยู่แค่นั้น: แพ็กเกจติดตั้งแล้ว บริการทำงานอยู่ sshd ดูเหมือนได้รับการคุ้มครอง หกเดือนต่อมาจึงพบว่า jail กำลังอ่านไฟล์ที่ไม่มีอยู่บนระบบนี้ และทุกอย่างที่เหลือ — เมล แผงควบคุม ฟอร์มเข้าสู่ระบบของเว็บไซต์ — ไม่เคยได้รับการคุ้มครองเลย

มาดูกันว่าบน VPS ธรรมดาที่มีเว็บไซต์ควรเปิดอะไรบ้าง ควรใส่ตัวเลขเท่าไร และจะมั่นใจได้อย่างไรว่า jail ทำงานจริง ไม่ใช่แค่มีอยู่ในไฟล์ตั้งค่า

ตรวจก่อนว่า sshd จับอะไรได้บ้างไหม

คำสั่งเดียว:

sudo fail2ban-client status sshd

ผลลัพธ์มีบรรทัด Currently failed, Total failed และ Total banned ถ้าบนเซิร์ฟเวอร์ที่อยู่บนอินเทอร์เน็ตมาอย่างน้อยหนึ่งวันแล้ว Total failed เป็นศูนย์ แสดงว่า jail ไม่ทำงาน ลองเทียบกับความเป็นจริง:

sudo lastb | wc -l

ความพยายามล้มเหลวนับพันใน lastb กับศูนย์ใน Fail2ban หมายความอย่างเดียวเท่านั้น: ตัวกรองกำลังมองผิดที่

สาเหตุที่พบบ่อยที่สุดคือ Debian 12 มันไม่ติดตั้ง rsyslog เป็นค่าเริ่มต้นอีกต่อไป ไฟล์ /var/log/auth.log จึงไม่มีอยู่เลย และ jail sshd มาตรฐานถูกตั้งให้อ่านไฟล์นั้นพอดี ไม่มีข้อผิดพลาดใด ๆ แจ้งเรื่องนี้: บริการเริ่มทำงาน สถานะแสดงผล ตัวนับยังเป็นศูนย์ วิธีแก้คือย้ายไปใช้เจอร์นัลของ systemd:

[sshd]
enabled = true
backend = systemd

อีกทางเลือกคือนำ rsyslog กลับมาเป็นแพ็กเกจ ถ้า auth.log แบบข้อความล้วนจำเป็นสำหรับเครื่องมืออื่น Ubuntu 24.04 ก็มีพฤติกรรมแบบเดียวกัน

ควรวางการตั้งค่าของตัวเองไว้ที่ไหน

อย่าแตะ /etc/fail2ban/jail.conf: เมื่อแพ็กเกจอัปเดตมันจะถูกเขียนทับ และการแก้ไขทุกอย่างที่ทำไว้ที่นั่นจะหายไปเงียบ ๆ ในวันหนึ่ง การตั้งค่าของคุณเองไปอยู่ที่ /etc/fail2ban/jail.local — ไฟล์นี้ถูกอ่านเป็นลำดับสุดท้ายและมีน้ำหนักเหนือไฟล์ส่วนกลาง

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd

ignoreip พร้อมที่อยู่ของคุณเองไม่ใช่ความฟุ่มเฟือยแต่เป็นประกัน: การล็อกตัวเองออกเพราะพิมพ์ผิดนั้นง่ายกว่าที่คิด ถ้าไม่มีที่อยู่คงที่ ให้เก็บทางเข้าที่สองไว้นอกเหนือจาก SSH — คอนโซลของผู้ให้บริการหรือ VNC

jail ที่เปลี่ยนภาพรวม: recidive

jail ธรรมดามีความจำสั้น: ห้าครั้งในสิบนาที แบนหนึ่งชั่วโมง แล้วอีกหนึ่งชั่วโมงต่อมาทุกอย่างก็เริ่มใหม่ บอตอยู่กับสิ่งนี้ได้สบายและจะกลับมาทั้งพรุ่งนี้และมะรืนนี้ recidive ปิดช่องว่างนี้พอดี: มันไม่ได้อ่านล็อกของระบบแต่อ่านล็อกของ Fail2ban เอง นั่นคือมันแบนคนที่ Fail2ban แบนไปแล้ว

[recidive]
enabled  = true
bantime  = 1w
findtime = 1d
maxretry = 5

อ่านแบบนี้: «ถูกแบนห้าครั้งในหนึ่งวัน — หายไปหนึ่งสัปดาห์» jail นี้ต้องการไฟล์ /var/log/fail2ban.log ดังนั้นถ้าคุณย้ายการบันทึกล็อกของ Fail2ban เองไปที่ syslog แล้ว recidive ก็จะต้องใช้ backend = systemd ด้วย

ในทางปฏิบัติ ผลของ jail ตัวเดียวนี้มักเห็นได้ชัดกว่าการปรับจูนตัวอื่นทั้งหมดอย่างละเอียด: ขาประจำหายไปและในล็อกเหลือแต่พื้นหลังใหม่ ๆ

ถ้าเซิร์ฟเวอร์มีเว็บไซต์ควรเปิดอะไรอีก

เรียงตามประโยชน์จากมากไปน้อย:

  • nginx-http-auth หรือ apache-auth — การเดารหัสผ่านบนการยืนยันตัวตนพื้นฐาน จำเป็นถ้าพื้นที่ผู้ดูแลหรือเว็บไซต์ staging อยู่หลังรหัสผ่านของเว็บเซิร์ฟเวอร์;
  • nginx-botsearch — การค้นหาเส้นทางที่รู้จักกันดี: /wp-login.php, /phpmyadmin, /.env นี่ไม่ใช่การเจาะแต่เป็นการสอดแนม และมันมาก่อนทุกอย่าง;
  • nginx-limit-req — ทำงานได้ก็ต่อเมื่อมี limit_req_zone กำหนดไว้ใน Nginx เอง ถ้าไม่มี jail ก็เปิดอยู่แต่ไร้ประโยชน์;
  • postfix-sasl และ dovecot — จำเป็นถ้าคุณรันเมลของตัวเอง การเดารหัสผ่านกล่องจดหมายเกิดขึ้นตลอดเวลาและมักไม่มีใครดูเลย
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log

ฟอร์มเข้าสู่ระบบของเว็บไซต์เองเป็นอีกเรื่องหนึ่ง แอปพลิเคชันไม่มีล็อกการเข้าสู่ระบบล้มเหลวของตัวเอง และใน access.log ความพยายามที่ล้มเหลวดูเหมือน POST ธรรมดาที่ได้ 200 หรือ 302 — แยกจากความพยายามที่สำเร็จไม่ออกด้วยตัวกรองทั่วไปใด ๆ มีสองทาง: ให้แอปพลิเคชันเขียนความล้มเหลวลง syslog (ระบบจัดการเนื้อหาส่วนใหญ่มีปลั๊กอินสำหรับเรื่องนี้) หรือจำกัดอัตราคำขอไปยังหน้าเข้าสู่ระบบ อย่างที่สองเป็นตัวจำกัดไม่ใช่ตัวตรวจจับ และรู้ไว้ก่อนย่อมดีกว่า

ตัวเลข

bantime = 10m คือค่าเริ่มต้นที่ทำให้ผู้คนตัดสินว่า Fail2ban ไร้ประโยชน์: สิบนาทีเพียงพอให้บอตกลับมา หนึ่งชั่วโมงสำหรับการแบนครั้งแรกบวกกับ recidive หนึ่งสัปดาห์ทำงานได้ดีกว่าการแบนถาวรมาก ซึ่งเมื่อเวลาผ่านไปจะกลายเป็นรายการกฎยาวเป็นไมล์

maxretry = 3 สำหรับ SSH คือวิธีล็อกตัวเองออกที่เชื่อถือได้ ห้าครั้งในสิบนาทีก็ตัดการเดารหัสผ่านได้ดีพอ ๆ กัน

การเดาแบบช้า — หนึ่งครั้งทุกห้านาที — จะไม่มีวันตกอยู่ใน findtime นั่นไม่ใช่เหตุผลให้ยืดหน้าต่างเป็นหนึ่งวัน: คุณจะได้การแบนผิดพลาดกับเพื่อนร่วมงานของคุณเอง สิ่งที่ได้ผลกับการเดาแบบช้าไม่ใช่เกณฑ์แต่คือการปิดการยืนยันตัวตนด้วยรหัสผ่าน

ตรวจให้แน่ใจว่าการแบนไปถึงแพ็กเก็ต

Fail2ban เพียงเรียกคำสั่งภายนอก ถ้า banaction ไม่ตรงกับสิ่งที่กรองทราฟฟิกจริง ล็อกจะเต็มไปด้วยบรรทัด Ban 198.51.100.7 อย่างร่าเริงในขณะที่แพ็กเก็ตยังคงมาถึง Debian 12 ใช้ nftables เป็นค่าเริ่มต้น และถ้าเปิด UFW อยู่ ตัวเลือกที่ถูกต้องคือ banaction = ufw ตรวจสอบด้วย:

sudo nft list ruleset | grep -c f2b
sudo iptables -S | grep f2b

อย่างน้อยหนึ่งในสองควรแสดงกฎ และตัวกรองเองก็ทดสอบได้โดยไม่ต้องรอการโจมตี:

sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf

ด้านล่างของผลลัพธ์บอกว่ามีกี่บรรทัดที่ตรงกัน ศูนย์หมายความว่าตัวกรองกับล็อกไม่เจอกัน และไม่มีประโยชน์ที่จะตั้งค่าอะไรต่อ

สิ่งที่ Fail2ban ไม่ทำ

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

ปัญหาอีกเรื่องคือทั้งหมดนี้ต้องมีคนมาดูเป็นครั้งคราว ไม่มีใครพิมพ์ fail2ban-client status ทีละ jail ติดต่อกันหลายสัปดาห์ และจำนวนการแบนที่เพิ่มขึ้นจะถูกสังเกตเห็นเมื่อมีอะไรพังไปแล้ว บนหน้าเดโมด้านล่าง ข้อมูลเดียวกันอยู่บนหน้าเดียว: รายการ jail ใครถูกแบนอยู่ตอนนี้ และความพยายามมาจากที่ใด