この記事は、VPS や EC2 で Web サイトを運用していて、サーバに SSH で入る Web 担当者・制作会社向けです。
Web サーバの設定は固めたのに、SSH は「インストールしたときのまま」になっていないでしょうか。
sshd の設定を 7 つ見直して、パスワード総当たりで入られない状態にし、それが本当に効いているかをコマンドで確認するところまでをまとめます(前提は Ubuntu 22.04/24.04・Debian 12 の OpenSSH)。
まず現状確認: 今の SSH は狙われているか
インターネットに 22 番を開けているサーバには、公開直後から自動化されたログイン試行が届きます。まず自分のサーバのログで数を見ておきます。
# 直近 24 時間のログイン失敗の件数(Ubuntu/Debian のユニット名は ssh)
sudo journalctl -u ssh --since "24 hours ago" --no-pager \
| grep -cE "Failed password|Invalid user"
件数が多くても慌てる必要はありませんが、パスワード認証が有効なままなら、どこかで弱いパスワードが当たる可能性は残り続けます。以下の 7 設定でそこを断ちます。
作業前の約束: 自分を締め出さない
SSH の設定ミスは「サーバに二度と入れない」事故に直結します。次の 3 点を守ってください。
- 今つないでいるセッションは閉じない(設定を反映しても既存セッションは切れません)。確認は必ず別のターミナルから新規接続で行う
- 反映前に必ず
sudo sshd -tで構文チェックする(何も出なければ OK) - VPS 管理画面のコンソール / EC2 シリアルコンソールなど、SSH 以外の入り口を確認しておく
設定1: 鍵認証に切り替える
パスワードの代わりに公開鍵で認証します。鍵は手元の PC で作り、公開鍵だけをサーバへ置きます。
# 手元の PC で実行(パスフレーズは必ず付ける)
ssh-keygen -t ed25519 -C "yourname@example.com"
# 公開鍵をサーバへ登録(まだパスワードで入れるうちに)
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@203.0.113.10
サーバ側で権限を確認します。~/.ssh やファイルの権限が緩いと、sshd は鍵を無視します(StrictModes の既定動作)。
# サーバ側(deploy ユーザーで)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
ls -la ~/.ssh
この時点で別ターミナルから鍵でログインできることを確認してから次へ進みます。鍵ログインを確認せずにパスワード認証を切るのが、締め出し事故の典型です。
設定2: パスワード認証を止める(drop-in の罠に注意)
ここが一番ハマりやすいところです。OpenSSH の sshd_config は**「同じキーワードは最初に読んだ値が使われる」仕様です。Ubuntu/Debian の /etc/ssh/sshd_config は先頭で Include /etc/ssh/sshd_config.d/*.conf を読み込むため、sshd_config.d/ 内のファイルが本体の設定より優先されます**。
さらに cloud-init で作られたサーバ(多くの VPS / EC2 イメージ)では、/etc/ssh/sshd_config.d/50-cloud-init.conf に PasswordAuthentication yes が書かれていることがあります。この状態で本体の sshd_config に PasswordAuthentication no と書いても効きません。
まず現状を確認します。
ls -l /etc/ssh/sshd_config.d/
sudo grep -rn "PasswordAuthentication" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
対策は、50 より先に読まれる名前で自分の設定ファイルを置くことです(ファイルは名前順に読まれます)。
sudo tee /etc/ssh/sshd_config.d/01-hardening.conf > /dev/null <<'EOF'
# 01-hardening.conf : 50-cloud-init.conf より先に読ませて優先させる
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
EOF
KbdInteractiveAuthentication は、パスワード入力をキーボード対話方式で受け付ける経路です。OpenSSH 8.7 で旧名 ChallengeResponseAuthentication からこの名前に整理されました。PasswordAuthentication だけ止めてもこちらが開いていると経路が残るので、両方 no にします。
cloud-init 側の設定(ssh_pwauth: false)でも無効にしておくと、50 番が書き戻されても安心です。
設定3: root の直接ログインを禁止し、入れるユーザーを限定する
root で直接入れる状態は、ユーザー名を推測する手間すら攻撃側に与えません。作業用ユーザーで入ってから sudo する運用にします。
sudo tee -a /etc/ssh/sshd_config.d/01-hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
# SSH で入ってよいユーザーだけを列挙(スペース区切り)
AllowUsers deploy
EOF
PermitRootLogin の既定値は prohibit-password(root も鍵なら可)なので no にします。AllowUsers を書くと列挙したユーザー以外はログインできません。制作会社と共用しているサーバでは、足し忘れで相手を締め出さないよう事前に一覧を合意しておきます。
設定4: 試行回数と待ち時間を絞る
1 接続あたりの試行回数と、ログイン完了までの猶予を短くします。
sudo tee -a /etc/ssh/sshd_config.d/01-hardening.conf > /dev/null <<'EOF'
# 1 接続での認証試行回数(既定 6)
MaxAuthTries 3
# ログイン完了までの猶予(既定 120 秒)
LoginGraceTime 30
EOF
ssh-agent に鍵を多数登録していると正しい鍵の前に打ち切られることがあるので、手元の ~/.ssh/config で IdentitiesOnly yes と IdentityFile を指定します。
なお、OpenSSH 9.8 以降には PerSourcePenalties という仕組みが既定で有効になっており、認証に失敗し続ける送信元を一定時間受け付けなくなります(既定は認証失敗 1 回あたり 5 秒・上限 10 分)。Ubuntu 24.04 LTS や Debian 12 の標準パッケージは 9.8 より前のため、この機能はありません。自分のサーバのバージョンは次で確認できます。
ssh -V
sudo sshd -T | grep -i persourcepenalties # 9.8 以降なら値が表示される
設定5: 入口そのものを絞る(ファイアウォール / AWS)
sshd の設定はすべて「接続が届いたあと」の話です。管理者の接続元 IP が決まっているなら、そもそも 22 番に届かせないのが一番効きます。
Ubuntu の ufw の例です。SSH を許可するルールを先に入れてから有効化してください(逆順だとその場で締め出されます)。
# 管理者の固定 IP からのみ 22 番を許可
sudo ufw allow from 198.51.100.20 to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 既に「どこからでも SSH 可」のルールがあれば番号を確認して削除
sudo ufw status numbered
# sudo ufw delete <番号>
sudo ufw enable
sudo ufw status verbose
AWS の EC2 なら、セキュリティグループで 22 番の送信元を 0.0.0.0/0 にしないことです。
# 管理者 IP だけに 22 番を許可(sg-xxxx は自分のセキュリティグループ ID)
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 22 --cidr 198.51.100.20/32
AWS Systems Manager の Session Manager を使えば、22 番を閉じたまま入る構成も取れます。
ポート番号の変更はスキャンのログが減るだけで、守りの本体にはなりません。変える場合、Ubuntu 22.10 以降は sshd がソケット起動(ssh.socket)のため、sudo systemctl daemon-reload と sudo systemctl restart ssh.socket まで実行しないと待ち受けポートが変わらない点に注意します。
設定6: fail2ban で失敗の多い送信元を自動遮断する
送信元 IP を固定できない(外出先から入る等)場合は、fail2ban でログイン失敗の多い IP を一定時間遮断します。
sudo apt install -y fail2ban python3-systemd
Debian/Ubuntu のパッケージは /etc/fail2ban/jail.d/defaults-debian.conf で sshd の jail が最初から有効です。ただし Debian 12 など rsyslog を入れていない環境には /var/log/auth.log が無く、ファイルを監視する設定のままでは何も検知しません。ログの読み先を journald にします。
# /etc/fail2ban/jail.local
[DEFAULT]
# 自分の管理用 IP は遮断対象から外す(締め出し防止)
ignoreip = 127.0.0.1/8 ::1 198.51.100.20
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd # Currently failed / Currently banned が動いていれば稼働中
設定7: 「入られていないか」を定期的に確認する
最後に、設定で防ぐだけでなく痕跡を見る習慣を作ります。特に authorized_keys は、侵入した側が「次も入れるように」自分の公開鍵を追記する場所です。見覚えのない鍵が無いか、定期的に棚卸しします。
# 全ユーザーの authorized_keys と登録鍵の指紋を一覧
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do
[ -f "$f" ] || continue
echo "== $f ($(stat -c '%y' "$f"))"
sudo ssh-keygen -lf "$f"
done
# 最近ログインに成功した記録(ユーザー・接続元・時刻)
last -n 20 -a
sudo journalctl -u ssh --since "7 days ago" --no-pager | grep "Accepted"
見覚えのない接続元や時間帯の Accepted があれば、鍵の入れ替えとサイトの改ざん確認に進みます。
反映と確認
設定ファイルを書いたら、構文チェック → 反映 → 別ターミナルから確認します。
# 1. 構文チェック(何も出なければ OK)
sudo sshd -t
# 2. 実際に効いている値を確認(drop-in の優先順位も反映された最終値)
sudo sshd -T | grep -Ei "^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|maxauthtries|logingracetime|allowusers)"
# 3. 反映(既存セッションは切れない)
sudo systemctl reload ssh
sshd -T で passwordauthentication no と出ていなければ、どこかのファイルが先に yes を設定しています(設定2 を再確認)。
最後に、別ターミナルからパスワード認証が拒否されることを確かめます。
# 手元の PC から。鍵を使わずパスワードだけで試す
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@203.0.113.10
# → "Permission denied (publickey)." と出れば、パスワード経路は閉じている
# 鍵では従来どおり入れること
ssh deploy@203.0.113.10
まとめ
| # | 設定 | 効果 |
|---|---|---|
| 1 | 鍵認証(ed25519 + パスフレーズ) | 推測・総当たりが成立しない認証へ |
| 2 |
PasswordAuthentication no / KbdInteractiveAuthentication no を 01- の drop-in で |
cloud-init の上書きを回避して確実にパスワード経路を閉じる |
| 3 |
PermitRootLogin no + AllowUsers
|
入れるアカウントを最小化 |
| 4 |
MaxAuthTries / LoginGraceTime
|
1 接続あたりの試行と滞留を削る |
| 5 | ufw / セキュリティグループで送信元制限 | そもそも 22 番に届かせない |
| 6 | fail2ban(backend = systemd) |
送信元を固定できない場合の自動遮断 |
| 7 |
authorized_keys 棚卸し + ログイン記録確認 |
入られた痕跡に早く気づく |
ポイントは、設定を書いたら sshd -T で最終値を確認することです。「書いたはずなのに効いていない」状態が、SSH ではいちばん危険です。
関連記事
本記事のような設定の抜けは、Web サイトを 9 つの守りでまるごと守るサイトドックでまとめて対策できます → https://sitedock.jp