CentOS Stream 9 に fail2ban を入れ、SSH の総当たりを自動で遮断する
篠原 隆司
アフィリエイト広告を利用しています
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
挨拶や報告は無しで大丈夫です
さくらのVPSのCentOS Stream 9に、fail2ban を入れた覚書です。
今回は、fail2ban で、SSH のログインの失敗が続く接続元を、自動で遮断します。Web(Apache)へのアクセスの守りは、続く 3 本の記事(サーバー全体の設定・サイトごとの設定・.htaccess の設定)で扱います。
記事の中の IP アドレス・ドメインなどは、実際のものから書き換えています。
なお本番環境で利用される場合はここにある内容だけを鵜呑みにせずセキュリティ専門家に相談されることをお勧めします。
環境
| 実施日 | 2026-11-07 |
| サーバ | さくらのVPS 2G
|
| OS | CentOS Stream 9 |
| SELinux | 無効(Disabled) |
cat /etc/redhat-release
CentOS Stream release 9
経緯と解決策
経緯について
インターネットに公開しているサーバーには、SSH のログインに、ユーザー名やパスワードを総当たりで試す接続が日々届きます。人だけでなく、bot やマルウェアからも来ます。
SSH は、最初にすることのように、公開鍵だけでログインさせ、firewalld で決まった IP アドレスだけを通せば、ほとんど届かなくなります。
ただ、出先から入るなどの事情で IP アドレスを絞れないこともあります。また、サーバーを移したときや設定を見直したときに、絞り込みが外れてしまうこともあります。
解決案を考える
そこで、fail2ban を入れます。fail2ban は、ログを見て、失敗が続く接続元を一定時間遮断するソフトウェアです。
今回守るのは、SSH(sshd)です。IP アドレスで絞れないときや、絞り込みが外れたときの保険にします。
遮断は firewalld に任せます。設定は、fail2ban に元からある jail.conf を直さず、/etc/fail2ban/jail.local に書きます。
10 分のあいだに 5 回失敗した接続元を、1 時間遮断します。
なお、fail2ban はパスワードの総当たりへの対策で、短い時間に大量のアクセスを送り付ける攻撃(DoS)を止めるものではありません(補足説明の「fail2ban の仕組み」)。
事前確認
fail2ban を入れる前に、firewalld・SSH のポート・EPEL を確かめます。
作業は root で行います。
firewalld が動いているか
fail2ban は、遮断を firewalld に頼みます。firewalld が動いていることを確かめます。
systemctl is-active firewalld
active
止まっている場合は、最初にすることの「SSHのIP制限」で、起動と自動起動の設定をしてから進めてください。
SSH のポート
fail2ban の SSH の守りは、既定では 22 番ポートを遮断します。ポートを変えている場合は、その番号を設定に書くので、確かめておきます。
sshd -T | grep -E '^port '
port 22
この記事では、既定の 22 で進めます。変えている場合は、その番号に読み替えます。
EPEL に fail2ban があるか
fail2ban は EPEL リポジトリにあります。最初にすることで EPEL を入れ、普段は無効(enabled=0)にしているので、--enablerepo=epel を付けて確かめます。
dnf --enablerepo=epel info fail2ban
バージョン の行に 1.1.0 と出れば、この記事と同じ版が入ります。
今回行うこと
はじめに、fail2ban をインストールします。
そのあと、jail.local を作って起動します。
最後に、fail2ban-client と firewalld で、遮断できることを確かめます。
インストールする
dnf --enablerepo=epel install fail2ban fail2ban-firewalld
依存関係が解決しました。
================================================================================
パッケージ アーキテクチャー バージョン リポジトリー サイズ
================================================================================
インストール:
fail2ban noarch 1.1.0-6.el9 epel 9.3 k
fail2ban-firewalld noarch 1.1.0-6.el9 epel 9.5 k
依存関係のインストール:
fail2ban-selinux noarch 1.1.0-6.el9 epel 31 k
fail2ban-sendmail noarch 1.1.0-6.el9 epel 12 k
fail2ban-server noarch 1.1.0-6.el9 epel 465 k
トランザクションの概要
================================================================================
インストール 5 パッケージ
(略)
これでよろしいですか? [y/N]: y
(略)
libsemanage.semanage_direct_install_info: Overriding fail2ban module at lower priority 100 with module at priority 200.
(略)
完了しました!
本体の fail2ban-server、SELinux の規則の fail2ban-selinux、メールで知らせる動きの fail2ban-sendmail も一緒に入ります。fail2ban は、これらをまとめて入れるためのパッケージです。
途中の libsemanage… の行は、SELinux の規則を入れ替えたという知らせで、エラーではありません。
fail2ban-firewalld は、遮断に firewalld を使う設定(/etc/fail2ban/jail.d/00-firewalld.conf)を入れるパッケージです。中身を見ておきます。
cat /etc/fail2ban/jail.d/00-firewalld.conf
# This file is part of the fail2ban-firewalld package to configure the use of
# the firewalld actions as the default actions. You can remove this package
# (along with the empty fail2ban meta-package) if you do not use firewalld
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
banaction が firewallcmd-rich-rules なので、遮断は firewalld の rich rule で行われます。
jail.local を作る
/etc/fail2ban/jail.local を作り、全体の値と SSH の守りを書きます。
vi /etc/fail2ban/jail.local
[DEFAULT]
# 遮断しない接続元。1 行に 1 つ書き、2 行目からは行頭を空白で下げる
ignoreip =
# このサーバー自身(IPv4)
127.0.0.1/8
# このサーバー自身(IPv6)
::1
# 事務所の固定 IP
203.0.113.10
# 作業用の VPN の出口
203.0.113.20
# 取引先のネットワーク
198.51.100.0/24
# 10 分(findtime)のあいだに 5 回(maxretry)失敗したら、1 時間(bantime)遮断する
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = 22
backend = systemd
ignoreip のように、値を 2 行目以降に続けるときは、行頭を空白で下げます。下げないと、前の行の続きとして読まれません。
vi に貼り付けると字下げがずれることがあるので、:set paste を打ってから挿入モードに入って貼り付けます。
全体の値([DEFAULT])
[DEFAULT] に書いた値は、すべての守り(ジェイル)に効きます。今回は sshd だけですが、あとで守りを足したときも、そのジェイルに書かなかった値は、[DEFAULT] の値が使われます。
- ignoreip:遮断しない接続元。例は、IP アドレス 1 つ(
203.0.113.10)、もう 1 つ(203.0.113.20)、ネットワーク(198.51.100.0/24)です。1 行に 1 つ書き、何の IP アドレスかを、その上の行にコメントで書いておきます。SSH を通している IP アドレスは、必ずここに入れます(補足説明の「自分を遮断しないために」) - bantime / findtime / maxretry:
findtime(10 分)のあいだにmaxretry(5 回)失敗した接続元を、bantime(1 時間)遮断します
SSH の守り([sshd])
- port:SSH のポート。事前確認で見た番号を書きます
- backend = systemd:sshd のログを、systemd のジャーナルから読みます
設定に誤りが無いか確かめる
設定に誤りが無いかを確かめます。
fail2ban-client -t
OK: configuration test is successful
ignoreip が思ったとおりに読まれたかも見ます。-d は、fail2ban が設定を読んだ結果を出します。
fail2ban-client -d | grep ignoreip
['set', 'sshd', 'addignoreip', '127.0.0.1/8', '::1', '203.0.113.10', '203.0.113.20', '198.51.100.0/24']
ignoreip に、書いた IP アドレスだけが並び、コメントが混ざっていなければ大丈夫です。[DEFAULT] に書いた値が、sshd に入っています。
起動する
起動して、自動起動にも登録します。
systemctl enable --now fail2ban
Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service → /usr/lib/systemd/system/fail2ban.service.
動いているかを見ます。--no-pager を付けないと、表示が止まって q を押すまで戻りません。
systemctl status fail2ban --no-pager
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; preset: disabled)
Active: active (running) since Sat 2026-11-07 10:44:03 JST; 4s ago
Docs: man:fail2ban(1)
Process: 12344 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 12345 (fail2ban-server)
Tasks: 9 (limit: 10000)
Memory: 35.8M (peak: 36.3M)
CPU: 1.2s
CGroup: /system.slice/fail2ban.service
└─12345 /usr/bin/python3 -s /usr/bin/fail2ban-server -xf start
11月 07 10:44:03 example.com systemd[1]: Starting Fail2Ban Service...
11月 07 10:44:03 example.com systemd[1]: Started Fail2Ban Service.
11月 07 10:44:03 example.com fail2ban-server[12345]: Server ready
Active: active (running) になっていれば動いています。enabled は自動起動の登録です(preset: disabled は、パッケージを入れたときの既定が「登録しない」という意味です)。
動きを確かめる
sshd の守りが動いていることを確かめます。
fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
sshd の中も見ます。
fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
sshd はファイルではなく systemd のジャーナルから読むので、どの記録を読んでいるかが Journal matches に出ます。
起動した直後なので、失敗の数は 0 です。SSH のポートを変えていると、22 番を狙う総当たりが届かないので、しばらく 0 のままのことも珍しくありません。
この記事は、SELinux を無効にしたサーバーで試しています。
SELinux が有効な場合(getenforce で Enforcing と出る場合。CentOS Stream 9 の既定)は、SELinux に止められていないかも見ます。何も出なければ止められていません。
ausearch -m avc -ts recent | grep fail2ban
最後に、遮断できるかを、使われていない IP アドレスで試します。192.0.2.1 は説明用に決められた IP アドレスで、実際の接続元にはなりません。
# 試しに遮断する
fail2ban-client set sshd banip 192.0.2.1
# firewalld に規則が足されたことを確かめる
firewall-cmd --list-rich-rules
1
rule family="ipv4" source address="192.0.2.1" port port="22" protocol="tcp" reject type="icmp-port-unreachable"
最初の 1 は、遮断した数です。SSH のポート(22)にだけかかる規則が足されています。
# 解く
fail2ban-client set sshd unbanip 192.0.2.1
# 規則が消えたことを確かめる
firewall-cmd --list-rich-rules
1
1 は解いた数です。--list-rich-rules から 192.0.2.1 の行が消えていれば、遮断は firewalld で効いています。
遮断と解除の記録は、/var/log/fail2ban.log に残ります。
tail -n 20 /var/log/fail2ban.log
(略)
2026-11-07 10:44:03,870 fail2ban.jail [12345]: INFO Jail 'sshd' started
2026-11-07 10:50:11,946 fail2ban.actions [12345]: NOTICE [sshd] Ban 192.0.2.1
2026-11-07 10:50:18,617 fail2ban.actions [12345]: NOTICE [sshd] Unban 192.0.2.1
遮断を解く
自分や、知っている相手を遮断してしまったときは、次のように解きます。
# 遮断している IP アドレスを見る(Banned IP list の行)
fail2ban-client status sshd
# 1 つ解く(空白で区切って、いくつか並べてもよい)
fail2ban-client set sshd unbanip 203.0.113.30
# すべて解く
fail2ban-client unban --all
fail2ban を起動し直しても、遮断は解けません。fail2ban は遮断をデータベースに覚えていて、起動し直すと、1 時間(bantime)が過ぎていない遮断を戻します(/var/log/fail2ban.log に Restore Ban と出ます)。解くときは、unbanip か unban --all を使います。
自分の IP アドレスが SSH で遮断されると、つながらなくなります。そのときは、さくらのVPS のコントロールパネルのコンソールからログインして解きます。
元に戻す
fail2ban を使わなくするときは、次のようにします。
Web へのアクセスの守り(次の記事から)も入れているときは、先にそちらを元に戻します。jail.local を消すと、Web の守りのジェイルが使う ignoreip も消えるためです。
# 止めて、自動起動を外す
systemctl disable --now fail2ban
# 設定を消す
rm /etc/fail2ban/jail.local
# アンインストールする
dnf remove fail2ban fail2ban-firewalld
# fail2ban が足した規則が残っていないことを確かめる
firewall-cmd --list-rich-rules
補足説明
fail2ban の仕組み
fail2ban は、ログの何を失敗とみなすか(フィルタ)、見つけたら何をするか(アクション)と、その 2 つを組み合わせた守りの単位(ジェイル)で動きます。
今回の sshd はジェイルの名前で、フィルタとアクションは、fail2ban に元からあるものを使っています。
fail2ban がするのは、「ログの決まった行を、IP アドレスごとに数える」ことです。sshd が数えるのは、ログインに失敗した行です。
アクセスの回数そのものは数えないので、何度も接続を送り付ける攻撃や、DoS は止まりません。また、多くの IP アドレスから一斉に来る攻撃(DDoS)にも向きません。
起動したときは、ログのうち findtime(10 分)の内の行を読んで数えます。そのあとは、増えた分を読んでいきます。
jail.conf を直接直さない理由
/etc/fail2ban/jail.conf の冒頭には、このファイルは直さず、jail.local や jail.d/ の下のファイルに書くように、と書かれています。
変えたい値だけを別のファイルに書いておけば、fail2ban を更新して jail.conf が変わっても、どこを変えたかを見失いません。
設定は、jail.conf → jail.d/*.conf → jail.local → jail.d/*.local の順に読まれ、後から読んだ値が使われます。jail.local に書かなかった値は、jail.conf などの値のままです。
firewalld で遮断する
遮断のしかた(banaction)は、fail2ban-firewalld が入れる 00-firewalld.conf で firewallcmd-rich-rules になります。jail.local の [DEFAULT] に banaction を書くと、こちらが上書きされるので、書きません。
遮断は、firewalld の rich rule として足されます。規則がかかるのは、ジェイルの port に書いたポートだけです。sshd なら SSH のポートです。
firewalld は、通す rich rule より、拒む rich rule を先に当てます。そのため、最初にすることで SSH を通している IP アドレスでも、fail2ban に遮断されると SSH がつながらなくなります。
自分を遮断しないために
ignoreip は「遮断しない接続元」の一覧で、「入ってよい接続元」の一覧ではありません。ignoreip に無い IP アドレスからでも、ふつうに使っている限りは、今までどおり入れます。
遮断されるのは、10 分のあいだに 5 回失敗したときだけです。自分でも、パスワードの打ち間違いや鍵の取り違えが続けば遮断されます。いつも使う IP アドレスは、ignoreip に入れておきます。
ignoreip に書けるのは、次の 3 つです。
- IP アドレス:
203.0.113.10 - 範囲(CIDR):
198.51.100.0/24。198.51.100.*のようなワイルドカードは書けないので、範囲はこの形で書きます - ホスト名:fail2ban が IP アドレスに引いて使います
プロバイダの IP アドレスの範囲をまるごと書くこともできますが、勧めません。範囲が広いので、同じプロバイダを使う人からの総当たりも遮断しなくなります。
家庭の回線のように IP アドレスが変わることがある場合も、そのときの IP アドレスを書くのはやめておきます。変わったあとに、その IP アドレスを使う別の人を遮断しなくなるためです。固定の IP アドレスだけを書き、それ以外の場所からは、遮断されたらコンソールから解く、と考えておきます。
コメントは、ignoreip の例のように、行頭に # を置いた行に書きます。203.0.113.10 # 事務所 のように値の後ろに書くと、# から後ろも遮断しない相手の一部として読まれるおそれがあります。
読まれた中身は、fail2ban-client -d | grep ignoreip で確かめられます。
設定を変えるときは、作業中のターミナルを閉じずに、新しいターミナルでつながることを確かめてください。
まとめ
- fail2ban は、ログを見て、ログインの失敗が続く接続元を、firewalld で一定時間遮断する。DoS の対策ではない
- 設定は
jail.localに書き、jail.confなどの元のファイルは直さない - SSH のポートを変えているなら、その番号を書く
- 試しの遮断と解除で、firewalld に規則が足されることを確かめる
ignoreip に入れてください。遮断されると、コントロールパネルのコンソールからしか入れなくなります。Web へのアクセスの守りは、次の記事で扱います。
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
本ページの内容は可能な限り正確な情報を提供するよう努めていますが、内容の正確性・最新性・安全性を保証するものではありません。本情報を利用して生じたいかなる損害についても、当方は一切の責任を負いません。実施にあたっては必ずご自身の判断と自己責任にてお願いいたします。