Home > ブログ > ipsetで大量のBlock指定を行う

ブログ

ipsetで大量のBlock指定を行う

お客様が運用しているWebサーバー(国内向けECサイト)に海外からのアクセスが大量にあり高負荷になって困っているということで、フィルタリング設定のお手伝いをしました。

Webサーバーならapacheやnginxでdeny設定をしてもいいのですが、今回はかなり多くのネットワークアドレスをブロック対象とする必要がありました。不要な通信はできるだけ手前で落とした方がいいので、Kernelのnetfilter(iptablesとかfirewalld)で落すようにしました。

netfilterを使う場合、iptablesやfirewalldでブロック対象のエントリを個別に登録することもできますが、netfilterに大量のエントリ登録をすると再起動時にエントリの追加/削除に時間がかかったり、通信のフィルタリングのマッチングがリスト検索になるので通信性能が落ちてしまいます(図1の左側)。

ipsetの例
図1 ipsetを使わなった場合と使った場合

そこでこのような場合には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

タグ: Linux Server 作業実績

Webサーバーのことで何かお困りですか?
ちょっとしたことから対応できます。
お困りのことがございましたらお気軽にお問いあわせください。
詳細はこちら

PHP8対応作業はこちら

Top

アーカイブ

タグ

Server (24) 作業実績 (17) PHP (15) プログラミング (15) ネットワーク (12) C (8) OpenSSL (8) C++ (7) PHP関連更新作業 (7) EC-CUBE (6) Webアプリ (6) Nginx (5) AWS (4) 書籍 (4) laravel (4) Linux (4) JavaScript (4) Rust (3) Vue.js (3) 与太話 (3) Symfony (2) Golang (2) OSS (1) MySQL (1) お知らせ (1) デモ (1) CreateJS (1) Apache (1)