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 はそもそも存在せず、標準の sshd jail はまさにそのファイルを読むように設定されています。これについてエラーは一切出ません。サービスは起動し、ステータスは表示され、カウンタはゼロのままです。直し方は systemd のジャーナルへ切り替えることです。
[sshd]
enabled = true
backend = systemd
もう一つの選択肢は、平文の auth.log が他のツールに必要なら rsyslog をパッケージとして戻すことです。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——基本認証に対するパスワード総当たり。管理領域やステージングサイトが Web サーバーのパスワードの背後にあるなら必要です;nginx-botsearch——既知のパスの探索:/wp-login.php、/phpmyadmin、/.env。これは侵入ではなく偵察であり、他のすべてに先立ちます;nginx-limit-req——Nginx 側にlimit_req_zoneが定義されている場合にのみ機能します。無ければ jail は有効でも役に立ちません;postfix-saslとdovecot——自前でメールを運用しているなら必須です。メールボックスへのパスワード推測は絶え間なく行われ、たいてい誰も見ていません。
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
サイト自身のログインフォームは別の話です。アプリケーションには失敗ログインの独自ログがなく、access.log では失敗した試行は 200 や 302 を返す普通の POST にしか見えません——どんな汎用フィルタでも成功と区別できないのです。道は二つ。アプリケーションに失敗を syslog へ書かせるか(たいていのコンテンツ管理システムにはそのためのプラグインがあります)、ログインページへのリクエスト頻度を制限するかです。後者は検知器ではなく制限器であり、それを先に知っておくほうがよいでしょう。
数字について
bantime = 10m こそ、Fail2ban を役立たずと判断させてしまう既定値です。十分あればボットは戻ってきます。最初の遮断に一時間、加えて一週間の recidive のほうが、永久遮断よりずっとうまく働きます。永久遮断は時間とともに果てしなく長いルール一覧に化けます。
SSH に maxretry = 3 は自分を締め出す確実な方法です。十分間に五回でも総当たりは同じくらいよく断ち切れます。
ゆっくりした推測——五分に一回——は決して 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)です。
別の問題として、これらは時折誰かが見る必要があります。何週間も jail ごとに fail2ban-client status を打つ人はいませんし、遮断件数の増加は何かが壊れた後になって気づかれます。下のデモページでは同じデータが一つのページに載っています。jail の一覧、いま誰が遮断されているか、そして試行がどこから来ているかです。