server-memo.net

firewalld + ipset でアクセス制限

   

「CentOS 7」環境の「firewalld」で特定の国からのアクセスを拒否する設定を行ったところ、ルールが4000~5000とかなり多くなってしまいました。

その結果「firewalld」の動作がかなり重くなってしまい、良い方法を色々と調べてみたところ「ipset」と組み合わせて使用することで幸せになれるという情報が見つかりましたので、導入した際の手順についてまとめました。

ipsetとは

IPアドレスやネットワークアドレス、ポート番号、インターフェイス名などを組み合わせて管理することができるユーティリティになります。

「firewalld」で一つ一つルールを追加するよりも、格段にパフォーマンスも向上します。

ipsetインストール

まずは「yum」で「ipset」をインストールします。

# yum install ipset

セットの作成

「ipset」を使用するには、まずセット(ポリシー名みたいなもの)作成してから、そこにネットワークアドレスやIPアドレス等を追加していく必要があります。

セットを作成するためコマンドは下記の通りとなります。

ipseet create セット名 タイプ名 オプション

セット名

セット名はどのようなルールを管理しているのかが分かりやすい名前を設定します。

今回はアクセスを禁止するネットワークアドレスをまとめて管理するため「BLACKLIST」と付けることにします。

タイプ名

作成するセットの種類を指定します。

指定できるタイプは色々とあるのですが、今回使用するタイプはネットワークアドレスを管理するための「hash:net」を指定します。

タイプ一覧

指定できるタイプについて簡単にまとめて見ました。

タイプ 管理する情報
hash:ip hashを使用してIPアドレスを管理
hash:net hashを使用してネットワークアドレスを管理
hash:ip,port hashを使用してIPとポート番号を管理
hash:net,port hashを使用してネットワークアドレスとポート番号を管理
hash:ip,port,ip hashを使用してIPとポート番号の2つを管理
hash:ip,port,net hashを使用してIPとポート番号とネットワークアドレスの3つを管理
hash:net,iface hashを使用して、ネットワークアドレスとNICの2つを管理
bitmap:ip IPアドレスを管理
オプションでrange指定が必要
bitmap:ip,mac IPアドレスとMACアドレスの管理
オプションでrange指定が必要
bitmap:port ポート番号を管理
オプションでrange指定が必要
list:set 複数のセットを管理することができる

オプション

今回はすべてデフォルト設定でいきますので、特にオプションの指定は行いません。

オプション一覧

「ipset create」でセット作成する際に使用できるオプションの一覧です。

オプション 説明 使用可能タイプ
family inet(IPv4)かinet6(IPv6)を設定
デフォルトはinet
IPを使用するタイプ全般
hashsize ハッシュのサイズ
デフォルトは1024
hashを使用するタイプ全般
maxelem 登録できるルールの数
デフォルトは65536
hashを使用するタイプ全般
netmask ネットマスクを指定(1-31) bitmap:ip
hash:ip
range IPやポート番号の範囲を指定 bitmap系のタイプで使用(必須)
timeout セットの有効期限を設定
デフォルトは無制限(0)
全てのタイプで使用可能

セット作成例

今回作成する「BLACKLIST」という名前のセットには、ネットワークアドレス登録するのでタイプは「hash:net」を指定して作成します。

# ipset create BLACKLIST hash:net

ネットワークアドレス追加

セットの作成が完了しましたら、「ipset add」コマンドでネットワークアドレスを追加していきます。

ipset add セット名 ネットワークアドレス/ネットマスク

追加例

先ほど作成したセットである「BLACKLIST」にネットワークアドレスを追加していきます。

# ipset add BLACKLIST 192.168.0.0/24
# ipset add BLACKLIST 192.168.2.0/24
追加確認

セットに登録された情報は下記のコマンドで確認することが出来ます。

ipset list セット名

先ほど実際に登録した内容を確認してみます。

# ipset list BLACKLIST
Name: BLACKLIST
Type: hash:net
Revision: 3
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16848
References: 0
Members:
192.168.0.0/24
192.168.2.0/24

登録したネットワークアドレスが確認できました。

firewalldと連携

「ipset」の準備が出来たので、次に「firewalld」との連携設定を行っていきます。

連携には「firewall-cmd」の「--direct」オプションを使用して設定を行っていきます。

書式

firewall-cmd [--permanent] --direct --add-rule [ipv4|ipv6] テーブル名 チェイン 優先順位 -m set --match-set ipsetセット名 src -j DROP

作業時の注意点

「firewalld」の設定を行う場合は十分に注意して行ってください。

設定を失敗すると、対象サーバに接続することが出来なくなってしまう場合もあります。

設定例

今回は外部からの接続対してのルールを設定するので、テーブルは「filger」を、チェインは「INPUT」を指定しています。

# firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -m set --match-set BLACKLIST src -j DROP
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m set --match-set BLACKLIST src -j DROP

ここで設定した内容は「INPUT_direct」チェインに登録されます。

設定ファイル

「--direct」オプションで設定した内容は「/etc/firewalld/direct.xml」に保存されます。

direct.xml内容
<?xml version="1.0" encoding="utf-8"?>
<direct>
  <rule priority="0" table="filter" ipv="ipv4" chain="INPUT">-m set --match-set BLACKLIST src -j DROP</rule>
</direct>

設定確認

「iptables -L INPUT_direct -v」コマンドで「INPUT_direct」チェインの設定を確認して、「match-set」の部分に「ipset」で作成したセットが設定されていることを確認します。

INPUT_directチェイン確認
# iptables -L  INPUT_direct -v
Chain INPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source     destination
    0     0 DROP       all  --  any    any     anywhere   anywhere   match-set BLACKLIST src
INPUTチェイン確認

次に「INPUT」チェインに「INPUT_direct」チェインが登録されていることを確認してください。

# iptables -L  INPUT -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source     destination
  432 42740 ACCEPT     all  --  any    any     anywhere   anywhere      ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere   anywhere
  491 48989 INPUT_direct  all  --  any    any  anywhere      anywhere
  491 48989 INPUT_ZONES_SOURCE  all  --  any    any    anywhere      anywhere
  491 48989 INPUT_ZONES  all  --  any    any     anywhere            anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere      anywhere
  491 48989 REJECT     all  --  any    any     anywhere      anywhere   reject-with icmp-host-prohibited

両方とも確認できましたら、「ipset」と「fireawalld」の連携は完了です。

ipset自動作成

「ipset」と「firewalld」の連携が完了しましたら、次に「ipset」に作成したセットを自動作成する設定を行っていきます。

「ipset」はメモリ上にセット内容を格納しているため、サーバを再起動したりすると内容が削除されてしまいます。

そのため「firewalld」の起動前に「ipset」のセットを作成して、セットにルールを追加するという作業が必要になるのですが、サーバの再起動毎に手動で作業を行うのは現実的ではないので、自動的にセットを作成するためのスクリプトを作成していきます。

ipset データセーブ格納ディレクトリ作成

まずは、データ保存用のディレクトリを作成します。

# mkdir /etc/ipset

セット内容保存

現在の「ipset」の情報は下記の方法で保存することが出来ます。

ipset save > ファイル名

情報の保存

「BLACKLIST」というファイルに「ipset」のセット情報を保存していきます。

# ipset save > /etc/ipset/BLACKLIST
リストア方法

保存した情報は「ipset restore」コマンドでリストアすることができます。

ipset restore < ファイル名

ipset作成スクリプト

サーバ起動時や、「firewalld」の再起動時に自動的に「ipset」のセットを作成するために、「ipset_create.sh」というスクリプトを作成します。

※スクリプトの名前はお好きな名前でかまいません。

# vi /etc/ipset/ipset_create.sh

スクリプト内容

「ipset destroy」で既存のセット情報があればそれを削除して、その後「ipset restore」でバックアップした情報からセットを作成するという動作をします。

#!/bin/bash

IPSET=/sbin/ipset
DIR=/etc/ipset
CONF=BLACKLIST

cd $DIR
$IPSET destroy
$IPSET restore < $CONF

パーミッション設定

作成したスクリプトのパーミッションを設定します。

# chmod 744 ipset_create.sh

スクリプト動作確認

スクリプトの作成が終わりましたら、次に動作試験を行っていきます。

firewalldとの連係解除

「firewalld」と連携していると「ipset destroy」や「ipset restore」が行えないため、一度「firewalld」との連携を解除します。

# firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set BLACKLIST src -j DROP

スクリプト動作試験

「firewalld」との連携が解除されましたら、「ipset destoy」でセットを削除してからスクリプトを実行しセットがリストアされることを確認してください。

既存のセット削除と削除確認
# ipset destroy
# ipset list
スクリプト実行
# sh -x /etc/ipset/ipset_create.sh
# ipset list

スクリプトの動作に問題が無ければ、「firewalld」が起動する前に「ipset」のセット作成スクリプトを実行して、セットを作成するように設定を行っていきます。

firewalldsystemd管理用スクリプト変更

「firewalld」が起動する前にipsetのセットが作成されている必要があるため、「firewalld」のsystemd管理用スクリプト(/usr/lib/systemd/system/firewalld.service)を編集していきます。

# cp -p /usr/lib/systemd/system/firewalld.service /usr/lib/systemd/system/firewalld.service_yyyymmdd
# vi /usr/lib/systemd/system/firewalld.service

変更内容

[Service]セクションに「ExecStartPre」を設定すると、起動前に実行するスクリプトを指定できるので「ipset」作成用のスクリプト(/etc/ipset/ipset_create.sh)を指定します。

[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
ExecStartPre=/etc/ipset/ipset_create.sh      <-----追加
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
ExecReload=/bin/kill -HUP $MAINPID

動作確認

「/usr/lib/systemd/system/firewalld.service」の編集が完了しましたら、「firewalld」と「ipset」の連携試験を行っていきます。

firewalld再起動テスト

「ipset」の中身を空にしてから「firewalld」を再起動して「ipset」と連携できているか確認します。

ipset flush

一旦「ipset」のセット「BLACKLIST」の情報を削除します。

# ipset destroy
# ipset list

「firewalld」を再起動して、「ipset」と連携できているか確認します。

# systemctl restart firewalld

ipset確認

「ipset」のセット登録情報を確認します。

# ipset list BLACKLIST

iptable設定状況確認

「INPUT」と「INPUT_direct」チェインの内容を確認し、それぞれ登録されていることを確認してください。

# iptables -L  INPUT_direct -v
Chain INPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source     destination
   14   612 DROP       all  --  any    any     anywhere   anywhere   match-set BLACKLIST src
# iptables -L  INPUT -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source     destination
  171 15112 ACCEPT     all  --  any    any     anywhere   anywhere   ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere   anywhere
   41  2480 INPUT_direct  all  --  any    any     anywhere   anywhere
    8  1032 INPUT_ZONES_SOURCE  all  --  any    any     anywhere   anywhere
    8  1032 INPUT_ZONES  all  --  any    any     anywhere   anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere   anywhere
    8  1032 REJECT     all  --  any    any     anywhere   anywhere   reject-with icmp-host-prohibited

サーバ再起動試験

「firewalld」の再起動試験で問題がなければ大丈夫だと思いますが、念のためにサーバの再起動試験が行えるのであれば実施したほうが安心です。

もし、今すぐにサーバの再起動が行えない場合は、次回サーバ再起動時に忘れずに確認を行ってください。

以上で作業完了となります。

国別のIPリスト作成方法

特定の国からの通信を遮断したい場合、その国に配布されているIPアドレスの一覧を調べる必要があります。

IPアドレスを管理している各団体のページから情報をダウンロードしてきて、自分で必要な情報を抽出するという方法もありますが、それは面倒なので私はいつも下記のサイトのお世話になっています。

https://ipv4.fetus.jp/

IPアドレスの一覧が欲しい国を選択すると、その国に割り当てられているIPアドレスの一覧を表示してくれます。

また、画面右側の「Download」の「プレインテキスト」からテキストで情報をダウンロードすることができるので、これを元にIPセットを作成すると良いでしょう。

ちなみに、私はセットに登録する際には下記のようなスクリプトを使用して登録しています。

※登録するセットは事前に作成しておいてください。

#!/bin/bash

LIST="ファイル名"
SET="セット名"
COM="/sbin/ipset add"

while read line; do
        $COM $SET $line
done < $LIST

 - firewalld