ログを監視して、特定のアタックパターンを見付けたらアクションを起こす fail2ban と言うツールがあるんですが、これはディフォルトでは
iptables -I <fail2ban-targetchain> -s <banned-ip> -j DROP
みたいなコマンドラインを発行する。
アタック元が少ない場合はこれで問題ないのだけど、 パケットを突然叩き落とす様になるので 同時に多数のアドレスを遮断すると問題が起きる。
TCPスタック的には単にクライアントが応答しなくなったように見えるので、 タイムアウトまで待機する。 従って、短い時間に多数のクライアントから攻撃を受けた場合、 そのサーバの接続上限まで全てがタイムアウト待ちのコネクションで 埋めつくされる事になる。
これを防ぐには何とかして TCP をリセットするしかない。
"-j DROP" をやめて "-j REJECT --reject-with tcp-reset" にすると、 拒否された接続元には RST が飛んで即座に切れるが、サーバ側は分からないので やっぱり待ってしまう。
そこでさらに、サーバ側に届けば切れるようなパケットは通すようにしてみる。 具体的にはこんな感じ。
iptables -I <fail2ban-targetchain> 1 -s <banned-ip> -p tcp --tcp-flags RST,FIN NONE -j REJECT --reject-with tcp-reset
RST を受け取ったクライアントが RST+ACK を返す事を期待して 素通りする様にしている。 サーバには突然変な RST+ACK を受け取る事になるので、 何れにしろ TCP 接続は リセットされる。FIN が付いているのはおまけ。
やってみた所大体巧く言っているようだ。 TCP スタックまで弄っている様な物には対処できないけど、 そういうのはあまりなさそう。