サーバに IP 制限をかけたいと思ったのですが、CentOS 7.x からはアクセス制御が iptables ではなく firewalld に変更されています。 iptables は依然として firewalld の下で動いているので、やろうと思えば iptables で IP 制限をすることもできますが、制限する IP のリストが大きくなった場合、iptables はあまり効率が良くないのと、管理も煩雑化してしまうため、今回は ipset を用いて IP 制限を行うことにしました。
IP sets
IP sets are a framework inside the Linux kernel, which can be administered by the ipset utility. Depending on the type, an IP set may store IP addresses, networks, (TCP/UDP) port numbers, MAC addresses, interface names or combinations of them in a way, which ensures lightning speed when matching an entry against a set.
† インストールは yum で
インストールは yum で一撃です。
もしfail2ban をインストールしている場合には、既にインストールされているはずです。
yum install -y ipset
† まずは設定ファイルを作る
設定方法は「firewalld + ipset でアクセス制限 | server-memo.net」を参考にさせていただきました。
今回はアクセス制限用のリストを作るので BLACKLIST という名前にしています。
まずは設定を保存するための /etc/ipset というディレクトリを作成して、ユーティリティ用のスクリプトを作ります。
# 設定用のディレクトリを作成
mkdir -p /etc/ipset
# ユーティリティ (add.sh restore.sh save.sh) を生成
cd /etc/ipset
cat << 'EOS' >add.sh
#!/bin/bash
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR
IPSET=/sbin/ipset
CONF=BLACKLIST
TS="`date --iso-8601=seconds`"
if [ -n "$1" ] ; then
$IPSET add $CONF $1
else
echo "Usage: $0 n.n.n.n/n"
fi
EOS
cat << 'EOS' > restore.sh
#!/bin/bash
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR
IPSET=/sbin/ipset
CONF=BLACKLIST
CONF2=WHITELIST
$IPSET destroy $CONF >/dev/null 2>&1
$IPSET restore < $CONF
$IPSET destroy $CONF2 >/dev/null 2>&1
$IPSET restore < $CONF2
EOS
cat << 'EOS' >save.sh
#!/bin/bash
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR
IPSET=/sbin/ipset
CONF=BLACKLIST
CONF2=WHITELIST
$IPSET save $CONF > $CONF
$IPSET save $CONF2 > $CONF2
EOS
chmod 755 *.sh
次に ipset を使ってセットを作成し、作成したセットに IP アドレスを追加します。
# セット作成
ipset create BLACKLIST hash:net counters timeout 0
ipset create WHITELIST hash:net counters timeout 0
# 許可アドレスの追加(以下のコマンドを必要なだけ)
ipset add WHITELIST 10.0.0.0/8
# 制限アドレスの追加(以下のコマンドを必要なだけ)
./add.sh 192.168.100.0/24
./add.sh 192.168.101.1/32
# 保存のテスト(WHITELIST, BLACKLISTというファイルができるかどうか確認します)
./save.sh
最後に firewalld と連携したら完了です。
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m set ! --match-set WHITELIST src -m set --match-set BLACKLIST src -j DROP
firewall-cmd --reload
※上記だと全ての通信を DROP してしまうので、特定のポートの通信のみを縛るには multiport を使って以下のように書くことができます。
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m multiport -p tcp --dports 22,80,443 -m set ! --match-set WHITELIST src -m set --match-set BLACKLIST src -j DROP
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m multiport -p udp --dports 22,80,443 -m set ! --match-set WHITELIST src -m set --match-set BLACKLIST src -j DROP
firewall-cmd --reload
ipset の内容は再起動したりすると消えてしまうので、以下のように firewalld のサービスの定義を変更して起動時と終了時に設定のリストアや保存を自動的に行うようにしておきます。
cp -a /usr/lib/systemd/system/firewalld.service /etc/systemd/system
cd /etc/systemd/system
patch <<'EOF'
--- firewalld.service.org 2017-01-18 08:28:33.000000000 +0900
+++ firewalld.service 2017-01-28 21:45:36.985884227 +0900
@@ -10,6 +10,8 @@
[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
+ExecStartPre=/etc/ipset/restore.sh
+ExecStopPost=/etc/ipset/save.sh
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
ExecReload=/bin/kill -HUP $MAINPID
# supress to log debug and error output also to /var/log/messages
EOF
systemctl daemon-reload
定義の内容や稼働状況は以下のコマンドで確認できます。
ipset list BLACKLIST
意外とシンプルに実現できました。
† 2017/2/4 追記
不便なのでホワイトリスト(無条件に許可するIP)も設定できるようにしました。
† 2022/9/21 追記
ipset が firewalld で管理されるようにしました。