iptables と netfilterを勉強し直す(ゼロからのfirewall構築)その2

650

前回の続きです

/etc/iptables/rules.v4を編集して基礎ルールを追加する

前回の最後に説明した方法で、基礎的なルールを追加していきます。

vi /etc/iptables/rules.v4
# Generated by iptables-save v1.8.7 on Sun Jan  9 11:57:32 2022
*filter
:INPUT DROP [3:219]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [454:42121]
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p udp -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 ! -i lo -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
COMMIT
# Completed on Sun Jan  9 11:57:32 2022

今回は上の9~12行目を追加します。

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEP

ループバックデバイスを用いた内部通信をINPUT/OUTPUTともに通過させます。

iptables -A INPUT -d 127.0.0.0/8 ! -i lo -j REJECT --reject-with icmp-port-unreachable
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

ICMPエコー(ping)に関しては、こちらからのpingが届かない相手からのリクエストを遮断し、その後で残りすべてのICMPエコーを受け入れます。
 

ファイルを保存したら、以下のコマンドで iptablesにファイルを再読込させます

netfilter-persistent reload

 

iptables -L を使った確認では、どのデバイスを使っているかなどがはっきり表示されないので、今回は iptables -vnL で確認してみましょう


Chain INPUT (policy DROP 107 packets, 18261 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  750 51992 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
    2   204 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
    4   304 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0           
    0     0 REJECT     all  --  !lo    *       0.0.0.0/0            127.0.0.0/8          reject-with icmp-port-unreachable
   10   840 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 8

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

Chain OUTPUT (policy ACCEPT 670 packets, 117K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    6   508 ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0         

通過パケット量、使用するデバイス(Loなのかeth0なのか)、宛先のアドレスすべて詳細に表示されます。基本的にこの繰り返しで作業を進めます。
 

hashlimitを使って攻撃に対処する

iptables には hashlimit というモジュールがあり、特定の宛先からの通信数を制限することができます。ブルートフォースアタックなどに対して有効です。基本的な概念としては、

1.初めて通信を行った相手に回数券を渡す
2.新しく通信をするたびに回数券を1枚づつ消費していく
3.特定の分数ごとに回数券を追加させることができる
4.回数券を使い切ると1時的にBANされて通信できなくなる
5.特定の時間がすぎると、BANリストから削除され、1に戻る

と言った感じのようです。いままでの記述に -m state –state NEW(新規接続に限り有効) を追加して、「宛先ポート番号」と「-j ACCEPT」の間に以下を追記するだけで設定できます。

# Hashlimitモジュールの使用開始
-m hashlimit

# 1分に1枚回数券を追加
–hashlimit 1/m

# 回数券の初期枚数は6枚
–hashlimit-burst 6
       
# 通信は送信元IPとポートのペアで管理する
–hashlimit-mode srcip,dstport

# sshlimitという名前で管理する
–hashlimit-name sshlimit

# 120秒(120000ミリ秒)でリセットする
–hashlimit-htable-expire 120000

つまり、192.168.1.100というアドレスを持つ機器が22番ポートにアクセスする場合、最初の1分は6回までアクセス可能、それ以降は1分に1回だけアクセス可能、通信を2分間停止させた場合は、また1分6回までアクセス可能……というように「不必要な回数の通信を行う相手」に対して、門戸を狭めるように働きます。
 

具体的には、/etc/iptables/rules.v4 のsshに関する記述を以下のように書き換えます。

-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -m hashlimit --hashlimit 1/m --hashlimit-burst 20 --hashlimit-mode srcip,dstport --hashlimit-name sshlimit --hashlimit-htable-expire 120000 -j ACCEPT

それから reload します

netfilter-persistent reload

 

実際に連続してssh接続を仕掛けると、7回目でブロックされて通信できなくなります。サーバ内に /proc/net/ipt_hashlimit/{定義した管理名} ファイルが存在していたら、ブロックされている証拠です。ファイルには、「カウント秒数、ソースIP、宛先IP、ポート、それからブロック管理に使っているのであろう謎の数値」が書かれています。

cat /proc/net/ipt_hashlimit/sshhlimit
55 192.168.1.140:0->0.0.0.0:22 10824142247960832 24739011540000000 8246337180000000

 

なお、hashlimitにはセーフリスト(ホワイトリスト)的なものはありません。セーフリストを設定したい場合は前回説明した「iptablesは上から順に処理される」というルールを思い出して、hashlimitの記述の1行上に以下を書いておきましょう。192.168.1.1からの22番ポートへの通信はすべて許可されます。

-A INPUT -s 192.168.1.1 -p tcp -m tcp --dport 22 -j ACCEPT

 

次回に続く)