サーバーを立ち上げたばかりで、何もしないうちにトラブル発生
CentOSからUbuntuに移行したばかりの頃、インストールが完了したらすぐにコードをデプロイする習慣があった――設定手順など一切気にせずに。その結果、3日後にはSSHへのブルートフォース攻撃を受け、ログはエラーメッセージで埋め尽くされ、スワップを設定していなかったせいでNode.jsのプロセスが深夜2時にOOM killerに処理されるという事態になった。
CentOSを離れてからUbuntuのパッケージ管理システムに慣れるまで約1週間かかった。しかしそれより辛かったのは、バックアップからサービスを復元するのに午前中まるごと費やしたことだ――最初に20分だけ設定に時間を割いていれば、完全に避けられたミスのせいで。
この記事は、Ubuntu Server 22.04をインストールするたびに実行しているチェックリストだ。難しいことは何もない――すべてプロダクションのインシデントという授業料を払って身につけたことばかりだ。
新規インストールのサーバーはなぜトラブルが起きやすいのか?
Ubuntu Server 22.04のフレッシュインストールは問題なさそうに見えるが、実際にはそのままにしておくと危険な点がいくつかある:
- SSHがパスワードログインを許可している:ボットスキャナーは常にインターネット全体をスキャンし続けている。新しいパブリックIPを持つサーバーは数分以内にプローブされ、root/123456、admin/admin、ubuntu/ubuntuなど何千ものユーザー名/パスワードの組み合わせを試される。
- UFWはインストール済みだが有効になっていない:すべてのポートが外部からアクセス可能な状態。これがデフォルトだ。
- パッケージが数カ月前のものになっている可能性がある:ISOはリリース時点でビルドされており、すでにパッチが当たっている脆弱性が残っている場合がある。
- スワップがない:クラウドVPSはスワップパーティションを省略することが多い。RAMが満杯になるとOOM killerが容赦なく動き出す。
- デフォルトのタイムゾーンがUTC:ログには
03:00と記録されているのに、実際には日本時間の正午12時だったりする――デバッグがひどく面倒になる。
実施すべき手順 — 優先順位順
1. すぐにシステムを更新する
他のことをする前に。更新前にデプロイするのは不必要なリスクだ:
sudo apt update && sudo apt upgrade -y
sudo apt autoremove -y
カーネルが更新された場合は、すぐに再起動する:
sudo reboot
2. 専用ユーザーを作成し、rootを使わない
rootで直接ログインするのは悪い習慣だ――コマンドを一つ間違えればシステム全体が壊れ、何も止めてくれない。sudo権限を持つ新しいユーザーを作成する:
adduser deploy
usermod -aG sudo deploy
rootをロックする前に、新しいユーザーでログインして権限を確認すること。
3. SSHキーを設定してパスワードログインを無効にする
ブルートフォースボットはパスワードを試してくる――パスワードログインを無効にすれば、その攻撃ベクターをそのまま遮断できる。ローカルマシンで:
# SSHキーがなければ作成する
ssh-keygen -t ed25519 -C "[email protected]"
# 公開鍵をサーバーにコピーする
ssh-copy-id deploy@your-server-ip
キーでのログインが成功したら、/etc/ssh/sshd_configを編集する:
sudo nano /etc/ssh/sshd_config
以下の3行を変更する:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
SSHを再起動する:
sudo systemctl restart sshd
重要:新しいセッションでテストする際は、古いSSHセッションをそのまま維持しておくこと。ロックアウトされても古いセッションから修正できる。
4. UFWファイアウォールを有効にする
本当に必要なポートだけを開放し、残りはすべて閉じる:
# まずSSHを許可する — 必須。これをしないと自分でロックアウトされる
sudo ufw allow ssh
# Webサーバーを動かす場合
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# ファイアウォールを有効にする
sudo ufw enable
# 結果を確認する
sudo ufw status verbose
カスタムポート(例:2222)でSSHを動かしている場合は、有効にする前にこの行を追加する:
sudo ufw allow 2222/tcp
5. タイムゾーンを設定する
日本のユーザー向けにサーバーをデプロイするなら日本時間に設定する――ログが格段に読みやすくなる:
sudo timedatectl set-timezone Asia/Tokyo
# ベトナムのサーバーの場合はこちらを使用
sudo timedatectl set-timezone Asia/Ho_Chi_Minh
# 確認する
timedatectl status
6. スワップファイルを作成する
冒頭で述べた深夜2時のOOM問題?原因はスワップがなかったことだ。Node.jsのプロセスはRAMを少し多く使うだけでkillされてしまい、バッファがまったくなかった。1〜2GBのRAMを持つクラウドVPSなら、2GBのスワップを作成するのが適切だ:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 確認する
free -h
# 再起動時に自動でマウントする
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
デフォルトではLinuxはかなり早い段階でスワップを使用する(swappiness=60)。プロダクションサーバーでは10に設定する――本当に必要な時だけスワップを使用する:
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
7. 基本パッケージをインストールする
常にすぐ必要というわけではないが、デバッグが必要な時に揃っていないと非常に困る:
sudo apt install -y \
curl wget git vim htop \
net-tools dnsutils \
unzip build-essential \
fail2ban
8. Fail2banを設定する
パスワードログインを無効にしても、ボットは接続を試み続け、ログをスパムしてくる。Fail2banはN回の失敗後にIPアドレスを自動ブロックする:
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# ステータスを確認する — ブロックされたIPアドレスの数を表示
sudo fail2ban-client status sshd
9. セキュリティアップデートの自動適用を有効にする
毎週手動でサーバーを更新することを誰も覚えていない。自動化してしまおう:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
確認を求められたら「Yes」を選択する。セキュリティパッチのみが自動的に適用される――全パッケージではないため、アップデートでアプリケーションが壊れる心配はない。
10. 動作中のサービスを確認する
最後のステップ:不必要に外部に公開されているものがないか確認する:
# リッスンしているポートを確認する
sudo ss -tlnp
# 動作中のサービスを確認する
sudo systemctl list-units --type=service --state=running
不要なものを無効にする:
sudo systemctl disable --now snapd # snapを使わないなら無効にする
sudo systemctl disable --now bluetooth # サーバーにBluetoothは不要
次回のためにスクリプトにまとめる
4〜5回繰り返した後、スクリプトを書いた。コマンドを一つひとつ覚えなくても、一つのコマンドで最初の6ステップが完了する:
#!/bin/bash
# bootstrap-ubuntu.sh — Ubuntu Server 22.04インストール直後に実行するスクリプト
set -e
echo "[1/6] Updating system..."
apt update && apt upgrade -y && apt autoremove -y
echo "[2/6] Installing essential packages..."
apt install -y curl wget git vim htop net-tools dnsutils unzip fail2ban
echo "[3/6] Setting up swap..."
if [ ! -f /swapfile ]; then
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo 'vm.swappiness=10' >> /etc/sysctl.conf
fi
echo "[4/6] Configuring UFW..."
ufw allow ssh
ufw --force enable
echo "[5/6] Enabling Fail2ban..."
systemctl enable fail2ban
systemctl start fail2ban
echo "[6/6] Enabling auto security updates..."
apt install -y unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
echo "Done! Don't forget to:"
echo " - Create a non-root user"
echo " - Set up SSH key auth and disable password login"
echo " - Set correct timezone: timedatectl set-timezone Asia/Tokyo"
rootで実行する:
chmod +x bootstrap-ubuntu.sh
sudo ./bootstrap-ubuntu.sh
デプロイ前の最終チェックリスト
すべて完了したら、漏れがないことを確認するためにこれらのコマンドを素早く実行する:
# UFWは有効か?
sudo ufw status
# SSHのパスワードログインは無効か?
grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config
# スワップはあるか?
free -h
# タイムゾーンは正しく設定されているか?
date
# Fail2banは動作しているか?
sudo fail2ban-client status
合計で約15〜20分かかる。初回は各ステップの出力を読みながらなので時間がかかるかもしれないが、2回目からはスクリプトを実行するだけで完了する。一方、インシデント後にバックアップから復元するには約4時間かかり、問題が残っていないことを確認するのにさらに半日かかった。

