tech

UFWのDENYルールが効いていなかった話|ルール順序の落とし穴と修正方法

UFWのDENYルールが効いていなかった話|ルール順序の落とし穴と修正方法

sudo ufw deny from xxx.xxx.xxx.xxx でブロックしたはずの IP から、攻撃が届き続けていました。

発覚したのは、LogWatch(サーバーのログを毎日まとめてメールで送ってくれるツール)のレポートを確認していたときです。 数日前にブロックしたはずの IP が、今日も Nginx のログに記録されている。 最初は設定ミスを疑いましたが、原因はもっと根本的なところにありました。

UFW のルール順序の問題です。

この記事では、なぜ sudo ufw deny from コマンドが場合によって効かないのか、その仕組みと正しい対処法を整理します。

ブロックしたのに、なぜ通り抜けてくるのか

まず、状況を整理します。

UFW でブロックが正しく機能していれば、その IP からのパケット(ネットワークを流れるデータのかたまり)はファイアウォールの時点で遮断されます。 Nginx まで到達できないので、Nginx のログには何も記録されません。

ところが、Nginx のログにはそのリクエストが記録されていました。 Nginx がリクエストを受け取れている、つまり UFW がそのパケットを通してしまっていたということです。

UFW の基本的な仕組みについては、以前の記事で解説しています。

UFW と nftables の違いと fail2ban での使い分け|ban 状況を可視化する設定方法

原因はルールの「順番」にあった

sudo ufw status numbered でルールの一覧を確認しました。

表示されたのは、こんな構造でした。

[ 1] Anywhere   DENY IN  xxx.xxx.xxx.0/24
[ 2] Anywhere   DENY IN  xxx.xxx.xxx.0/24
[ 3] Anywhere   DENY IN  xxx.xxx.xxx.45
[ 4] Anywhere   DENY IN  xxx.xxx.xxx.52
[ 5] Anywhere   DENY IN  xxx.xxx.xxx.xxx
[ 6] Anywhere   DENY IN  xxx.xxx.xxx.62
[ 7] Nginx Full ALLOW IN Anywhere          ← ★ ポート 80/443 をここで許可
[ 8] 22/tcp     ALLOW IN Anywhere
[ 9] Anywhere   DENY IN  xxx.xxx.xxx.115  ← ★ ALLOWより後にある
[10] Anywhere   DENY IN  xxx.xxx.xxx.233
...
[17] Anywhere   DENY IN  xxx.xxx.xxx.xxx  ← ★ ブロックしたかった IP

DENY ルールがずらりと並んでいて、一見ちゃんと設定されているように見えます。 ところが、ルール 7 番に注目してください。

UFW(内部では iptables や nftables が動いています)は、上から順にルールを評価し、最初に一致したルールを適用した時点で処理を終了します。 これを「ファーストマッチ方式」といいます。

ポート 80 や 443( Web アクセス)へのパケットが届いたとき、処理の流れはこうなります。

パケット到着(送信元: xxx.xxx.xxx.xxx、宛先: ポート 80)
  ↓
ルール 1〜6: 送信元 IP が一致しない → スキップ
  ↓
ルール 7: Nginx Full = ポート 80/443 を ALLOW → ✅ ここで「通過」が決定
  ↓
ルール 8〜17: 到達しない(処理はすでに終わっている)

ルール 17 の DENY は、ルール 7 の ALLOW に先にマッチされてしまい、永遠に評価されない状態になっていたのです。

なぜこうなってしまったのか

sudo ufw deny from [IP アドレス] は、ルールを常にリストの末尾に追加します。 これは UFW の仕様です。

サーバーの初期設定では、Nginx Full の ALLOW ルールがすでに存在しています。 そこに後から DENY を追加すると、必ず ALLOW より下に追加されます。

初期状態
 [ 1] Nginx Full ALLOW IN Anywhere
 [ 2] 22/tcp     ALLOW IN Anywhere

↓ sudo ufw deny from xxx.xxx.xxx.xxx を実行

追加後
 [ 1] Nginx Full ALLOW IN Anywhere         ← ALLOW が先にある
 [ 2] 22/tcp     ALLOW IN Anywhere
 [ 3] Anywhere   DENY IN  xxx.xxx.xxx.xxx  ← 末尾に追加された DENY

これが、sudo ufw deny from を使う限り、後から追加した DENY ルールはポート 80/443 に対しては一切機能しない理由です。 Ubuntu の公式ドキュメントにも「特定ルールは汎用ルールより前に置かなければならない」と明記されています。

この問題に気づきにくい理由が 2 つあります。

ひとつは、sudo ufw status numbered でルール一覧を確認すると DENY ルールとして「表示はされている」こと。 設定済みだと思い込んでしまいます。

もうひとつは、Nginx がエラーレスポンスを返している場合、「Nginx が拒否した」ように見えること。 UFW が通してしまっていることに気づきません。

修正方法:ALLOW ルールを末尾に移動する

対応策は 2 つありました。

案 A:各 DENY ルールを ALLOW より前に一つずつ挿入し直す → ルールの数だけ操作が必要で手間がかかる。将来また同じ問題が起きるリスクもある。

案 B:ALLOW ルール 2 つを削除して末尾に再追加する → DENY ルールがいくつあっても、ALLOW を末尾に動かすだけで一括解決できる。

案 B を採用しました。

まず、現在のルール番号を確認します。

bash
sudo ufw status numbered

ALLOW ルールの番号を確認したら、後ろの番号から順に削除します(前から削除すると番号がずれるため)。

bash
# 例: 8 番が 22/tcp、7 番が Nginx Full の場合 sudo ufw delete 8 sudo ufw delete 7

削除できたら、末尾に再追加します。

bash
sudo ufw allow "Nginx Full" sudo ufw allow 22/tcp

最後に結果を確認します。

bash
sudo ufw status numbered

修正後のルール順序はこうなります。

[ 1] Anywhere   DENY IN  xxx.xxx.xxx.0/24
[ 2] Anywhere   DENY IN  xxx.xxx.xxx.0/24
...
[15] Anywhere   DENY IN  xxx.xxx.xxx.xxx  ← ブロックしたい IP
[16] Nginx Full ALLOW IN Anywhere         ← ALLOW を末尾へ移動
[17] 22/tcp     ALLOW IN Anywhere

これで、ブロックしたい IP からのパケットはルール 15 で確実に遮断され、ルール 16 の ALLOW には到達しなくなります。

sudo ufw allow "Nginx Full" を実行したとき、Skipping adding existing rule (v6) と表示された場合は、IPv6 のルールがすでに存在していたため重複追加をスキップした正常な動作です。

今後の正しい IP ブロックコマンド

修正後も sudo ufw deny from を使い続けると、また同じ問題が発生します。 今後は insert 1 を使います。

bash
# ❌ 使ってはいけない(末尾追加になる) sudo ufw deny from xxx.xxx.xxx.xxx # ✅ 正しい方法(先頭に挿入する) sudo ufw insert 1 deny from xxx.xxx.xxx.xxx

insert 1 は「現在のルール 1 の前に挿入する」という意味です。 どれだけルールが増えていても、常に ALLOW ルールより前に DENY を配置できます。

サブネット(/24 などの IP 帯)をまとめてブロックしたい場合も同じです。

bash
sudo ufw insert 1 deny from xxx.xxx.xxx.0/24

操作のたびに確認を忘れずに。

bash
sudo ufw status numbered

DENY ルールがすべて Nginx Full の ALLOW より上にあれば正常です。

なお、sudo ufw allowsudo ufw delete などのコマンドによるルール変更は即座に反映されますsudo ufw reload は不要です。

わかったこと

今回の問題をひとことでまとめると、「UFW のルール一覧に表示されていること」と「そのルールが機能していること」は別物だ、ということです。

  • UFW は上から順に評価し、最初に一致したルールで処理が終わる
  • sudo ufw deny from は末尾追加のため、既存の ALLOW より後に配置される
  • ALLOW より後にある DENY は、そのポートに対しては機能しない
  • 正しくは sudo ufw insert 1 deny from で先頭に挿入する

sudo ufw status numbered でルール一覧を確認するとき、「DENY が表示されているか」だけでなく「DENY が ALLOW より前にあるか」を目視で確認する習慣が必要だと実感しました。

この記事が役に立ったら、シェアしていただけると嬉しいです!

関連記事