iptables の hashlimit で HTTP DDoS を IP 単位にレート制限する
⚠️ これはメモ書きです iptables / netfilter / hashlimit の挙動を完全に理解した上で書いた記事ではなく、自宅で食らった時に動いた設定を後追いで整理しただけのもの。 各オプションの意味は man と AI 説明に頼っていて、自分の中で「腹落ちした」状態には至っていない。 同じ状況で困っている人の取っ掛かりにはなるかもしれないが、本番投入する前に man と他の解説記事で必ず裏取りしてほしい。
まとめ
- iptables の
hashlimitモジュールで、送信元 IP ごとに独立したトークンバケットを持たせて HTTP/HTTPS をレート制限できる - ユーザー定義チェーンに「正常レートなら RETURN」「超過したら DROP」を 2 段で組むのが定番形
--hashlimit-burstを 100 くらい残しておくと、ブラウザの並列リクエストを潰さずに済む
想定するケース
自宅サーバや小規模 VPS の 80/443 に、じわじわ高頻度のリクエストが来ている。CDN や WAF を挟まずに、iptables だけで送信元 IP ごとのレート制限をかけたい。自宅で実際に食らった時にこの形で凌いだので、その手順をまとめる。
完成形
# 1. チェーン作成
iptables -N HTTP_DDOS
# 2. 正常レートの範囲内なら戻す
iptables -A HTTP_DDOS -p tcp -m multiport --dports 80,443 \
-m hashlimit \
--hashlimit 1/s \
--hashlimit-burst 100 \
--hashlimit-htable-expire 300000 \
--hashlimit-mode srcip \
--hashlimit-name t_HTTP_DDOS \
-j RETURN
# 3. 超過分はログを残して落とす
iptables -A HTTP_DDOS -j LOG --log-prefix "HTTP_DDOS_DROP: " --log-level 4
iptables -A HTTP_DDOS -j DROP
# 4. INPUT から飛ばす
iptables -I INPUT -p tcp -m multiport --dports 80,443 -j HTTP_DDOS各オプションの意味
マッチ条件
| オプション | 意味 |
|---|---|
-A HTTP_DDOS | HTTP_DDOS チェーンの末尾に追加 |
-p tcp -m multiport --dports 80,443 | TCP の宛先ポート 80 または 443 |
hashlimit パラメータ
hashlimit は標準の limit モジュールの上位版で、キーごとに独立したトークンバケットを持てる。
| オプション | 意味 |
|---|---|
--hashlimit 1/s | バケットの補充レート。1 秒あたり 1 トークン |
--hashlimit-burst 100 | バケットの最大容量。100 トークンまで貯められる |
--hashlimit-mode srcip | 集計のキー。送信元 IP ごとに独立したバケットを持つ |
--hashlimit-name t_HTTP_DDOS | /proc/net/ipt_hashlimit/t_HTTP_DDOS で覗ける名前 |
--hashlimit-htable-expire 300000 | 300,000 ms = 5 分アイドルした IP のエントリを破棄 |
アクション
-j RETURN で、マッチしたパケット(=バケットにトークン残あり)は呼び出し元チェーンに戻る。落とすのは末尾の -j DROP に任せる。
動作イメージ
ある送信元 IP から HTTP パケットが届くと:
- その IP 用のバケット(容量 100、補充 1/s)を引く
- トークン残あり →
-j RETURNでチェーンを抜けて通常処理へ - トークン枯渇 → このルールにマッチしないので次のルール(LOG → DROP)に進む
なぜ burst を 100 にするのか
1/s だけだと「ブラウザが画像 10 個を並列に取りに行く」場面で即 DROP される。burst 100 は「平常時は 1 秒 1 リクエストで補充するけど、瞬間的には 100 まで貯められる」という意味。
つまり
- ブラウザの並列読み込み → バースト枠でこなす
- 持続的に毎秒数十リクエスト → 補充が追いつかず DROP
という棲み分けになる。実トラフィックに合わせて 50〜200 で調整する。自分の場合は 100 で実用上困らなかった。
動いているか確認する
/proc/net/ipt_hashlimit/t_HTTP_DDOS を見ると、現在保持されている IP ごとのトークン残量・最終アクセス時刻が確認できる。「特定の IP がブロックされ続けているか」を知りたい時はここが速い。
DROP 数は iptables -L HTTP_DDOS -v -n でルールごとのカウンタが取れる。
落とし穴
- RETURN だけ書いて DROP を書き忘れると、超過パケットも結局チェーン末尾の暗黙 RETURN で抜けて通常処理に進んでしまう。これだとレート制限が効かない
- IPv6 で HTTP を受けている環境では
ip6tables側にも同じ設定が要る - IP 単位の制限なので、NAT 配下の大規模ユーザー(モバイルキャリアや学校・企業)を巻き込んで巻き添え DROP する可能性がある。CDN 越しだと送信元 IP は CDN のものになるので、この設定はオリジンサーバ直撃のケース向き