
WordPress を外部公開していると、存在しない install.php を探しに来るスキャナーや、?author=1 などのユーザー名列挙ボットが 24 時間絶え間なく押し寄せます。
これらに対し、HTTP 404 や 403 のエラーページを律儀に返していては、Nginx のワーカープロセスや TCP コネクション、さらにはストレージのログ書き込みリソースが無駄に浪費されます。
本稿では、「悪意あるスキャンを Nginx のハニーポット(罠)で即座に通信破棄(HTTP 444)し、そのアクセスログをトリガーにして fail2ban が OS のファイアウォール層(iptables / nftables)でパケットごと即時一発 BAN する」 完全自動迎撃の構築手順を徹底的に深掘りします。
1. 迎撃アーキテクチャと処理フロー
目指すのは、「攻撃者に 1 バイトの情報も与えず、2 回目のアクセスすら許さない」防御ラインです。
Plaintext
[ 攻撃者 / 脆弱性スキャナー ]
│
│ ① 罠URL (install.php / ?author=1 等) へリクエスト
▼
┌────────────────────────────────────────────────────────┐
│ [ Nginx Web サーバー ] │
│ ├─ 罠にマッチ ──▶ 【 HTTP 444 】で即座に TCP 切断 │
│ │ (HTMLやヘッダーすら返さず接続強制終了) │
│ │ │
│ └─ 高速 RAM ディスク上のアクセスログへ "444" を書き込み │
└────────────────────────────────────────────────────────┘
│
│ ② ログの "444" を常時監視 (inotify)
▼
┌────────────────────────────────────────────────────────┐
│ [ fail2ban デーモン ] │
│ ├─ フィルターで悪質スキャンと判定 │
│ └─ 【 maxretry = 1 】:猶予ゼロで即座に監獄へ投獄 │
└────────────────────────────────────────────────────────┘
│
│ ③ カーネルへ遮断命令を発行
▼
┌────────────────────────────────────────────────────────┐
│ [ OS パケットフィルター (iptables / nftables) ] │
│ └─ 攻撃元 IP からの全通信を DROP (24時間完全遮断) │
└────────────────────────────────────────────────────────┘なぜ return 444; と maxretry = 1 なのか?
- HTTP 444 (No Response):Nginx 独自の特殊ステータスコード。サーバーはクライアントに対して HTTP レスポンスヘッダーすら返さず、TCP コネクションを即座に DROP / RST します。攻撃側のスキャナーはタイムアウトや切断エラーで待たされ、こちらの CPU リソース消費は最小限に抑えられます。
- maxretry = 1 (一発 BAN):通常の運用において、正規の閲覧者や検索エンジンのクローラーが
wp-admin/install.phpやauthor=1を叩くことは 100% あり得ません。したがって、複数回の試行を待つ猶予(リトライカウント)は不要であり、1 回触れた瞬間にブラックリストへ放り込む のが最も安全かつ合理的です。
2. Step 1:Nginx 側に「踏んだら即死」の罠(ハニーポット)を仕掛ける
サーバー設定ファイルに、典型的な攻撃パターンを捕獲するトラップロケーションを配置します。
対象設定ファイル:/etc/nginx/sites-available/example.com
Nginx
# =========================================================
# 🛡️ fail2ban 迎撃用ハニーポット設定
# =========================================================
# 1. 初期インストールファイルの探索(運用開始後は絶対に使われない)
location = /wp-admin/install.php {
return 444;
}
# 2. WordPress ユーザー名の列挙スキャン (?author=1 等)
if ($query_string ~* "^author=[0-9]+") {
return 444;
}
# 3. 古いマニフェストファイル・外部XMLRPCの悪用
location ~* /(wlwmanifest\.xml|xmlrpc\.php) {
return 444;
}
# 4. 典型的なバックアップ・設定ファイルの探索
location ~* \.(?:env|sql|bak|old|config|ini)$ {
return 444;
}- ポイント: ここで
deny all;(403 Forbidden)を使わずにreturn 444;を指定するのが肝です。403 を返すと「ファイルは存在するが拒否された」というヒントをスキャナーに与えてしまいますが、444 はサーバーの存在自体を疑わせる挙動になります。
3. Step 2:fail2ban フィルターの作成(正規表現の定義)
Nginx がアクセスログに刻んだ 444 ステータスを正確に拾い上げる fail2ban フィルターを作成します。
作成ファイル:/etc/fail2ban/filter.d/nginx-wp-scan.conf
INI
[Definition]
# Nginx の Combined ログフォーマットに対応した正規表現
# 罠にかかって HTTP 444 を返したリクエストのみをターゲットにする
failregex = ^<HOST> - .* "(GET|POST|HEAD) /(wp-admin/install\.php|wlwmanifest\.xml|xmlrpc\.php).*" 444
^<HOST> - .* "(GET|POST|HEAD) /.*\?author=[0-9]+.*" 444
^<HOST> - .* "(GET|POST|HEAD) /.*\.(env|sql|bak|old|config|ini).*" 444
ignoreregex =<HOST>は fail2ban の定義済みマクロで、IPv4 および IPv6 アドレスの両方に自動でマッチします。- 末尾に
444を指定することで、正常な通信(200番台や300番台)を誤って拾うリスクを完全に排除しています。
4. Step 3:Jail(監獄)の設定と RAM ディスク対応
フィルターを適用し、ファイアウォール(iptables)にルールを注入する Jail 定義を作成します。
作成ファイル:/etc/fail2ban/jail.d/wordpress-scan.local
INI
[nginx-wp-scan]
enabled = true
port = http,https
filter = nginx-wp-scan
# 高速化のために RAM ディスクへログを逃がしている場合の実パスを指定
logpath = /mnt/ramdisk/logs/nginx/*.access.log
# 監視方式(systemd ではなくファイル直読みの場合は pyinotify または gamin)
backend = auto
# 1回でも罠を踏んだら即時BAN
maxretry = 1
# 検知ウィンドウ(秒):10分間の間に1回でも合致したら発動
findtime = 600
# 遮断時間(秒):86400秒 = 24時間
bantime = 86400
# 遮断アクション:iptables で HTTP(80) / HTTPS(443) へのパケットをDROP
action = iptables-multiport[name=WPScan, port="http,https"]チューニングの技術ポイント
- RAM ディスク(tmpfs)ログの監視:SSDの寿命保護やI/Oボトルネック解消のためにログを
/mnt/ramdisk/等のメモリ上に配置している場合でも、logpathを正しく指定すれば問題なく動作します。*.access.logとワイルドカードを指定することで、複数ドメインのログを一括で監視可能です。 bantime = 86400(24時間):短時間の BAN(10分〜1時間)だと、解除された直後に同じボットから再びスキャンを受けます。24時間〜数日間締め出すことで、ボット側のスキャン対象リストから自然脱落させます。
5. 動作テストと運用管理コマンド
設定が完了したら、サービスを再起動してテストします。
① 設定の構文チェックと反映
Bash
# Nginx 設定のテストとリロード
sudo nginx -t
sudo systemctl reload nginx
# fail2ban の設定テストと再起動
sudo fail2ban-client -d | grep nginx-wp-scan
sudo systemctl restart fail2ban② フィルターのテスト(過去ログに対する検証)
既存のログに対して、作成した正規表現が意図通りマッチするかテストします。
Bash
sudo fail2ban-regex /mnt/ramdisk/logs/nginx/example.com.access.log /etc/fail2ban/filter.d/nginx-wp-scan.conf- 出力の最下部に
Failures: XX (matched)と表示されれば、正規表現は正常に動作しています。
③ 監視ステータスと BAN リストの確認
Bash
sudo fail2ban-client status nginx-wp-scan正常稼働時の出力例:
Plaintext
Status for the jail: nginx-wp-scan
|- Filter
| |- Currently failed: 0
| |- Total failed: 48
| `- File list: /mnt/ramdisk/logs/nginx/example.com.access.log
`- Actions
|- Currently banned: 5
|- Total banned: 5
`- Banned IP list: 198.51.100.23 203.0.113.88 ...④ iptables 側での確認
カーネルのフィルタリングテーブルに、fail2ban が挿入した DROP ルールが反映されているか確認します。
Bash
sudo iptables -L f2b-WPScan -n -vREJECTまたはDROPのターゲットとして、BAN された IP アドレスがずらりと並んでいれば成功です。
⑤ 誤爆時の手動 BAN 解除(リカバリ手順)
万が一、自分自身の IP や管理回線が BAN されてしまった場合の解除コマンドです。
Bash
sudo fail2ban-client set nginx-wp-scan unbanip <解除したいIPアドレス>まとめ:この構成がもたらす運用メリット
- ゼロリソース防御の実現:ボットが罠に触れた初弾は Nginx の 444 で即時切断され、2 発目以降のアクセスは Nginx に届く前(OS の iptables 層)でパケットごと無言で破棄されます。
- ログ肥大化の抑止:執拗なスキャンアタックが続いても、アクセスログに無駄な行が何千行も刻まれることがなくなります。
- 自宅サーバーの安全圏維持:ポートスキャナーや既知の脆弱性探索スクリプトを機械的かつ冷徹に門前払いし続けるため、管理者のメンテナンス負荷を極小化できます。


コメント