こういった経験をしたことはありませんか。
iptablesはパケット1つを処理するたびにルールを上から順に照合します。ルール数が増えるほど照合コストが増加し、高トラフィック環境ではCPUへの負荷が無視できなくなります。
この記事では、大量のIPアドレスを「セット」としてまとめて管理できるipsetコマンドを解説します。RHEL 9系・Ubuntu 24.04 LTSを想定環境として説明します。iptablesやfirewalldと組み合わせることで、ルール数を最小限に保ちながら数万件規模のIPアドレスを効率よくフィルタリングできます。
この記事のポイント
・ipsetはIPアドレス・ネットワークをハッシュセットで管理し、iptablesのルール爆発を防ぐ
・`ipset create`でセット作成後、`ipset add`でIPを追加し、iptablesの`-m set --match-set`で参照する
・firewalldとの連携は`firewall-cmd --ipset`で行い、既存の管理フローを壊さずに導入できる
・再起動後も設定を維持するには`service ipset save`またはsystemdユニットで自動復元する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
ipsetとは何か|iptablesの弱点を補う仕組み
iptablesは1つのマッチ条件に対して1つのルールを書く設計です。10個のIPをブロックするには10本の-A INPUT -s <IP> -j DROPが必要で、ルールが増えるほどカーネルはパケット1つにつきすべてのルールを上から照合します。ipsetはこの問題を解消するためのLinuxカーネルモジュールです。IPアドレスやネットワーク(CIDR)、ポート番号などをハッシュテーブル構造のセットとして管理します。iptablesからセット名で参照するだけで、セットの中身が何万件あっても高速な照合が可能です。
主な使いどころ:
・DDoS対策で攻撃元IPをまとめてブロック
・国単位の地理的IPブロック
・許可リスト(ホワイトリスト)の管理
・不正アクセスのログから自動的にIPを登録してブロック
ipsetのインストールと基本的な使い方
1. インストール
RHEL 9系 / Rocky Linux / AlmaLinuxの場合:# dnf install ipset ipset-service
# apt install ipset
# ipset version
ipset v7.11, protocol version: 7
2. セットを作成する
ipset create <セット名> <セットタイプ>でセットを作成します。よく使うセットタイプは以下の3つです:・hash:ip — 個別IPアドレスを管理するセット
・hash:net — CIDRネットワークアドレスを管理するセット
・hash:ip,port — IPアドレスとポートの組み合わせを管理するセット
ブロックリスト用のセットを作成する例:
# ipset create blocklist hash:ip
3. IPアドレスを追加・確認・削除する
# ipset add blocklist 203.0.113.10 # ipset add blocklist 203.0.113.20 # ipset add blocklist 198.51.100.0/24
# ipset list blocklist
Name: blocklist Type: hash:ip Revision: 4 Header: family inet hashsize 1024 maxelem 65536 Size in memory: 312 References: 0 Number of entries: 3 Members: 203.0.113.10 203.0.113.20 198.51.100.0/24
ipset delを、セット自体を削除するにはipset destroyを使います:# ipset del blocklist 203.0.113.10 # ipset destroy blocklist
iptablesと組み合わせる|-m set --match-set の書き方
ipsetのセットはiptablesの-m set --match-setオプションで参照します。# iptables -I INPUT -m set --match-set blocklist src -j DROP
・
-m set — ipsetモジュールを使用する・
--match-set blocklist — セット名「blocklist」を参照する・
src — 送信元(source)IPで照合する(宛先の場合はdst)現在のINPUTチェーンのルールを確認します:
# iptables -L INPUT -v -n --line-numbers
firewalldとipsetを連携させる方法
RHEL 9系ではiptablesを直接操作するよりfirewalldを使うのが標準です。firewalld 0.6以降はipsetをネイティブにサポートしています。1. ipsetをfirewalld経由で作成する
# firewall-cmd --permanent --new-ipset=blocklist --type=hash:ip # firewall-cmd --reload
2. IPをfirewalld経由で追加する
# firewall-cmd --permanent --ipset=blocklist --add-entry=203.0.113.10 # firewall-cmd --permanent --ipset=blocklist --add-entry=198.51.100.0/24 # firewall-cmd --reload
3. セットをゾーンのブロックルールに適用する
# firewall-cmd --permanent --zone=public --add-rich-rule='rule source ipset=blocklist drop' # firewall-cmd --reload
# firewall-cmd --ipset=blocklist --get-entries
/etc/firewalld/ipsets/配下にXML形式で保存されます。--permanentをつけて操作した場合は再起動後も設定が引き継がれます。ipsetの永続化|再起動後も設定を維持する
ipsetコマンドで直接作成したセットは、再起動すると消えます。永続化の方法は環境によって2通りあります。方法1: ipset-service(RHEL系)
RHEL系ではipset-serviceパッケージに含まれるsystemdサービスを使います:# systemctl enable --now ipset
/etc/sysconfig/ipsetへ保存するには:# service ipset save # または # ipset save > /etc/sysconfig/ipset
方法2: ipset saveとsystemdユニット(Ubuntu・汎用)
ipset-serviceがない環境(Ubuntuなど)では、設定ファイルとsystemdユニットで対応します:# ipset save > /etc/ipset.conf
/etc/systemd/system/ipset-restore.serviceを作成します:[Unit] Description=ipset restore Before=network-pre.target Wants=network-pre.target [Service] Type=oneshot ExecStart=/sbin/ipset restore -f /etc/ipset.conf ExecStop=/sbin/ipset save -f /etc/ipset.conf RemainAfterExit=yes [Install] WantedBy=multi-user.target
# systemctl daemon-reload # systemctl enable ipset-restore
実務活用例|SSH不正ログイン元を自動ブロックするスクリプト
SSHへの不正ログイン試行をjournalctlで検出し、攻撃元IPを自動的にipsetに追加するシェルスクリプトの例を示します。#!/bin/bash # SSH不正ログイン元IPをipsetのblocklist(hash:ip)に追加する # このスクリプトはcronで定期実行する IPSET_NAME="blocklist" THRESHOLD=5 # セットが存在しない場合は作成 ipset list ${IPSET_NAME} > /dev/null 2>&1 || ipset create ${IPSET_NAME} hash:ip # 直近1時間のSSH失敗ログからIPを集計し、THRESHOLD回以上の攻撃元を追加 journalctl -u sshd --since "1 hour ago" --no-pager 2>/dev/null \ | grep "Failed password" \ | awk '{print $(NF-3)}' \ | sort | uniq -c | sort -rn \ | awk -v threshold=${THRESHOLD} '$1 >= threshold {print $2}' \ | while read ip; do ipset test ${IPSET_NAME} "${ip}" > /dev/null 2>&1 \ || ipset add ${IPSET_NAME} "${ip}" done
0 * * * * /usr/local/sbin/auto-blocklist.sh
・
ipset testで既存エントリの重複追加を防いでいます・
THRESHOLDの値は環境に合わせて調整してください(上記は5回)・スクリプト実行前に、対象のipsetとiptables/firewalldルールが設定済みであることを確認してください
トラブルシュート|よくあるエラーと対処法
「ipset: Kernel and userspace incompatible」
カーネルのipsetモジュールとipsetコマンドのバージョンが一致していない場合に発生します。カーネルモジュールを明示的にロードします:# modprobe ip_set # modprobe ip_set_hash_ip
# dnf update ipset kernel # reboot
「Set cannot be destroyed: it is in use by a kernel component」
iptablesのルールがセットを参照した状態でセットを削除しようとすると発生します。先にiptablesのルールを削除してからセットを削除します:# iptables -D INPUT -m set --match-set blocklist src -j DROP # ipset destroy blocklist
「Hash is full, cannot add more elements」
セットの上限(デフォルトはmaxelem 65536)に達しています。セット作成時にmaxelemを大きく設定します:# ipset create blocklist hash:ip maxelem 1000000
# ipset save > /tmp/ipset-backup.conf # ipset destroy blocklist # ipset create blocklist hash:ip maxelem 1000000 # grep -v "^create blocklist" /tmp/ipset-backup.conf | ipset restore
「firewall-cmd: ERROR: 'blocklist' not found」
firewalldが管理するipsetと、ipsetコマンドで直接作成したセットは別物です。firewalldで使うipsetは必ずfirewall-cmd --permanent --new-ipsetで作成し、/etc/firewalld/ipsets/配下で管理します。本記事のまとめ
| やりたいこと | コマンド |
|---|---|
| セットを作成する(個別IP用) | ipset create blocklist hash:ip |
| セットを作成する(CIDR用) | ipset create netblock hash:net |
| IPを追加する | ipset add blocklist 203.0.113.10 |
| セットの内容を確認する | ipset list blocklist |
| iptablesで送信元IPをブロック | iptables -I INPUT -m set --match-set blocklist src -j DROP |
| firewalldでipsetを作成 | firewall-cmd --permanent --new-ipset=blocklist --type=hash:ip |
| firewalldでIPを追加 | firewall-cmd --permanent --ipset=blocklist --add-entry=203.0.113.10 |
| 設定を保存する(RHEL系) | service ipset save |
| セットの上限を増やして再作成 | ipset create blocklist hash:ip maxelem 1000000 |
>> セミナー詳細・お申込みはこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:LinuxのOpenSSHでSFTPユーザーをChrootDirectoryに制限する方法|シェルアクセスなしのファイル転送サーバーを構築する
- この記事の属するカテゴリ:セキュリティへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます