VPS Notes
VPS

VPS の初期設定 (Ubuntu 24.04): SSH 鍵・ufw・fail2ban・自動更新を 30 分で済ませる

契約直後の VPS を安全に使い始めるための最小構成。root ログイン禁止、鍵認証、ファイアウォール、fail2ban、自動セキュリティ更新までを、XServer VPS クラウドの実機で通した出力つきで手順化しました。

ubuntusshufwfail2banxserver-vps

本記事にはプロモーション (アフィリエイト広告) が含まれます。

VPS を契約した直後は、root にパスワードで入れる状態で、世界中からログイン試行が飛んできます。この記事では、その状態から 「鍵を持つ自分だけが SSH で入れて、それ以外のポートは閉じていて、セキュリティ更新が勝手に当たる」 状態まで持っていきます。所要時間は 30 分ほどです。

やることは 6 つです。

  1. 作業用ユーザーを作り、root で直接入るのをやめる
  2. SSH を鍵認証だけにする
  3. ufw で SSH 以外を閉じる
  4. fail2ban で総当たりを弾く
  5. unattended-upgrades でセキュリティ更新を自動化する
  6. タイムゾーンとスワップを整える

手順は XServer VPS クラウドで検証していますが、Ubuntu 24.04 が動く VPS ならどこでも同じです。イメージによって最初から入っているもの (スワップ、自動更新など) が違うので、各手順で「今どうなっているか」を確認してから手を入れる形にしています。

この記事で使う環境

XServer VPS クラウド
4GB / NVMe 50GB で月 2,480 円 (12 か月契約なら 2,068 円)。1 日 1 回・7 日分の自動バックアップが標準なので、設定をしくじっても戻せます。この記事の手順はすべてこのプランで検証しています。
公式サイトを見る

XServer VPS クラウドでは、コントロールパネルのパケットフィルターで SSH (22/tcp) だけを許可した状態から始めます。この記事で入れる ufw は OS 側の設定で、パケットフィルターと二重になります。後で Web サーバーなどのポートを開けるときは、両方 で開ける必要があります。片方だけ開けて「繋がらない」となりがちなので覚えておいてください。

1. 最初のログインとパッケージ更新

まず root で入り、パッケージを最新にします。IP アドレスは契約情報のものに置き換えてください。

ssh root@203.0.113.10

初回は「このホストの鍵は未知です。続けますか」と聞かれます。

The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is: SHA256:uy+zqroSnSTqXMicNMlda8XLYJvAqbvbtx0qr/oU3tI
Are you sure you want to continue connecting (yes/no/[fingerprint])?

yes で進めて構いません。厳密にやるなら、VPS パネルのコンソールで ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub を打ち、表示される指紋がこれと同じことを確認します。初回だけの手間で、以降は鍵が記録されるので聞かれません。

パッケージを最新にします。

apt update && apt upgrade -y

XServer VPS クラウドの Ubuntu 24.04 イメージは、契約時点でカーネルが 6.8.0-31 とかなり古く、初回の upgrade でカーネルを含む大量の更新が入りました。私の環境では 2〜3 分かかっています。終わると次のように「再起動を検討してください」と出ます。

Pending kernel upgrade!
Running kernel version:
  6.8.0-31-generic
Diagnostics:
  The currently running kernel version is not the expected kernel version 6.8.0-139-generic.

Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting.

言われた通り再起動します。1 分ほどで SSH できるようになります。

reboot

2. 作業用ユーザーを作る

root で日常作業をしないために、sudo が使える一般ユーザーを作ります。ユーザー名は好きなもので構いません。ここでは deploy にします。

adduser deploy
usermod -aG sudo deploy

adduser はパスワードと、フルネームなどの付加情報を聞いてきます。パスワード以外は Enter で飛ばして構いません。XServer のイメージはロケールが日本語に設定されているので、メッセージも日本語で出ます。

info: ユーザ `deploy' を追加しています...
info: 1000 から 59999 の範囲でUID/GID を選択しています...
info: 新しいグループ `deploy' (1000) を追加しています...
info: 新しいユーザ `deploy' (1000) をグループ `deploy (1000)' として追加しています...
info: ホームディレクトリ `/home/deploy' を作成しています...
info: `/etc/skel' からファイルをコピーしています...
新しいパスワード:
新しいパスワードを再入力してください:
passwd: パスワードは正しく更新されました
deploy のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
    フルネーム []:
    部屋番号 []:
    職場電話番号 []:
    自宅電話番号 []:
    その他 []:
以上でよろしいですか? [Y/n] Y
info: 追加のグループに新しいユーザ 'deploy' を追加しています 'users' ...
info: ユーザ `deploy' をグループ `users' に追加しています...

鍵認証に切り替えた後も、このパスワードは sudo で使うので控えておいてください。

3. SSH を鍵認証にする

ローカルで鍵を作る

ローカル (Mac) 側で鍵ペアを作ります。

ssh-keygen -t ed25519 -C "deploy@vps"

保存先を聞かれたら Enter で ~/.ssh/id_ed25519 に、パスフレーズは任意です。

既に ~/.ssh/id_ed25519 がある場合は、上書きしないでください。 already exists. Overwrite (y/n)? と聞かれたら n で止め、-f で別名にします。

ssh-keygen -t ed25519 -f ~/.ssh/vps_ed25519 -C "deploy@vps"

私はここで y を押してしまい、既存の鍵を上書きしました。幸い GitHub もラズパイも別の鍵を使う設定だったので実害はありませんでしたが、その鍵で入っていたサーバーがあれば締め出されるところでした。別名にした場合は、以降の ssh-copy-idssh-i ~/.ssh/vps_ed25519 をつけるか、~/.ssh/config にこう書いておくと楽です。

Host vps
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/vps_ed25519

公開鍵を VPS に送る

ssh-copy-id が一番確実です。ユーザーのパスワードを聞かれるので、手順 2 で設定したものを入れます。

ssh-copy-id deploy@203.0.113.10
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/you/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
deploy@203.0.113.10's password:

Number of key(s) added:        1

Now try logging into the machine, with: "ssh 'deploy@203.0.113.10'"
and check to make sure that only the key(s) you wanted were added.

送れたら、root のセッションは閉じずに 別のターミナルから鍵で入れることを確認します。ここで確認せずに次へ進むと、締め出されたときに戻れなくなります。

ssh deploy@203.0.113.10

パスワードを聞かれずに入れれば成功です。ログイン時の表示から、いくつか環境のことが分かります。

Welcome to Ubuntu 24.04.5 LTS (GNU/Linux 6.8.0-139-generic x86_64)

  System load:  0.08              Processes:               155
  Usage of /:   8.8% of 47.39GB   Users logged in:         1
  Memory usage: 6%                IPv4 address for enp3s0: 172.16.x.x
  Swap usage:   0%

タイムゾーンは最初から JST でした (System information as of ... JST)。ディスクは 50GB プランで実容量 47.39GB です。インターフェース enp3s0 に付いているのはプライベート IP で、グローバル IP は XServer 側で NAT されています。この構成は後で Tailscale や Docker を使うときに関係してくるので、覚えておいてください。

パスワード認証と root ログインを止める

まず、sshd が今どういう設定で動いているかを見ます。sshd -T は、設定ファイルがどこに分かれていても、最終的に有効な値を出してくれます。

sudo sshd -T | grep -iE "^(passwordauthentication|permitrootlogin|pubkeyauthentication|kbdinteractiveauthentication)"
permitrootlogin yes
pubkeyauthentication yes
passwordauthentication yes
kbdinteractiveauthentication no

root のパスワードログインと、パスワード認証そのものが有効です。これを止めます。

Ubuntu の /etc/ssh/sshd_config は先頭に Include /etc/ssh/sshd_config.d/*.conf があり、そこに置いたファイルが本体より先に読まれます。sshd は 先に読まれた設定が勝つ ので、本体を書き換えずに、sshd_config.d/ に自分の設定を置くだけで上書きできます。

sudo tee /etc/ssh/sshd_config.d/10-hardening.conf <<'EOS'
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOS

XServer VPS クラウドの Ubuntu 24.04 イメージでは sshd_config.d/ は空で、PermitRootLogin yes などは本体の sshd_config に書かれていました。一方、Ubuntu 公式のクラウドイメージを使う VPS では、sshd_config.d/50-cloud-init.confPasswordAuthentication yes が置かれていることがあります。どちらの場合でも、10- で始まるファイル名なら先に読まれるので、この手順で上書きできます。

設定に文法エラーがないか確認してから反映します。

sudo sshd -t && sudo systemctl restart ssh

もう一度 sshd -T で、値が変わったことを確認します。

sudo sshd -T | grep -iE "^(passwordauthentication|permitrootlogin)"
permitrootlogin no
passwordauthentication no

Ubuntu 22.10 以降は SSH が ssh.socket で起動する構成になっています。systemctl restart sshd ではなく ssh を指定してください。

反映後、また別のターミナルから 鍵で入れること、ssh root@...Permission denied (publickey) で弾かれることの両方を確認します。

ssh root@203.0.113.10
root@203.0.113.10: Permission denied (publickey).

ここまでできたら、最初に開いていた root のセッションを閉じて大丈夫です。

SSH のポートは変えなくていい

ポートを 22 から変える手順を勧める記事が多いですが、この記事では変えません。鍵認証だけにした時点で総当たりは成立しませんし、ポートを変えるとツールやドキュメントの前提とずれて、後の手順で毎回引っかかります。ログが煩わしいなら、後述の fail2ban で十分です。

4. ufw で SSH 以外を閉じる

ufw は Ubuntu に最初から入っています。「入ってくる通信は全部拒否、SSH だけ許可」にします。

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable

enable のときに「SSH が切れるかもしれない」と聞かれますが、OpenSSH を先に許可しているので y で進めて問題ありません。

Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
ファイアウォールはアクティブかつシステムの起動時に有効化されます。
sudo ufw status verbose
状態: アクティブ
ロギング: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
新しいプロファイル: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)

Web サーバーを立てるときは、そのときに sudo ufw allow 80,443/tcp のように足します。最初から開けておく必要はありません。

Docker を使う予定がある場合、Docker は ufw を迂回して iptables を直接書き換えます。「ufw で閉じたのにコンテナのポートが外から見える」という事故はここから起きます。Docker を入れる記事で改めて扱いますが、-p 127.0.0.1:8080:8080 のようにループバックに束縛する癖をつけておくと安全です。

5. fail2ban で総当たりを弾く

鍵認証だけなので侵入はされませんが、試行のログが延々と溜まるのは気持ちが悪いので、一定回数失敗した IP を遮断します。

sudo apt install -y fail2ban

設定は /etc/fail2ban/jail.local に書きます。ここで 1 つ決めておくことがあります。fail2ban が SSH の失敗ログをどこから読むかです。

Ubuntu 24.04 の公式クラウドイメージには rsyslog が入っておらず、/var/log/auth.log がありません。fail2ban の sshd jail はデフォルトでこのファイルを見に行くので、そのままだと起動に失敗します。一方、XServer のイメージには rsyslog が入っていて auth.log は存在していました。

ls -la /var/log/auth.log
-rw-r----- 1 syslog adm 5846129  9月 14 20:05 /var/log/auth.log

どちらのイメージでも動くように、読み取り先を journald にします (backend = systemd)。ファイルの有無に関係なく systemd のジャーナルから読むので、環境を選びません。

sudo tee /etc/fail2ban/jail.local <<'EOS'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd

[sshd]
enabled = true
EOS
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:    3
|  |- Total failed:    85
|  `- Journal matches:    _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned:    3
   |- Total banned:    3
   `- Banned IP list:    183.252.x.x 202.165.x.x 160.187.x.x

Journal matches が表示されていれば journald から読めています。

注目してほしいのは数字の方です。これは 有効化した直後の出力 で、すでに 85 回の失敗と 3 つの IP の遮断が記録されています。SSH をインターネットに開けてから 1 日ほどで、auth.log は 5.8MB になっていました。その間ずっと、世界中から root のパスワードを当てに来ていたということです。鍵認証にしていたので入られてはいませんが、「契約直後の VPS はこういう状態で置かれている」と知っておく価値はあります。

6. セキュリティ更新を自動で当てる

unattended-upgrades が入っていると、セキュリティ更新が毎日自動で適用されます。XServer のイメージには最初から入っていて、有効になっていました。

sudo apt install -y unattended-upgrades
unattended-upgrades はすでに最新バージョン (2.9.1+nmu4ubuntu1) です。

有効かどうかは /etc/apt/apt.conf.d/20auto-upgrades で分かります。両方 "1" なら動いています。

cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

このファイルがない、または "0" になっている場合は、sudo dpkg-reconfigure -plow unattended-upgrades でダイアログを出して「Yes」を選ぶと有効になります。

カーネル更新の後に自動で再起動させたい場合は、次の設定を足します。個人用の VPS なら深夜に再起動させて問題ないことが多いです。

sudo tee /etc/apt/apt.conf.d/52-auto-reboot <<'EOS'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
EOS

ゲームサーバーやデータベースを動かす予定なら、この自動再起動は入れずに、自分のタイミングで再起動する運用にしてください。

7. タイムゾーンとスワップ

XServer のイメージはタイムゾーンが最初から JST です。念のため確認しておきます。

timedatectl | grep "Time zone"
                Time zone: Asia/Tokyo (JST, +0900)

他社の VPS で UTC になっている場合は sudo timedatectl set-timezone Asia/Tokyo で変えます。

メモリ 4GB でも、Docker のビルドや複数サービスの同居で OOM が起きることがあります。スワップがあれば、遅くなっても落ちなくなります。まず今の状態を見ます。

free -h
               total        used        free      shared  buff/cache   available
Mem:           3.8Gi       444Mi       2.9Gi       5.1Mi       755Mi       3.4Gi
Swap:          2.0Gi          0B       2.0Gi

XServer のイメージには 2GB のスワップが最初からありました。初期状態のメモリ使用量は 444MB で、残り 3.4GB が使えます。この値は、後で Claude Code や n8n を載せたときの比較の基準になります。

Swap:0B の環境では、次のように 2GB のスワップを作ります。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

最終確認

ここまでの状態をまとめて確認します。

sudo ss -tlnp
sudo ufw status
sudo fail2ban-client status
systemctl is-enabled unattended-upgrades
State   Recv-Q  Send-Q   Local Address:Port   Peer Address:Port  Process
LISTEN  0       4096        127.0.0.54:53          0.0.0.0:*      users:(("systemd-resolve",pid=594,fd=17))
LISTEN  0       4096     127.0.0.53%lo:53          0.0.0.0:*      users:(("systemd-resolve",pid=594,fd=15))
LISTEN  0       4096           0.0.0.0:22          0.0.0.0:*      users:(("sshd",pid=27581,fd=3),("systemd",pid=1,fd=185))
LISTEN  0       4096              [::]:22             [::]:*      users:(("sshd",pid=27581,fd=4),("systemd",pid=1,fd=186))
状態: アクティブ

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Status
|- Number of jail:    1
`- Jail list:    sshd
enabled

ss で外向きに LISTEN しているのは 0.0.0.0:22 だけです。127.0.0.53127.0.0.54 の 53 番は systemd-resolved の DNS キャッシュで、ループバックにしか束縛されていないので外からは見えません。ufw は SSH のみ許可、fail2ban は sshd jail が動作、自動更新は有効。これで土台は完成です。

つまずいた点

ログインから最終確認まで、実際にやって引っかかったところです。契約とパケットフィルターでのつまずきは申し込みの記事に書きました。

所要時間は 30 分ほどでした。

次にやること

まとめ

この状態にしておけば、何を載せても土台の心配はいりません。

XServer VPS クラウド
この記事の検証環境。自動バックアップが標準で、コントロールパネルからの OS 再インストールも数分で終わります。
公式サイトを見る