大量のアクセスを試みる迷惑な接続元を制限する(iptables hashlimit)


iptablesを使用して一定時間あたりの接続数を制限する方法です。
例えば、自サーバへのブルートフォース攻撃に対して制限を実施し、攻撃の効率を落とすことができます。
攻撃者をIPアドレスで特定できない場合や、サービスの性質上IPアドレスで制限できない場合(SMTP等)、また正規の利用者に影響させたくない場合等に有効です。

イメージはこんな感じ
![IPTABLES_HASHLIMIT.png](https://qiita-image-store.s3.amazonaws.com/0/63424/a51b2783-675e-caf8-2e96-c8557989e86c.png)

#構成 
全体での制限を行う場合（例：port22に対する通信を一括して制限する場合）は`limit`を使用しますが、クライアントIPアドレス等を元に個別に制御する際は`hashlimit`を使用します。

hashlimit制御イメージはこのような感じです

* 通信を行うクライアントは「送信元IPアドレス」と「宛先ポート」でまとめられて**対象グループ**となります。まとめ方は「--hashlimit-mode srcip,dstport」で指定しています
* **対象グループ**ごとに**チケット**が割り当てられます。**チケット**を持っていると**対象グループ**は通信を行うことができます。通信を行うごとにチケットを消費します。
* 初期状態では30枚のチケットを持っています。その枚数は 「--hashlimit-burst 30」で指定しています
* **チケット**は1分ごとに1枚追加されます。追加のペースは「--hashlimit 1/m」で指定しています
* **チケット**は最大で30枚持つことができます。あふれた**チケット**は削除されます。最大数は「--hashlimit-burst 30」で指定しています（初期枚数と同じ）
* **対象グループ**の**チケット情報**は通信が無くなってから120秒後に削除されます。情報の保存期間は「--hashlimit-htable-expire 120000」で指定しています

(備考：**チケット**,**対象グループ**などの言葉は説明の為に付けたもので、一般的な名称ではありません)

#設定例
ICMPに対して制限を設定してみます

````lang:hashlimit.sh
# HASHCHECK チェインの作成
iptables -N HASHCHECK

# HASHLIMITを使用して接続数の制限実施し許可する
#  -m hashlimit ・・・hashlimitモジュールを使用
#  --hashlimit-name hashcheck_t ・・・ハッシュテーブル名を「hashcheck_t」とする
#  --hashlimit 1/m ・・・時間あたりの接続数を「1/分」に設定
#  --hashlimit-burst 30 ・・・バースト値(上記--hashlimit値を無視して接続できる数)を「30」に設定
#  --hashlimit-mode srcip,dstport ・・・制限対象を「送信元IPアドレス」と「宛先ポート」で識別する
#  --hashlimit-htable-expire 120000 ・・・ハッシュテーブル中のレコード保持期間を「120000ミリ秒」に設定
iptables -A HASHCHECK -m hashlimit --hashlimit-name hashcheck_t \
         --hashlimit 1/m --hashlimit-burst 30 --hashlimit-mode srcip,dstport  \
         --hashlimit-htable-expire 120000 -j ACCEPT

# 許可されなかった通信をログに記録する
iptables -A HASHCHECK -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES HASH DROP] : '

# 許可されなかった通信をDROPする
iptables -A HASHCHECK -j DROP

# チェインを適応
iptables -A INPUT -p icmp  -j HASHCHECK #サンプル ICMPを制限
````
実際に使用する場合は、HASHLIMITでの制限を行う前に以下の処理を行ったほうがよいでしょう。

* 信頼できるIPアドレス/IPアドレス帯からの通信を許可(誤爆防止)
* 確立済み通信[ELATED,ESTABLISHED]の許可(確立済みの通信に対してhashlimit実施することは余り無いように思えます)

#確認

チェインの状態を確認

````
# iptables -L -vn
Chain INPUT (policy ACCEPT 31 packets, 2332 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 HASHCHECK  icmp --  *      *       0.0.0.0/0            0.0.0.0/0

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 16 packets, 2544 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain HASHCHECK (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           limit: up to 1/min burst 30 mode srcip-dstport htable-expire 120000
    0     0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0           limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix `[IPTABLES HASH DROP] : '
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0
````

PINGが制限されるか確認する

````
$ ping SERVER
PING SERVER (*.*.*.*) 56(84) bytes of data.
64 bytes from SERVER: icmp_seq=1 ttl=64 time=62.9 ms
64 bytes from SERVER: icmp_seq=2 ttl=64 time=0.186 ms
64 bytes from SERVER: icmp_seq=3 ttl=64 time=0.195 ms
64 bytes from SERVER: icmp_seq=4 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=5 ttl=64 time=0.189 ms
64 bytes from SERVER: icmp_seq=6 ttl=64 time=0.187 ms
64 bytes from SERVER: icmp_seq=7 ttl=64 time=0.188 ms
64 bytes from SERVER: icmp_seq=8 ttl=64 time=0.201 ms
64 bytes from SERVER: icmp_seq=9 ttl=64 time=0.187 ms
64 bytes from SERVER: icmp_seq=10 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=11 ttl=64 time=0.186 ms
64 bytes from SERVER: icmp_seq=12 ttl=64 time=0.191 ms
64 bytes from SERVER: icmp_seq=13 ttl=64 time=0.190 ms
64 bytes from SERVER: icmp_seq=14 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=15 ttl=64 time=0.184 ms
64 bytes from SERVER: icmp_seq=16 ttl=64 time=0.182 ms
64 bytes from SERVER: icmp_seq=17 ttl=64 time=0.188 ms
64 bytes from SERVER: icmp_seq=18 ttl=64 time=0.195 ms
64 bytes from SERVER: icmp_seq=19 ttl=64 time=0.186 ms
64 bytes from SERVER: icmp_seq=20 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=21 ttl=64 time=0.185 ms
64 bytes from SERVER: icmp_seq=22 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=23 ttl=64 time=0.189 ms
64 bytes from SERVER: icmp_seq=24 ttl=64 time=0.183 ms
64 bytes from SERVER: icmp_seq=25 ttl=64 time=0.194 ms
64 bytes from SERVER: icmp_seq=26 ttl=64 time=0.189 ms
64 bytes from SERVER: icmp_seq=27 ttl=64 time=0.191 ms
64 bytes from SERVER: icmp_seq=28 ttl=64 time=0.192 ms
64 bytes from SERVER: icmp_seq=29 ttl=64 time=0.200 ms
64 bytes from SERVER: icmp_seq=30 ttl=64 time=0.190 ms  <-ここでチケット(バースト分)を使い果たした
*(この間30秒)*
64 bytes from SERVER: icmp_seq=62 ttl=64 time=0.188 ms  <-通信開始から1分経過したためチケットが1枚追加され通信できた
*(この間60秒)*
64 bytes from SERVER: icmp_seq=122 ttl=64 time=0.190 ms <-また1分経過したためチケットが1枚追加され通信できた
*(この間60秒)*
64 bytes from SERVER: icmp_seq=182 ttl=64 time=0.188 ms <-また1分経過したためチケットが1枚追加され通信できた
*(この間60秒)*
64 bytes from SERVER: icmp_seq=242 ttl=64 time=0.188 ms <-また1分経過したためチケットが1枚追加され通信できた
````