ipsetで大量のBlock指定を行う
お客様が運用しているWebサーバー(国内向けECサイト)に海外からのアクセスが大量にあり高負荷になって困っているということで、フィルタリング設定のお手伝いをしました。
Webサーバーならapacheやnginxでdeny設定をしてもいいのですが、今回はかなり多くのネットワークアドレスをブロック対象とする必要がありました。不要な通信はできるだけ手前で落とした方がいいので、Kernelのnetfilter(iptablesとかfirewalld)で落すようにしました。
netfilterを使う場合、iptablesやfirewalldでブロック対象のエントリを個別に登録することもできますが、netfilterに大量のエントリ登録をすると再起動時にエントリの追加/削除に時間がかかったり、通信のフィルタリングのマッチングがリスト検索になるので通信性能が落ちてしまいます(図1の左側)。
そこでこのような場合にはipsetを使います。ipsetでは、ipアドレスやネットワークアドレスをまとめてテーブルに登録しておき、netfilterのマッチングの条件として指定することができます。
ipsetではエントリ数が多くてもエントリの追加、削除は高速に行われるのと、マッチングはハッシュ検索(typeによる)で行われるので、通信性能も劣化しません(図1の右側)。
ipsetには操作用にipsetコマンドがありますが、ipsetはfirewalldに統合されておりfirewall-cmdから操作可能です。今回はお客様の環境に合わせてCentOS7のfirewalld環境でipsetを設定する例を示しますが、CentOS6のようなiptables環境でもipsetは使用できます。
firewalld環境でipsetの設定をしていく
それでは、firewalld環境でipsetを設定していく例です。
ipsetを最初に設定する時は大まかに以下のような手順になります。
- (1) ipsetの作成
- (2) 作成したipsetをfirewalldのゾーン(今回はdrop)に追加
- (3) ipsetにブロック対象のアドレスを登録
まずはブロック対象のアドレスを登録するipsetを作成します。ここではblocklistという名前で作成します。
# firewall-cmd --new-ipset blocklist --type=hash:net --permanent
success
(ipsetが作成されているか確認)
# firewall-cmd --get-ipsets --permanent
blocklist
次にblocklistにマッチするアクセス元の通信をdropゾーンに割り当てます。
# firewall-cmd --zone=drop --add-source=ipset:blocklist --permanent success (blocklistがdropゾーンに追加されているか確認) # firewall-cmd --info-zone=drop --permanent drop (active) target: DROP icmp-block-inversion: no interfaces: sources: ipset:blocklist services: ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
これでblocklistにマッチするアクセス元からの通信をブロックする設定となりました。あとはblocklistにブロック対象のIPアドレスを登録していきます。
(10.0.0.0/24をブロック対象に追加) # firewall-cmd --ipset=blocklist --add-entry=10.0.0.0/24 --permanent success (blocklist内のネットワークアドレスを確認) # firewall-cmd --ipset=blocklist --get-entries --permanent 10.0.0.0/24
これまでの設定はpermanentに対するものなので、動作中の設定には反映されていません。firewalldをreloadして実際に設定を有効にします。これで、10.0.0.0/24からの通信を無視するようになります。
(reloadしてpermanent設定を運用中の設定に反映する) # systemctl reload firewalld.service (動作中のblocklistの内容を確認) # firewall-cmd --ipset=blocklist --get-entries 10.0.0.0/24
余談ですが、動作中のipsetの設定ならipsetコマンドでも確認できます。firewall-cmdより引数がシンプルなので、個人的にはこちらが好みです。
# ipset list Name: blocklist Type: hash:net Revision: 6 Header: family inet hashsize 1024 maxelem 65536 Size in memory: 440 References: 7 Number of entries: 1 Members: 10.0.0.0/24
あとはブロック対象のアドレスをipsetに追加していけばいいだけなのですが、ひとつひとつ--add-entryするのは現実的ではないので、最後に設定ファイルから一括更新する手順を説明します。まずは設定ファイルの場所を確認します。
# firewall-cmd --ipset=blocklist --path-ipset=blocklist --permanent /etc/firewalld/ipsets/blocklist.xml
blocklist.xml設定ファイルの中身です。
<?xml version="1.0" encoding="utf-8"?> <ipset type="hash:net"> <entry>10.0.0.0/24</entry> </ipset>
xmlになっているので、ここに<entry>タグでネットワークアドレスを列挙してsystemctl reload firewalld.serviceで反映させることができます。
今回はブロックの設定でしたが、特定のIPリストからのみsshを許可するといったような使い方もできます。ipsetに適切な名前をつけておけば管理もしやすいので、複数のアドレスに対してREJECT,ACCEPT設定したいような場合は、対象アドレスが少ない場合でも最初からipsetで設定しておくと後々便利です。
通信性能の比較
最後にnetfilterで個別にエントリ登録してフィルタリングした場合とipsetを使ってフィルタリングした場合に、実際にどの程度通信速度に影響がでるかを見てみます。
※測定環境はVMWare Fusion上のCentOS7で、ホストのCPUは3.4 GHz Intel Core i5。
(1) フィルター設定がない時
$ ping 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.038 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.040 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.040 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.038 ms 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.039 ms 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.056 ms 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.053 ms ^C --- 127.0.0.1 ping statistics --- 7 packets transmitted, 7 received, 0% packet loss, time 6004ms rtt min/avg/max/mdev = 0.038/0.043/0.056/0.009 ms
自ホストにpingをしただけですが、フィルタリング設定がない初期状態では平均0.043msです。
(2) --add-sourceで2800エントリ登録した時
$ ping 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.136 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.106 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.147 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.101 ms 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.107 ms 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.196 ms 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.121 ms ^C --- 127.0.0.1 ping statistics --- 7 packets transmitted, 7 received, 0% packet loss, time 6004ms rtt min/avg/max/mdev = 0.101/0.130/0.196/0.033 ms
こちらは、netfilterに2800エントリのフィルタリング設定を行った時(図1左側)の結果です。平均が0.130msでICMP Echoの一往復で0.1ms程度遅くなっています。
(3) ipset(2800エントリ)でフィルタリング時
$ ping 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.039 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.042 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.042 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.058 ms 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.043 ms 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.058 ms ^C --- 127.0.0.1 ping statistics --- 6 packets transmitted, 6 received, 0% packet loss, time 5001ms rtt min/avg/max/mdev = 0.039/0.047/0.058/0.007 ms
ipset内に2800エントリを登録してフィルタリング設定を行った時(図1右側)の結果です。平均0.047msで(1)とほぼ同じ時間です。(2)のように通信速度が劣化していないのがわかります。
firewalld環境なら今回の例のようにipsetの設定も簡単にできるので、性能的にも保守性を考えても積極的に使っていくのがいいと思います。
投稿日:2019/07/06 11:43