はじめに:「公開できた」と「安全」は別の話
AIや入門記事を見ながらVPSにアプリを公開できた。初期設定も一通りやった。でも、このまま放っておいて大丈夫かは分からない、という人は多いと思います。
世の中のチェックリストの多くは「契約直後にこう設定しよう」という手順です。この記事は向きが逆で、アプリを公開した直後に、今のサーバーの状態を確認するためのものです。
結論を先に書きます。
見るべきなのは「設定した覚え」ではなく「今、実際に効いている値」です。
設定ファイルを書いた覚えがあっても、別のファイルで上書きされていたり、そもそも有効になっていなかったりします。なので、この記事では各項目について 確認コマンド → 見方 → 次にやること の順に並べました。
10項目の一覧
| # | 項目 | 確認コマンド | 期待する状態 |
|---|---|---|---|
| 1 | SSHの実効設定 | sudo sshd -T |
パスワード認証 no / root直ログイン no か prohibit-password |
| 2 | 鍵で入れる一般ユーザー | 別端末から ssh
|
一般ユーザーで鍵ログインできる |
| 3 | UFWの状態 | sudo ufw status verbose |
active、許可は必要なポートだけ |
| 4 | 待ち受けの棚卸し | sudo ss -tlnp |
想定外の 0.0.0.0 / [::] がない |
| 5 | Docker公開ポート | docker ps |
DB等が 0.0.0.0 で公開されていない |
| 6 | 事業者側ファイアウォール | VPSのコントロールパネル | 必要なポートだけ許可 |
| 7 | 自動セキュリティ更新 | cat /etc/apt/apt.conf.d/20auto-upgrades |
Unattended-Upgrade "1" |
| 8 | 再起動待ち | ls /var/run/reboot-required |
ファイルがない(または計画的に再起動) |
| 9 | Fail2Ban(任意) | sudo fail2ban-client status sshd |
入れているなら sshd jail が動いている |
| 10 | 時刻とタイムゾーン | timedatectl |
時刻同期が有効、意図したタイムゾーン |
検証環境
Ubuntu 22.04.5 LTS / OpenSSH 8.9p1 / ufw 0.36.1 / Docker 29.5.2
この記事のコマンドと出力は Ubuntu 22.04 で確認したものです。24.04 でもコマンドは同じものが使える想定ですが、出力や既定値の違いは確認していません(推測)。
出力例は筆者のVPSの実際の出力をもとに、サービス名・ユーザー名・ポート番号を一般的なものに置き換えています。
1. SSHの実効設定を見る
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|pubkeyauthentication|kbdinteractiveauthentication) '
sshd -T は、設定ファイルを読み込んだ結果、最終的に効いている設定を一覧表示するモードです(sshd(8) の -T: Extended test mode)。
出力例(Ubuntu 22.04.5 で確認):
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
見方
| 表示 | 意味 | 次にやること |
|---|---|---|
passwordauthentication no |
パスワードでのログイン不可 | OK |
passwordauthentication yes |
パスワード総当たりの対象になる | 項目2を確認してから no にする |
permitrootlogin no / prohibit-password
|
rootはログイン不可/鍵のみ | OK |
permitrootlogin yes |
rootにパスワードで入れる | 項目2を確認してから変更 |
なぜ設定ファイルを読むだけではダメなのか
sshd_config は Include で別ファイルを読み込めます。また、同じキーワードは最初に出てきた値が使われます(sshd_config(5))。
Ubuntu 22.04.5 の筆者環境では、/etc/ssh/sshd_config の12行目に Include /etc/ssh/sshd_config.d/*.conf があり、sshd_config.d 配下のファイルが本体の設定より先に読み込まれていました。つまり、本体に PasswordAuthentication no と書いても、sshd_config.d/*.conf に yes があればそちらが効きます。だから「書いたファイル」ではなく sshd -T の結果を見ます。
sshd -T の全体値だけでは見えない例外
もう一つ落とし穴があります。Match ブロックで特定のユーザーだけ設定を変えている場合です。筆者の環境にも、バックアップ用ユーザーだけパスワード認証を許すファイルがありました(以下は名前を一般化した例)。
# /etc/ssh/sshd_config.d/90-backup.conf
Match User backup-user
PasswordAuthentication yes
この状態でも、sudo sshd -T の全体値は passwordauthentication no と表示されました。Match の条件に当てはまる接続を指定しないと、例外は表示されないためです。
-T に -C で接続条件(ユーザー・ホスト・アドレスなど)を渡すと、その接続に Match を当てはめた結果を確認できます(sshd(8) の -C connection_spec)。
sudo sshd -T -C user=backup-user,host=localhost,addr=127.0.0.1 | grep passwordauthentication
マニュアルでは、-C を指定すると、その接続条件に合う Match ブロックの設定が反映された結果が表示されると説明されています。上の例なら、backup-user の接続では passwordauthentication yes と表示されるはずです(このコマンドの出力そのものは、筆者は確認していません)。
例外がないかは、まず Match を探すのが手早いです。
sudo grep -rn '^\s*Match' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
例外が意図したものなら、そのユーザーは鍵認証に切り替えられないか、接続元アドレスで絞れないか(Match User backup-user Address ...)を検討します。
2. 鍵で入れる一般ユーザーがいるか
項目1で設定を変える前に、必ずこれを確認します。
- 今つないでいるSSHセッションは閉じずに残しておく
- 別の端末(別ターミナル)から、一般ユーザー+鍵でログインする
ssh -i ~/.ssh/id_ed25519 youruser@<VPSのIP>
- ログインできて
sudoも使えたら、パスワード認証やroot直ログインを止める
順番を逆にすると、自分も締め出されます。締め出された場合は、VPS事業者のWebコンソールから入って直すことになります。
3. UFWの状態と許可ポート
sudo ufw status verbose
status verbose で、有効かどうか、既定のポリシー、ルール一覧が表示されます(ufw(8) Ubuntu manpage)。
出力例(Ubuntu 22.04.5 で確認、v6の行は省略):
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80,443/tcp (Nginx Full) ALLOW IN Anywhere
見方
-
Status: inactive→ ファイアウォールが動いていない。SSHを許可してからsudo ufw enable -
Default: deny (incoming)になっているか - 許可が 22 / 80 / 443 など必要なものだけか。開発中に開けた 3000 や 8080 が残っていないか
注意: Dockerで公開したポートにはUFWのルールが効きません(Docker: Packet filtering and firewalls)。上の例のようにUFWが 22/80/443 だけでも、Dockerで公開したポートは別に確認が必要です(項目5)。
4. 待ち受けの棚卸し
sudo ss -tlnp
今どのポートで、どのアドレスに対して待ち受けているかを一覧します。
| Local Address の表示 | 意味 |
|---|---|
127.0.0.1:5432 / [::1]:5432
|
サーバー内からだけ届く |
0.0.0.0:5432 / [::]:5432 / *:5432
|
すべてのアドレスで待ち受け。ファイアウォール次第で外から届く |
筆者のVPSでは、nginx(80/443)と sshd(22)のほかに、アプリのAPIサーバーが複数のポート(3000番台相当で4つ)、Dockerの docker-proxy が2つ待ち受けていました。公開しているつもりのサービスより、待ち受けているプロセスのほうが多いのはよくあることです。それぞれの Local Address が 127.0.0.1 か 0.0.0.0 かを1行ずつ見ます。
特に注意したいのは DB系(5432 PostgreSQL、3306 MySQL、6379 Redis、27017 MongoDB) と、開発用サーバー(3000、5173、8000、8080など)です。これらが 0.0.0.0 で出ていたら、本当に外から届くかを確かめます。
表示の見方と外からの実測の詳細は、DockerとUFWの記事を参照してください。
5. Docker公開ポート
docker ps --format 'table {{.Names}}\t{{.Ports}}'
出力例(筆者環境をもとに名前とポートを一般化、アドレスは省略):
NAMES PORTS
app-api-1 <アドレス>:3001->3001/tcp
app-api-2 <アドレス>:3002->3002/tcp
app-db-1 5432/tcp
app-db-2 5432/tcp
読み方:
-
app-db-1 5432/tcpのように->がない → コンテナ内だけで待ち受けていて、ホストには公開されていない。DBはこれが理想 -
0.0.0.0:3001->3001/tcpのように出る → ホストの全アドレスで公開。Composeでホスト側アドレスを省略して"3001:3001"と書くとこうなります(Docker: Port publishing and mapping) -
127.0.0.1:3001->3001/tcp→ ホスト内からだけ届く(nginx経由で公開する構成ならこれでよい)
ここでは深掘りしません。0.0.0.0 が出たら、DockerとUFWの記事の手順に進んでください。
- アプリのコンテナからしか使わないDBなら、
ports:自体を書かない(サービス名で届く) - 手元から触りたいなら
"127.0.0.1:5432:5432"+ SSHトンネル
6. 事業者側のファイアウォール
多くのVPSには、サーバーの外側で通信を絞る仕組み(パケットフィルタ、セキュリティグループなど、名前は事業者によって違います)があります。画面や名称は事業者ごとに違うので、ここでは一般的な確認方法だけ書きます。
- VPSのコントロールパネルで、その機能が有効か、どのポートが許可されているかを確認する
- 許可の一覧と、項目3(UFW)・項目5(Docker)の結果を並べて、意図しないポートが外側でも許可されていないかを見る
- 実際に外から届くかは、手元のPCから確かめる(DockerとUFWの記事のStep2)
7. 自動セキュリティ更新
cat /etc/apt/apt.conf.d/20auto-upgrades
出力例(Ubuntu 22.04.5 で確認):
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
Ubuntuは unattended-upgrades パッケージで、セキュリティ更新を自動適用します。このファイルが有効/無効と実行間隔を決めていて、"1" は毎日、"0" は無効です(Ubuntu Server docs: Automatic updates)。
公式ドキュメントでは「既定でインストールされ、セキュリティ更新の自動適用が有効」とされています(同上)。筆者の 22.04.5 のVPSでも上のとおり有効でした。VPS事業者のイメージによって違う可能性はあるので、自分のサーバーで確かめます。24.04 のイメージでの値は確認していません。
実際に動いているかはログで見ます。ログの場所は /var/log/unattended-upgrades/ です(同上)。
sudo ls -l /var/log/unattended-upgrades/
sudo tail -n 20 /var/log/unattended-upgrades/unattended-upgrades.log
ファイルが無い場合は sudo apt install unattended-upgrades で入れられます(同上)。
8. 再起動待ちになっていないか
ls /var/run/reboot-required && cat /var/run/reboot-required.pkgs
更新の中には、再起動しないと反映されないもの(カーネルなど)があります。Ubuntuでは、再起動が必要な更新があると /var/run/reboot-required が作られ、unattended-upgrades もこのファイルを見て再起動要否を判断します(Ubuntu Server docs: Automatic updates – Reboots)。
自動再起動は既定で無効(Automatic-Reboot "false")です(同上)。筆者の 22.04.5 でも 50unattended-upgrades の Automatic-Reboot はコメントアウトされたまま(既定の false)で、確認した時点で /var/run/reboot-required が存在していました。自動更新は毎日動いていても、再起動しない限り反映されない更新が溜まっていたわけです。
- ファイルがある → アプリへの影響が少ない時間に
sudo reboot - 再起動後、アプリやコンテナが自動で起動するか(
restart: unless-stoppedなど)も確認
9. Fail2Ban(任意)
この記事では必須にはしません。項目1でパスワード認証を切っていれば、総当たりで侵入されるリスクは大きく下がるからです。筆者のVPSにも入れていません。
ただし、項目1の Match の例外でパスワード認証を許しているユーザーがいるなら、話は別です。その場合は、例外をなくすか、接続元を絞るか、Fail2Ban を入れるかを検討します。
入れている場合は、sudo fail2ban-client status sshd で sshd の jail が動いているかを確認できます。
10. 時刻とタイムゾーン
timedatectl
出力例(Ubuntu 22.04.5 で確認、時刻の行は省略):
Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
NTP service: active
-
System clock synchronized: yesか。時刻がずれると、ログの突き合わせや証明書の期限判断を誤る -
Time zoneが意図どおりか。UTCのままでも問題はないが、ログを読むときにどちらか分かっていること
タイムゾーンは timedatectl set-timezone で変更できます。指定できる値は timedatectl list-timezones で一覧できます(timedatectl(1))。
sudo timedatectl set-timezone Asia/Tokyo
まとめ:コピー用チェックリスト
[ ] 1 sudo sshd -T で passwordauthentication no / permitrootlogin no or prohibit-password
Match の例外がないか grep し、あれば sshd -T -C user=... で確認
[ ] 2 別端末から一般ユーザー+鍵でログインできる(設定変更の前に確認)
[ ] 3 sudo ufw status verbose で active、deny (incoming)、許可は必要なポートだけ
[ ] 4 sudo ss -tlnp で想定外の 0.0.0.0 / [::] がない(特にDB・開発用ポート)
[ ] 5 docker ps で DB 等が 0.0.0.0 公開になっていない → あればDocker×UFWの記事へ
[ ] 6 VPSコントロールパネルのファイアウォールも確認
[ ] 7 20auto-upgrades が "1"、/var/log/unattended-upgrades/ にログがある
[ ] 8 /var/run/reboot-required がない(あれば計画的に再起動)
[ ] 9 (任意)Fail2Ban を入れているなら sshd jail が動いている
[ ] 10 timedatectl で時刻同期 yes、タイムゾーンが意図どおり
関連記事
- UFWで閉じていてもDockerの公開ポートは別扱い(Docker×UFWの実機検証)
- ポートの公開先を確認する: https://qiita.com/supportapp3/items/f33e81e397be21d68dcd
同じ確認を一括で見たい場合
この記事の確認のうち一部は、拙作の読み取り専用CLI vps-check で一括表示できます。設定は変更せず、結果を PASS / INFO / WARN / HIGH / SKIP で表示します。
| この記事の項目 | vps-check のチェックID |
|---|---|
| 1. SSHの実効設定 | SSH-001(パスワード認証)、SSH-002(rootログイン) |
| 3. UFWの状態 | FW-001 |
| 4. 待ち受けの棚卸し | NET-001(待ち受けポート)、NET-002(全アドレスで待ち受けるDB) |
| 5. Docker公開ポート | DKR-001(コンテナ)、DKR-002(Composeファイル) |
| 7. 自動セキュリティ更新 | UPD-001 |
Match による特定ユーザーの例外、事業者側のファイアウォール、再起動待ち、時刻は今のバージョンでは確認しないので、この記事の手順で見てください。
更新情報やちょっとしたTipsは X(@supportapp3_lab)で発信しています。
タグ案: VPS, Ubuntu, SSH, ufw, セキュリティ