את Fail2ban מתקינים ראשון, ובדרך כלל שם גם הכול נגמר: החבילה בפנים, השירות רץ, ו-sshd לכאורה מכוסה. חצי שנה אחר כך מתברר שהכלא קורא קובץ שאינו קיים במערכת הזאת, ושכל השאר — הדואר, לוח הבקרה, טופס הכניסה באתר — מעולם לא היה מכוסה כלל.

נעבור על מה שכדאי להפעיל ב-VPS רגיל שיש עליו אתר, אילו מספרים לשים שם, ואיך לוודא שהכלא עובד ולא רק קיים בהגדרות.

ראשית בדקו ש-sshd תופס משהו

פקודה אחת:

sudo fail2ban-client status sshd

בפלט יש שורות Currently failed, Total failed ו-Total banned. אם Total failed הוא אפס בשרת שנמצא באינטרנט לפחות יממה, הכלא אינו עובד. השוו את זה למציאות:

sudo lastb | wc -l

אלפי ניסיונות כושלים ב-lastb מול אפסים ב-Fail2ban אומרים בדיוק דבר אחד: המסנן מסתכל במקום הלא נכון.

הסיבה הנפוצה ביותר היא Debian 12. הוא כבר אינו מתקין את rsyslog כברירת מחדל, הקובץ /var/log/auth.log פשוט אינו קיים, והכלא הסטנדרטי 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.

הכלא שמשנה את התמונה: recidive

לכלא רגיל יש זיכרון קצר: חמישה ניסיונות בעשר דקות, שעת חסימה, וכעבור שעה הכול מתחיל מחדש. בוט חי עם זה בנוחות ויחזור מחר ומחרתיים. recidive סוגר בדיוק את הפרצה הזאת: הוא קורא לא את יומן המערכת אלא את היומן של Fail2ban עצמו, כלומר חוסם את מי ש-Fail2ban כבר חסם.

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

קראו את זה כ«נחסם חמש פעמים ביממה, נעלם לשבוע». הכלא זקוק לקובץ /var/log/fail2ban.log, ולכן אם העברתם את הרישום של Fail2ban עצמו ל-syslog, גם recidive יזדקק ל-backend = systemd.

בפועל ההשפעה של הכלא האחד הזה מורגשת בדרך כלל יותר מכוונון עדין של כל האחרים: הקבועים נושרים, וביומנים נשאר רק רקע טרי.

מה עוד להפעיל כשיש אתר בשרת

בסדר יורד של תועלת:

  • nginx-http-auth או apache-auth — ניחוש מול אימות בסיסי. נחוץ אם אזור הניהול או אתר בדיקה מוגנים בסיסמה של שרת האתר;
  • nginx-botsearch — סריקה של נתיבים מוכרים: /wp-login.php, /phpmyadmin, /.env. זו אינה פריצה אלא תצפית, והיא זו שקודמת לכל השאר;
  • nginx-limit-req — עובד רק אם limit_req_zone מוגדר ב-Nginx עצמו; בלעדיו הכלא מופעל וחסר תועלת;
  • 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 לכל כלא במשך שבועות, ועלייה במספר החסימות נקלטת אחרי שמשהו כבר נשבר. בעמודי ההדגמה למטה אותם נתונים מקובצים בעמוד אחד: רשימת הכלאים, מי חסום כרגע, ומאיפה מגיעים הניסיונות.