最低限のセキュリティ設定
これは脅しではありません。公開して数分後には、ログに攻撃の記録が残り始めます。まだ作成前なら、サーバー作成画面に設定を貼るだけで全部終わります。
VPSにグローバルIPが付いた瞬間から、そのアドレスは世界中のスキャナに補足されます。
これは比喩ではありません。設定後に /var/log/auth.log を確認すれば、公開から数時間で数百から数千件のログイン試行が記録されているのが分かります。
まだサーバーを作成していないなら、次の章だけで全部終わります。 すでに立ててしまった人は「1. 一般ユーザーを作る」から手動で進めてください。
作成前なら1ステップで済む — cloud-init
主要なVPS事業者は、サーバー作成画面に user-data(cloud-init の設定)を貼る欄を用意しています。ここに設定を書いておくと、初回ブート時に自動で実行されます。
手動設定との差は、作業時間ではなく危険な状態が一度も存在しないことです。手動でやる場合、サーバー作成からSSHで入って設定を終えるまでの数分から数十分のあいだ、パスワード認証が有効なまま公開され続けます。cloud-init なら、SSHが最初に待ち受けを始めた時点ですでに鍵認証のみ・ファイアウォール有効・fail2ban稼働の状態です。
貼り付ける内容(Debian / Ubuntu系)
ssh-ed25519 AAAA... の部分を自分の公開鍵に、myuser を好きなユーザー名に置き換えてください。
#cloud-config
users:
- name: myuser
groups: [sudo]
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-ed25519 AAAAC3Nza... your-key-here
disable_root: true
ssh_pwauth: false
package_update: true
package_upgrade: true
packages: [ufw, fail2ban, unattended-upgrades]
runcmd:
- ufw allow OpenSSH
- ufw default deny incoming
- ufw default allow outgoing
- ufw --force enable
- systemctl enable --now fail2ban
- printf 'APT::Periodic::Update-Package-Lists "1";\nAPT::Periodic::Unattended-Upgrade "1";\n' > /etc/apt/apt.conf.d/20auto-upgrades
これで、この記事の1〜5に書いてあることが全て済みます。対応関係は次のとおりです。
| cloud-init の記述 | 手動手順での相当箇所 |
|---|---|
users: ブロック | 1. 一般ユーザーを作る+鍵のコピー |
ssh_pwauth: false | 2. PasswordAuthentication no |
disable_root: true | 2. PermitRootLogin no |
runcmd: の ufw 4行 | 3. ファイアウォール |
20auto-upgrades の書き込み | 4. 自動セキュリティ更新 |
packages: の fail2ban | 5. fail2ban |
副次的な利点として、手動手順で起こりがちな事故を構造的に踏まなくなります。ufwは「OpenSSH許可 → enable」の順序が固定されるので順序ミスによる切断が起きません。パスワード認証の無効化も cloud-init が自前の設定ファイルで行うため、/etc/ssh/sshd_config.d/ の上書きに気づかず無効化できていなかった、という定番の失敗も回避できます。
各社でどこに貼るか
| 事業者 | 作成画面での場所 |
|---|---|
| Vultr | Additional Features → Cloud-Init User-Data |
| Linode | Add-ons → Metadata(User Data) |
| DigitalOcean | Advanced options → Add Initialization scripts |
| Hetzner | サーバー作成画面の Cloud config |
| Contabo | Cloud-Init(新コントロールパネル側) |
OS側にも条件があります。各社が配布する Debian / Ubuntu / Rocky などの標準イメージには cloud-init が入っていますが、自分で持ち込んだカスタムISOには入っていません。またLinodeのuser-dataは cloud-init 対応ディストリのみが対象です。
SolusVM や Virtualizor ベースの激安リセラー系VPSでは、そもそも user-data の入力欄がないことがあります。その場合は下の手動手順を使ってください。
ブート後に確認する
初回ブートはパッケージ更新を含むので、SSHで入れるまで1〜3分ほどかかります。入れたら結果を確認してください。
# cloud-init が完走したか(done なら成功)
cloud-init status --long
# パスワード認証が本当に無効か
sudo grep -rn PasswordAuthentication /etc/ssh/ | grep -v '#'
# ファイアウォールの状態
sudo ufw status verbose
# runcmd が途中でこけていないか
sudo tail -30 /var/log/cloud-init-output.log
cloud-init status が error の場合は、最後のログを見れば失敗したコマンドが分かります。SSHで入れている時点で鍵認証は成立しているので、慌てず個別に直せます。
ここまで確認できたら、この記事の「6. 攻撃されていることを実際に見る」へ進んでください。
ここから先は、すでにサーバーを立ててしまった人向け
以下を順番に実行してください。所要時間は10分程度です。
1. 一般ユーザーを作る
rootで日常作業をしないための準備です。
# ユーザーを作成(対話でパスワードを設定)
adduser myuser
# sudo 権限を与える
usermod -aG sudo myuser
Debian系以外では sudo グループが wheel のことがあります。
鍵を新しいユーザーにコピーする
rootで鍵認証を設定していた場合、そのままでは新ユーザーで入れません。
# root の鍵をコピーして所有者を変える
rsync --archive --chown=myuser:myuser ~/.ssh /home/myuser/
ここで一度、別のターミナルから新ユーザーでログインできることを確認してください。
ssh myuser@<IPアドレス>
2. SSHのパスワード認証を無効化する
これが最も効果の大きい対策です。 鍵認証だけにすれば、総当たり攻撃は原理的に成立しなくなります。
sudo nano /etc/ssh/sshd_config
次の3項目を設定します。
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
最近のディストリビューションでは /etc/ssh/sshd_config.d/ 配下の設定ファイルが優先されることがあります。そちらに PasswordAuthentication yes が残っていると無効化されないので確認してください。
# 上書きしている設定がないか確認
sudo grep -r "PasswordAuthentication" /etc/ssh/
# 設定に文法エラーがないか検証してから反映
sudo sshd -t && sudo systemctl restart ssh
sshd -t は文法チェックです。これを省くと、設定ミスでSSHが起動しなくなり締め出されます。 必ず実行してください。
3. ファイアウォールを設定する
必要なポート以外を全部閉じます。
sudo apt install -y ufw
# 先に SSH を許可する(これを忘れると締め出される)
sudo ufw allow OpenSSH
# 既定の方針: 入力は拒否、出力は許可
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 有効化
sudo ufw enable
sudo ufw status verbose
用途に応じてポートを開けます。
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 25565/tcp # Minecraft
事業者側のファイアウォールも使う
VultrやLinodeには、OSの外側にファイアウォール機能があります(Linodeは無料)。二重にしておくと、OS側の設定を壊しても守られます。 管理画面から設定できるので、あわせて有効にしておくことを推奨します。
4. 自動セキュリティ更新を有効にする
放置していると、既知の脆弱性が残り続けます。
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
有効になっているか確認します。
cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Unattended-Upgrade "1"; があれば有効です。
5. fail2ban を入れる(推奨)
鍵認証のみにしていれば必須ではありませんが、ログの汚染とリソースの無駄な消費を減らせます。
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
# 状態を確認
sudo fail2ban-client status sshd
既定の設定でSSHの保護が有効になります。何度も失敗したIPが一定時間ブロックされます。
6. 攻撃されていることを実際に見る
設定が済んだら、少し時間を置いてログを見てください。
# ログイン試行の失敗を数える
sudo grep "Failed password" /var/log/auth.log | wc -l
# 攻撃元のIPを多い順に
sudo grep "Failed password" /var/log/auth.log \
| awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head
# 試されたユーザー名
sudo grep "Invalid user" /var/log/auth.log \
| awk '{print $8}' | sort | uniq -c | sort -rn | head
root、admin、ubuntu、test、oracle といったユーザー名が延々と試されているのが見えるはずです。
パスワード認証を無効化していれば、これらは全て無意味な試行です。これがこの作業の意味です。
チェックリスト
よくある事故
| 事故 | 原因 | 予防策 |
|---|---|---|
| SSHから締め出された | 確認前に設定を反映した | 別ターミナルで接続確認してから閉じる |
| ufw有効化で切断された | SSH許可の前に有効化した | ufw allow OpenSSH を先に |
| sshdが起動しなくなった | 設定ファイルの文法エラー | sshd -t で検証してから再起動 |
| 秘密鍵を失った | 手元マシンの故障・初期化 | 鍵を安全な場所にバックアップ |
| パスワード認証が無効化されない | sshd_config.d/ の設定が優先 | grep -r で全設定を確認 |
いずれの場合も、事業者のWebコンソールから復旧できます。管理画面のどこにコンソール機能があるかを、事前に確認しておいてください。
よくある質問
本当に攻撃されるのですか
されます。インターネット上のIPアドレスは常時スキャンされており、SSHのポート22は最も狙われるポートの一つです。/var/log/auth.log を見れば、公開して数時間で数百〜数千件のログイン試行が記録されているのが確認できます。
SSHのポートを変えれば安全ですか
総当たり攻撃のログは減りますが、本質的な対策ではありません。ポートスキャンで見つけられるためです。ポート変更よりも、パスワード認証を無効化して鍵認証のみにするほうが遥かに効果的です。
rootで作業してはいけないのはなぜですか
操作ミスが即座に致命傷になるためです。またrootログインを許可していると、攻撃者は「root」というユーザー名が存在することを前提に総当たりできます。一般ユーザーを作り、必要なときだけsudoを使ってください。
fail2banは必須ですか
鍵認証のみにしていれば必須ではありません。ただしログの汚染を防ぎ、リソースの無駄な消費を減らす効果があります。導入コストが低いので入れておくことを推奨します。
cloud-initと手動設定はどちらが安全ですか
cloud-initです。手動設定では、サーバー作成から設定完了までの数分から数十分のあいだ、パスワード認証が有効なまま公開され続けます。cloud-initはSSHが最初に待ち受けを始める時点ですでに鍵認証のみ・ファイアウォール有効の状態になるため、危険な状態が一度も発生しません。
cloud-initはどのVPS事業者で使えますか
Vultr・Linode・DigitalOcean・Hetzner・Contabo はいずれもサーバー作成画面でuser-dataを渡せます。一方、SolusVMやVirtualizorベースの激安リセラー系ではuser-data欄が用意されていないことがあり、その場合は手動設定が必要です。