前の記事では、sed と awk(=行を列に分けて、集計や取り出しをするコマンド)でログを加工しました。今回は、そのログの読み方も使いながら、サーバーのセキュリティの基礎を整理します。
VPS を借りた次の日、何気なくログを見て青ざめたことがあります。世界中のいろいろな IP アドレスから、「root(=Linux の管理者ユーザー)」「admin」「test」などの名前で、何百回もログインを試された記録が残っていたのです。サーバーは、インターネットにつないだ瞬間から、休まずノックされつづけます。でも、やるべきことを順番に押さえておけば、こわがりすぎる必要はありません。
この記事では、自分のサーバーの状態を見て確かめることを中心に進めます。SSH(=ネットワークの向こうのコンピューターに、暗号化して安全にログインし、操作するしくみ)の設定やファイアウォールの切りかえのように、まちがえるとサーバーに入れなくなる操作は、文章だけで説明します。
- サーバーを守る4つの柱(最小権限・入口を減らす・更新・見張り)
- 自分の権限と、秘密のファイルの権限を確かめる方法
-
ssで、外に向けて開いている入口を確かめる方法 - ログイン記録や
sudoの記録の見方 -
fail2banのしくみと、状態の確かめ方
-
sudo・apt・chmodを使える -
systemctl・journalctlでサービスとログを見たことがある - SSH の鍵ログインと、VPS の初期設定の流れを知っている
まだの方は、前の記事「Linuxのsed・awk入門|ログを集計・置換するワンライナー【初心者向け】」から読むのがおすすめです。SSH とファイアウォール(=通信を、決めたルールで通したり止めたりする門番)の設定は「VPSを借りたら最初にやること|ユーザー作成・SSHの強化・ufwのチェックリスト【初心者向け】」でくわしく説明しています。
- 最小権限:人やプログラムに、必要なぶんだけの権限しか渡さないという考え方
- 待ち受け:プログラムが、決まったポートで通信が来るのを待っている状態。
ssではLISTENと表示されます - 管理画面のコンソール:VPS の会社の Web 画面から開ける、サーバーを直接操作できる画面。SSH でつながらなくなったときの非常口になります(ない会社もあります)
用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。
結論:「最小権限・入口を減らす・更新・見張り」の4つ
| 柱 | やること | 確かめるコマンド |
|---|---|---|
| 最小権限 | root で作業しない。秘密のファイルは自分だけ読める | id、sudo -l、ls -l |
| 入口を減らす | 使わないポートは閉じる。待ち受けは必要なものだけ | sudo ss -tlnp |
| 更新 | セキュリティの更新を早めに入れる | apt list --upgradable |
| 見張り | ログイン失敗をくり返す相手を止める。ログを見る | fail2ban-client status、journalctl |
準備:練習用のディレクトリを作る
- 1
- 2
- 3
mkdir -p ~/linux-practice/security-test
cd ~/linux-practice/security-test
pwdここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。
(例)
- 1
- 2
kaito@demo:~/linux-practice/security-test$ pwd
/home/kaito/linux-practice/security-testSTEP 1:最小権限を確かめる
まず、自分が誰で、どんな権限を持っているかを確かめます。
- 1
- 2
- 3
- 4
whoami
id
sudo -l
getent group sudo(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
kaito@demo:~/linux-practice/security-test$ whoami
kaito
kaito@demo:~/linux-practice/security-test$ id
uid=1000(kaito) gid=1000(kaito) groups=1000(kaito),27(sudo),100(users)
kaito@demo:~/linux-practice/security-test$ sudo -l
Matching Defaults entries for kaito on demo:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User kaito may run the following commands on demo:
(ALL : ALL) ALL
kaito@demo:~/linux-practice/security-test$ getent group sudo
sudo:x:27:kaitosudo のパスワードを聞かれたら、自分のパスワードを入れます(画面には表示されません)。(ALL : ALL) ALL は「どのユーザーとしても、どのコマンドでも実行できる」という意味です。
sudo -l は「自分が sudo で何をしてよいか」の一覧です。sudo グループに入っているユーザーは、何でもできる強い権限を持っています。sudo グループのユーザーは、必要な人だけにしておきましょう。
次に、秘密の情報を入れるファイルの権限を確かめます。API キー(=API を使うときに必要な、合言葉のような文字列)などを書いた .env ファイルを想定した、ダミーのファイルを作ります。
- 1
- 2
- 3
- 4
echo "API_KEY=dummy-1234567890" > secret.env
ls -l secret.env
chmod 600 secret.env
ls -l secret.env(例)
- 1
- 2
- 3
- 4
- 5
- 6
kaito@demo:~/linux-practice/security-test$ echo "API_KEY=dummy-1234567890" > secret.env
kaito@demo:~/linux-practice/security-test$ ls -l secret.env
-rw-rw-r-- 1 kaito kaito 25 Nov 1 04:17 secret.env
kaito@demo:~/linux-practice/security-test$ chmod 600 secret.env
kaito@demo:~/linux-practice/security-test$ ls -l secret.env
-rw------- 1 kaito kaito 25 Nov 1 04:17 secret.env検証環境は新しいファイルに「グループも書きこめる」設定(umask 002)なので、最初は -rw-rw-r-- でした。最後の r が「ほかの人も読める」という印です。chmod 600 のあとは -rw------- になり、自分だけが読み書きできる状態になりました。
600 は「自分だけ読み書きできる。ほかの人は何もできない」という権限です。ほかの人も読める状態の .env(=APIキーなどの秘密の設定を書いておくファイル)ファイルがないかは、次のように探せます。
- 1
- 2
find ~/linux-practice -type f -name "*.env" -perm /o+r
find ~ -type f -perm -o+w 2>/dev/null | head -n 5(例)
- 1
- 2
kaito@demo:~/linux-practice/security-test$ find ~/linux-practice -type f -name "*.env" -perm /o+r
kaito@demo:~/linux-practice/security-test$ find ~ -type f -perm -o+w 2>/dev/null | head -n 5どちらも何も表示されませんでした。ほかの人が読める .env も、だれでも書きこめるファイルもない、ということです。
「Permission denied が出るから」と、chmod 777 や chmod -R 777 で誰でも読み書きできるようにするのはやめましょう。秘密のファイルが丸見えになり、ほかのプログラムに書きかえられるおそれもあります。root で直接ログインして作業しつづけるのも、まちがいが即サーバー全体の事故になるので避けます。/etc/sudoers を直接編集するのも、書きまちがえると誰も sudo を使えなくなるので、この記事ではやりません。
STEP 2:開いている入口(ポート)を確かめる
サーバーの入口は「ポート(=1台のコンピューターの中で、どのサービスにつなぐかを分ける番号)で待ち受けているプログラム」です。-p をつけると、どのプログラムが待っているかまで表示されます。
- 1
sudo ss -tlnp(例)
- 1
- 2
- 3
- 4
- 5
kaito@demo:~/linux-practice/security-test$ sudo ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 5 127.0.0.1:3142 0.0.0.0:*
LISTEN 0 20 127.0.0.1:25 0.0.0.0:* users:(("exim4",pid=384,fd=5))
LISTEN 0 20 [::1]:25 [::]:* users:(("exim4",pid=384,fd=6))検証環境には SSH のサーバー(openssh-server)が最初は入っていないため、:22 の行はありませんでした。本物の VPS では、ふつう 0.0.0.0:22 や [::]:22 の sshd の行が表示されます。
0.0.0.0 と [::] は、右側の Peer Address(相手のアドレス)の欄にだけ出ています。これは「どの相手からでも受け付ける」という意味で、入口の場所を表す Local Address はすべて 127.0.0.1 か [::1] です。つまり、外から見える入口はありません。127.0.0.1:3142 は、検証環境で使っている apt のダウンロード用の中継のもので、読者の環境では表示されません。:25 の2行は、Debian に最初から入っているメール配送のプログラム(exim4)です。
| Local Address | 意味 |
|---|---|
0.0.0.0:ポート や [::]:ポート | 外からも見える入口 |
127.0.0.1:ポート | 自分の中だけの入口(外からは見えない) |
外から見える入口に、知らないプログラムがあったら要注意です。何のプログラムかを調べ、使っていないなら止めることを考えます。動いているサービスの一覧も見ておきましょう。
- 1
systemctl list-units --type=service --state=running --no-pager(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
kaito@demo:~/linux-practice/security-test$ systemctl list-units --type=service --state=running --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
console-getty.service loaded active running Console Getty
cron.service loaded active running Regular background program processing daemon
dbus.service loaded active running D-Bus System Message Bus
exim4.service loaded active running exim Mail Transport Agent
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
user@1000.service loaded active running User Manager for UID 1000
Legend: LOAD → Reflects whether the unit definition was properly loaded.
ACTIVE → The high-level unit activation state, i.e. generalization of SUB.
SUB → The low-level unit activation state, values depend on unit type.
7 loaded units listed.検証環境では、動いているサービスは7つでした。SSH のサーバーが入っていないので、ssh.service はありません。本物の VPS では、ssh.service などの行も並びます。
ss で見る「開いている入口」と、ufw で見る「通してよい入口」は別のもの sudo ss -tlnp でわかるのは、「どのプログラムが、どこで待っているか」です。外から本当に届くかどうかは、そのうえで、ファイアウォールが通すかどうかで決まります。VPS では、次の3つを組み合わせて見ます。
- 待ち受け:
sudo ss -tlnpのLocal Address(この記事の STEP 2) - ufw:
sudo ufw statusで、許可しているポートを見るだけにします(VPS の初期設定の記事) - VPS の管理画面のファイアウォール:VPS の会社によってはあります(会社により異なります)
注意したいのが Docker(=アプリを、必要なものごと「コンテナ」という箱に入れて動かす道具)です。docker run -p 5678:5678 のように公開したポートは、ufw(=Debian・Ubuntu で使える、ファイアウォールをかんたんに設定するための道具)で許可していなくても外から届くことがあります。そのため、n8n(=画面上で「ノード」というブロックをつないで、自動化の流れを作れるツール)などは -p 127.0.0.1:5678:5678 のように、自分の中だけで待つ形にしておくのが安全です(Docker 入門の記事、VPS で n8n を動かす記事)。
どこか1つだけを見て「閉じている」と思いこまないことが、「入口を減らす」の柱のいちばん大事なところです。
STEP 3:更新を確かめる
セキュリティの弱点は、毎日のように見つかり、直されています。更新を放っておくと、直された弱点をねらわれます。
- 1
- 2
- 3
sudo apt update
apt list --upgradable
systemctl status unattended-upgrades --no-pager(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
kaito@demo:~/linux-practice/security-test$ sudo apt update
Get:1 http://deb.debian.org/debian trixie InRelease [140 kB]
Get:2 http://deb.debian.org/debian trixie-updates InRelease [47.3 kB]
Get:3 http://deb.debian.org/debian-security trixie-security InRelease [43.4 kB]
Fetched 231 kB in 0s (1877 kB/s)
All packages are up to date.
kaito@demo:~/linux-practice/security-test$ apt list --upgradable
kaito@demo:~/linux-practice/security-test$ systemctl status unattended-upgrades --no-pager
Unit unattended-upgrades.service could not be found.All packages are up to date. のとおり、更新できるものはなく、apt list --upgradable は何も表示されませんでした。検証環境には unattended-upgrades が最初は入っていないため、Unit unattended-upgrades.service could not be found. と表示されています。
自動アップデートのしくみ(unattended-upgrades)は、「Linuxのバックアップと更新|rsync・tar・自動アップデートで守る【初心者向け】」で説明しています。
STEP 4:ログイン記録と sudo の記録を見る
誰が、いつログインしたか、誰が sudo を使ったかを見ます。前の記事の awk も使えます。
- 1
- 2
- 3
last -n 5
sudo journalctl -u ssh --since "1 day ago" --no-pager | tail -n 10
sudo journalctl _COMM=sudo --since today --no-pager | tail -n 5(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
kaito@demo:~/linux-practice/security-test$ last -n 5
-bash: last: command not found
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since "1 day ago" --no-pager | tail -n 10
-- No entries --
kaito@demo:~/linux-practice/security-test$ sudo journalctl _COMM=sudo --since today --no-pager | tail -n 5
Nov 01 04:17:27 demo sudo[942]: kaito : TTY=pts/0 ; PWD=/home/kaito/linux-practice/security-test ; USER=root ; COMMAND=/usr/bin/journalctl -u ssh --since '1 day ago' --no-pager
Nov 01 04:17:27 demo sudo[942]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=1000)
Nov 01 04:17:27 demo sudo[942]: pam_unix(sudo:session): session closed for user root
Nov 01 04:17:29 demo sudo[947]: kaito : TTY=pts/0 ; PWD=/home/kaito/linux-practice/security-test ; USER=root ; COMMAND=/usr/bin/journalctl _COMM=sudo --since today --no-pager
Nov 01 04:17:29 demo sudo[947]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=1000)検証環境には last コマンドが最初は入っていないため、command not found になりました(日本語の環境では「コマンドが見つかりません」)。SSH のサーバーも入っていないので、journalctl -u ssh は -- No entries --(記録なし)です。
journalctl _COMM=sudo には、直前の2つの sudo journalctl の記録だけが出ています。kaito が、どの場所(PWD)で、どのコマンド(COMMAND)を root として実行したかがわかります。
本物の VPS では、journalctl -u ssh に「Failed password for invalid user admin from …」のような行がたくさん並びます。これが、冒頭で話した「ノック」です。失敗した相手の IP アドレスを数えるなら、次のように書けます(本物の VPS で試すときの例です)。
- 1
sudo journalctl -u ssh --since today --no-pager | grep "Failed password" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5(例)
- 1
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | grep "Failed password" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5検証環境ではログインの失敗がないので、何も表示されませんでした。
$(NF-3) は「後ろから4番目の列」という意味です。この行では、IP アドレスがそこに入っています。
VPS の初期設定の記事でパスワードログインを止めていると、「Failed password」の行はほとんど出なくなり、かわりに「Invalid user … from …」や「Connection closed by …」のような行が並ぶことがあります。何も出ないときは、grep "Failed password" の部分を grep "Invalid user" に、$(NF-3) を $(NF-2) に変えて数えます。grep -i(大文字と小文字を区別しない)にすると、「Connection closed by invalid user …」の行まで入ってしまい、数が2倍になります。列の番号は、まず1行を目で見て確かめてから決めましょう。
「Invalid user」の行を、実際に数える(検証)
検証では、検証用のコンテナ(=アプリと、動かすのに必要なものをひとまとめにした箱)の中だけで SSH のサーバー(openssh-server)を動かし(記録の前に入れました。外からはつながりません)、自分のパソコンの中(127.0.0.1)から、ない名前の admin と nosuchuser で1回ずつログインを試しました。BatchMode=yes は「パスワードを聞かずにあきらめる」という意味で、Permission denied で終わるのが正しい結果です。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
systemctl is-active ssh
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@127.0.0.1
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null nosuchuser@127.0.0.1
sudo journalctl -u ssh --since today --no-pager | tail -n 8
sudo journalctl -u ssh --since today --no-pager | grep "Failed password" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5
sudo journalctl -u ssh --since today --no-pager | grep -i "invalid user"
sudo journalctl -u ssh --since today --no-pager | grep -i "invalid user" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5
sudo journalctl -u ssh --since today --no-pager | grep "Invalid user" | awk '{print $(NF-2)}' | sort | uniq -c | sort -rn | head -n 5(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
kaito@demo:~/linux-practice/security-test$ systemctl is-active ssh
active
kaito@demo:~/linux-practice/security-test$ ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@127.0.0.1
Warning: Permanently added '127.0.0.1' (ED25519) to the list of known hosts.
admin@127.0.0.1: Permission denied (publickey,password).
kaito@demo:~/linux-practice/security-test$ ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null nosuchuser@127.0.0.1
Warning: Permanently added '127.0.0.1' (ED25519) to the list of known hosts.
nosuchuser@127.0.0.1: Permission denied (publickey,password).
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | tail -n 8
Nov 01 06:20:03 demo sshd[859]: Server listening on 0.0.0.0 port 22.
Nov 01 06:20:03 demo sshd[859]: Server listening on :: port 22.
Nov 01 06:22:05 demo sshd-session[1117]: Invalid user admin from 127.0.0.1 port 60786
Nov 01 06:22:05 demo sshd-session[1117]: Connection closed by invalid user admin 127.0.0.1 port 60786 [preauth]
Nov 01 06:22:07 demo sshd-session[1120]: Invalid user nosuchuser from 127.0.0.1 port 60796
Nov 01 06:22:07 demo sshd-session[1120]: Connection closed by invalid user nosuchuser 127.0.0.1 port 60796 [preauth]
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | grep "Failed password" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | grep -i "invalid user"
Nov 01 06:22:05 demo sshd-session[1117]: Invalid user admin from 127.0.0.1 port 60786
Nov 01 06:22:05 demo sshd-session[1117]: Connection closed by invalid user admin 127.0.0.1 port 60786 [preauth]
Nov 01 06:22:07 demo sshd-session[1120]: Invalid user nosuchuser from 127.0.0.1 port 60796
Nov 01 06:22:07 demo sshd-session[1120]: Connection closed by invalid user nosuchuser 127.0.0.1 port 60796 [preauth]
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | grep -i "invalid user" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -n 5
2 from
2 127.0.0.1
kaito@demo:~/linux-practice/security-test$ sudo journalctl -u ssh --since today --no-pager | grep "Invalid user" | awk '{print $(NF-2)}' | sort | uniq -c | sort -rn | head -n 5
2 127.0.0.1journal には、1回の失敗につき「Invalid user admin from 127.0.0.1 port 60786」と「Connection closed by invalid user admin 127.0.0.1 port 60786 [preauth]」の2行が出ました。行の名前のところが sshd ではなく sshd-session になっているのは、Debian 13 の SSH では、ログインの受け付けを sshd-session というプログラムが行うためです(待ち受けの行は sshd のままです)。
Failed password で数える形は、パスワードを送っていないので何も出ませんでした。grep -i "invalid user" では4行が拾われ、$(NF-3) で数えると 2 from と 2 127.0.0.1 という、まちがった結果になりました。「Invalid user … from 127.0.0.1 port 60786」の行では、後ろから4番目が from だからです。grep "Invalid user" と $(NF-2)(後ろから3番目の列)にすると、2 127.0.0.1(127.0.0.1 から2回)と正しく数えられました。
STEP 5:fail2ban で見張りを置く
fail2ban は、ログを見張って、短い時間に何度もログインに失敗した相手を、一定時間ブロックするソフトです。
- 1
- 2
- 3
- 4
sudo apt install fail2ban
systemctl status fail2ban --no-pager
sudo fail2ban-client status
sudo fail2ban-client status sshd(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
kaito@demo:~/linux-practice/security-test$ sudo apt install fail2ban
Installing:
fail2ban
Installing dependencies:
python3-autocommand python3-jaraco.text python3-pyinotify python3-typing-extensions
python3-inflect python3-more-itertools python3-setuptools python3-zipp
python3-jaraco.context python3-pkg-resources python3-systemd whois
python3-jaraco.functools python3-pyasyncore python3-typeguard
Suggested packages:
system-log-daemon monit sqlite3 python-pyinotify-doc python-setuptools-doc
Summary:
Upgrading: 0, Installing: 16, Removing: 0, Not Upgrading: 0
Download size: 1861 kB
Space needed: 9106 kB / 107 GB available
Continue? [Y/n] Y
…(途中省略)…
Setting up fail2ban (1.1.0-8) ...
Created symlink '/etc/systemd/system/multi-user.target.wants/fail2ban.service' → '/usr/lib/systemd/system/fail2ban.service'.
…(途中省略)…
Processing triggers for man-db (2.13.1-1) ...
kaito@demo:~/linux-practice/security-test$ systemctl status fail2ban --no-pager
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-11-01 04:17:36 JST; 10s ago
Invocation: 8d8055ed3c874f48b49f815802b42455
Docs: man:fail2ban(1)
Main PID: 1290 (fail2ban-server)
Tasks: 5 (limit: 19197)
Memory: 12.2M (peak: 12.9M)
CPU: 85ms
CGroup: /system.slice/fail2ban.service
└─1290 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
kaito@demo:~/linux-practice/security-test$ sudo fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
kaito@demo:~/linux-practice/security-test$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- Journal matches: _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:fail2ban に、必要な部品15個がいっしょに入り、合わせて16個になりました。途中の長い表示は省略しています。入れただけで active (running)・enabled(自動で起動)になり、sshd の jail が1つ動いています。
検証環境では SSH へのログインの失敗がないので、Currently failed も Currently banned も 0 で、Banned IP list は空です。Journal matches の行は、「ssh のサービスのログ(journal)を見張っている」という意味です。
fail2ban の「jail(牢屋)」は、見張るサービスごとの設定です。Debian では、最初から SSH 用の sshd の jail が有効になっています。おもな設定を見てみましょう(見るだけです)。
- 1
- 2
cat /etc/fail2ban/jail.d/defaults-debian.conf
grep -E "^(bantime|findtime|maxretry) " /etc/fail2ban/jail.conf(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
kaito@demo:~/linux-practice/security-test$ cat /etc/fail2ban/jail.d/defaults-debian.conf
[DEFAULT]
banaction = nftables
banaction_allports = nftables[type=allports]
[sshd]
backend = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd
enabled = true
kaito@demo:~/linux-practice/security-test$ grep -E "^(bantime|findtime|maxretry) " /etc/fail2ban/jail.conf
bantime = 10m
findtime = 10m
maxretry = 5
…(途中省略)…Debian 13 の fail2ban の sshd の jail は、backend = systemd で、ファイルではなく journal(journalctl で見るログ)を読んで見張ります。ブロックには nftables(Linux のファイアウォールのしくみ)を使います。
grep の最初の3行が、全体の基本の値(bantime = 10m・findtime = 10m・maxretry = 5)です。「10分のあいだに5回失敗したら、10分ブロックする」という意味です。そのあとにも、ほかの jail 用の値(bantime = 48h など)がたくさん続くので、ここでは省略しています。
| 設定 | 意味 |
|---|---|
findtime | この時間のあいだに |
maxretry | この回数失敗したら |
bantime | この時間ブロックする |
ignoreip | ブロックしない相手(自分の IP など) |
fail2ban の設定を変えるときは、jail.conf を直接書きかえず、/etc/fail2ban/jail.local を作って上書きするのが決まりです。ただし、bantime を長くしたり、ignoreip を書きまちがえたりすると、自分自身がブロックされて、サーバーに入れなくなることがあります。わざとログインに失敗してブロックを試すのも、本物のサーバーではやめましょう。もしブロックされたら、VPS の管理画面のコンソールから入り、sudo fail2ban-client set sshd unbanip 自分のIP で解除します。また、ファイアウォール(ufw)を有効にする操作や、SSH の設定ファイル(VPS の初期設定の記事では /etc/ssh/sshd_config.d/ の中のファイル)でパスワードログインや root ログインを止める操作は、まちがえると接続が切れて戻れなくなるので、VPS の初期設定の記事の手順どおりに、管理画面のコンソールを開いた状態で行ってください。
🤖 詰まったらAIに聞こう:このサーバーの守りは足りている?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Linuxの初心者です。VPSのセキュリティを確認しています。
次の結果を見て、危なそうなところと、優先して直す順番を教えてください。
サーバーに入れなくなるおそれがある操作には、必ず注意を書いてください。
【OS】例:Debian 13
【sudo ss -tlnp の結果】ここに貼る(IPアドレスはダミーに置き換える)
【apt list --upgradable の結果】ここに貼る
【sudo fail2ban-client status sshd の結果】ここに貼る(Banned IP list は伏せる)
【気になっていること】例:知らないポートが開いているセキュリティの相談では、とくに貼ってはいけないものが多くなります。.env の中身・秘密鍵・パスワード・API キーは、どんな場合も貼らないでください。サーバーの本物の IP アドレス、ドメイン名(=IP アドレスの代わりに使う、example.com のような名前)、ユーザー名、SSH のポート番号も、203.0.113.10 のようなダミーに置き換えましょう。「どこが守りの弱いところか」がわかる情報は、それ自体が攻撃のヒントになります。
後片づけ
練習用のディレクトリを消します。fail2ban は、本物の VPS では入れたままにしておくのがおすすめです。練習用のパソコンで、もう使わない場合は消してかまいません。
- 1
- 2
- 3
- 4
cd ~/linux-practice
rm -r security-test
ls
sudo apt purge fail2ban(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
kaito@demo:~/linux-practice/security-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ rm -r security-test
kaito@demo:~/linux-practice$ ls
archive-test chmod-test edit-test find-test grep-test link-test perm-test redirect-test
kaito@demo:~/linux-practice$ sudo apt purge fail2ban
The following packages were automatically installed and are no longer required:
python3-pyasyncore python3-pyinotify python3-systemd whois
Use 'sudo apt autoremove' to remove them.
REMOVING:
fail2ban*
Summary:
Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 0
Freed space: 2244 kB
Continue? [Y/n] Y
(Reading database ... 27213 files and directories currently installed.)
Removing fail2ban (1.1.0-8) ...
Processing triggers for man-db (2.13.1-1) ...
(Reading database ... 26929 files and directories currently installed.)
Purging configuration files for fail2ban (1.1.0-8) ...security-test がなくなりました。ほかのフォルダは、これまでの記事の練習で作ったものです。purge は、設定ファイル(/etc/fail2ban)もいっしょに消します。消えたことは、次のように確かめられます。
(例)
- 1
- 2
kaito@demo:~/linux-practice$ ls /etc/fail2ban
ls: cannot access '/etc/fail2ban': No such file or directory日本語の環境では ls: '/etc/fail2ban' にアクセスできません: そのようなファイルやディレクトリはありません と表示されます。
The following packages were automatically installed and are no longer required は、「いっしょに入った部品が、もう使われていません」というお知らせです。消したいときは、表示のとおり sudo apt autoremove で消せます。
よくある質問
Q. last が command not found になります。
Debian 13 では、last は最初から入っていません。Debian 13 のリリースノート(5.1.9)では、util-linux が last と lastb を、login が lastlog を、もう用意しなくなったと案内されています。かわりに、last は wtmpdb(libpam-wtmpdb といっしょに入れる)、lastlog は lastlog2、lastb は lslogins --failed を使います。同じように、ファイルを開いているプログラムを探す lsof も、Debian 13 には最初から入っていません。
検証では、sudo apt install wtmpdb で入れてから last を打ちました(入れるときの表示は省いています。いっしょに libpam-wtmpdb も入りました)。
- 1
- 2
- 3
- 4
- 5
last -n 5
apt-cache show wtmpdb | grep -E '^(Package|Version|Description-en):'
sudo apt install wtmpdb
command -v last; readlink -f "$(command -v last)"
last -n 5(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
kaito@demo:~/linux-practice/security-test$ last -n 5
-bash: last: command not found
kaito@demo:~/linux-practice/security-test$ apt-cache show wtmpdb | grep -E '^(Package|Version|Description-en):'
Package: wtmpdb
Version: 0.73.0-3+deb13u1
Description-en: utility to display login/logout/reboot information
kaito@demo:~/linux-practice/security-test$ command -v last; readlink -f "$(command -v last)"
/usr/bin/last
/usr/bin/wtmpdb
kaito@demo:~/linux-practice/security-test$ last -n 5
open_database_ro: Cannot open database (/var/log/wtmp.db): unable to open database file入れると /usr/bin/last ができ、中身は wtmpdb になっていました。ただし、入れた直後の last -n 5 は「Cannot open database (/var/log/wtmp.db)」(記録のデータベースを開けません)というエラーになりました。入れてすぐに last を使えるとは限らない、ということです(検証では、このあとの動きは確かめていません)。入れなくても、この記事の STEP 4 の journalctl -u ssh でログインの記録は見られます。
Q. ufw があれば、fail2ban はいりませんか?
役割がちがいます。ufw は「どのポートを通すか」を決める門で、SSH のポートは自分が入るために開けておく必要があります。fail2ban は、その開いている SSH の門の前で、何度も失敗する相手を一定時間止める見張りです。鍵ログインだけにしていても、記録が増えすぎるのを防げるので、入れておくと安心です。
Q. WSL でもこの記事のことをやる必要はありますか?
WSL は、ふつうはインターネットから直接つながる場所ではないので、fail2ban や SSH の守りが必要になる場面は少なめです。ただし、WSL の中でサーバーを動かして外に見せる設定をしたときは別です。環境により異なります。.env を chmod 600 にする、chmod 777 にしない、といった最小権限の考え方は、WSL でもそのまま大切です。
まとめ
- サーバーを守る柱は「最小権限・入口を減らす・更新・見張り」の4つ
- 秘密のファイルは
chmod 600。chmod 777はしない -
sudo ss -tlnpで外から見える入口(0.0.0.0)を確かめ、知らないものは調べる -
apt list --upgradableと自動アップデートで、更新を放っておかない -
journalctl -u sshとlastで、ログインの記録を見る - fail2ban は、何度も失敗する相手を一定時間ブロックする見張り。設定の変更は自分を締め出さないよう慎重に
次の記事では、ここまでの知識をまとめて使う、「動かない」ときの切り分けフローを学びます。権限・PATH・ログ・ネットワークの順に見ていく方法です。
次に読む記事
- 前の記事:Linuxのsed・awk入門|ログを集計・置換するワンライナー【初心者向け】
- 次の記事:Linuxで「動かない」ときの切り分けフロー|権限・PATH・ログ・ネットワークの順に見る【初心者向け】
関連する記事
- プロが使う.gitignoreパターン集|現場で効くテンプレ:.env などの秘密のファイルを、Git に入れないようにしたい方に
- n8nとは?最初のワークフローを1本つくる:n8n をサーバーで動かす前に、基本を押さえたい方に
- 自動化スタック総覧|GAS・n8n・Python・Notionの使い分け:どの自動化に、どんな秘密の情報がかかわるかを整理したい方に