VPSは立てた瞬間から戦場である - SSH攻撃ログの現実と対策
VPSは立てた瞬間から戦場である - SSH攻撃ログの現実と対策
以前、VPSをマルウェアに感染させてしまった話を書いた。その後Xserver VPSで環境を再構築し、SSHをハードニングした直後、ふと気になった。
「自分のサーバーは、今どれくらい攻撃されているのか?」
調べてみたら、想像の10倍は殴られていた。この記録を残しておく。
ハードニングの内容
再構築にあたり、まず基本の5点を実施。
※ 作業中は既存セッションを閉じず、別ターミナルで新ユーザーのログインを確認してから次に進む。順番を間違えると自分が締め出される。
攻撃ログの集計
/var/log/auth.log を集計するだけで、攻撃の「気配」ではなく「実数」が見える。
試行されたユーザー名 TOP 20
sudo grep "Invalid user" /var/log/auth.log | \
awk '{print $8}' | sort | uniq -c | sort -rn | head -20
実際の出力(ユーザー名と回数):
| 試行ユーザー名 | 回数 | 考察 |
|---|---|---|
| admin | 172 | 定番中の定番 |
| user | 170 | 汎用辞書攻撃 |
| ubuntu | 145 | クラウドVPSのデフォルトユーザー狙い |
| user2 | 113 | |
| test | 43 | 開発環境の取り残し狙い |
| postgres | 33 | DB直撃 |
| ftpuser | 29 | |
| deploy | 26 | CI/CDアカウント狙い |
| steam | 25 | ゲームサーバー狙い |
| claude | 24 | ← 時代を感じる |
| administrator | 20 | Windows系混同狙い |
| bot | 18 | |
| sammy | 17 | DigitalOceanチュートリアル定番名 |
| frappe | 17 | ERPNext運用者狙い |
| vpn | 15 | |
| support | 15 | |
| ali | 15 | |
| odoo | 14 | Odoo運用者狙い |
| Administrator | 14 | |
| solana | 13 | クリプトノード運用者狙い |
読み取れること
1. 攻撃者は無差別ではなく「推測」している
solana steam odoo frappe jupyter grafana など、特定のソフトウェアを運用している人を狙う偵察が混ざっている。
「このVPSで何が動いているか」を当てに行って、当たれば即侵入試行、というパターン。
2. AIツールが新しい攻撃ターゲットになっている
claude が24回も試行されていたのは意外だった。
AIエージェントの普及に伴い、「AIツールを動かしているサーバー=狙い目」という認識が攻撃側に広がっているのかもしれない。
3. ヒヤリとした名前:Grafana
grafana もランクインしていた。
過去に所属していた組織では、シニアエンジニアが社内サーバーにGrafanaを入れて運用していた記憶がある。Grafanaはデフォルトポート 3000 が無認証で公開されているケースが多く、過去には深刻なCVE(パストラバーサルなど)も存在した。
運用している人は最低限、以下を確認したい。
admin/adminの初期パスワードは即変更- リバースプロキシ(Nginx等)経由にしてBasic認証やIP制限を追加
- バージョンを最新に保つ
「入れてる人が多い」「デフォルトのまま放置される傾向」の2点が揃ったOSSは、例外なく攻撃ターゲットになる。
ハードニング成功の「見えない証拠」
当初、こう期待していた。
sudo grep "Failed password" /var/log/auth.log
→ ほぼヒットしない。
一瞬「ログが壊れた?」と思ったが、そうではなかった。
ログをよく読むと、攻撃試行のほとんどがこう終わっている。
Disconnected from invalid user xxx [preauth]
この [preauth] が**「認証フェーズに到達する前に切断された」**ことを示している。
つまり PasswordAuthentication no が効いていて、攻撃者はパスワードを試すチャンスすら与えられていない。「Failed password」のログが発生しないのは、ハードニングが正常に機能している証拠だった。
攻撃者の流れ:
- SSH接続
- ユーザー名送信(
admin,ubuntu,claude...) - サーバー「そのユーザー存在しない or 鍵認証のみ」→ 即切断
- ログには
Invalid user ... [preauth]のみ残る
攻撃元IPランキング
sudo grep "Invalid user" /var/log/auth.log | \
awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
常連と思しきIPが数個、突出して出現していた。
国別で見ると予想通り偏りがあるが、個別IPの特定は避ける(攻撃元を晒す意味もない)。
次のアクション
fail2ban導入時の落とし穴
導入コマンドを打ったら、こんな画面が出た。
┌──── Pending kernel upgrade ────┐
│ Newer kernel available
│ The currently running kernel version is 5.15.0-126-generic
│ which is not the expected kernel version 5.15.0-176-generic.
│ Restarting the system to load the new kernel will not be
│ handled automatically, so you should consider rebooting.
一瞬「fail2ban入れたら壊れた?」と思ったが、これは無関係の通知。
過去の apt upgrade で新カーネルが入っていたが未反映だっただけで、fail2banのインストール処理は正常に継続する。
ただしこの通知が出たということは、いずれ再起動は必要ということ。
Docker(Dify, n8n, Ollama)を動かしている場合は、restart policyが always または unless-stopped になっていることを確認してから、業務影響の少ない時間帯に再起動する。
# Dockerコンテナのrestart policy確認
sudo docker inspect --format='{{.Name}}: {{.HostConfig.RestartPolicy.Name}}' \
$(sudo docker ps -q)
まとめ
- インターネットに公開した瞬間から、サーバーは攻撃されている。これは知識ではなく事実として、自分のログで確認すべき。
PasswordAuthentication noが効いていると、ログにFailed passwordは残らない。ログの「不在」が成功の証。- 攻撃者は特定ソフトウェアの運用者を狙う偵察もしている。
grafanaodoosolanaなどが狙われるのは、それらを運用する人が多く、かつデフォルト放置されやすいから。 - AI時代の新しい攻撃対象として、
claudeのようなユーザー名も辞書に追加されつつある。 - セキュリティは一度やって終わりではなく、継続的な観測と対応のプロセス。
「攻撃されている」という現実を数字で見るのは、セキュリティ意識を一段引き上げる最良の体験だった。
関連記事
- VPS-セキュリティ復旧ノウハウ-公開用 - マルウェア感染から復旧した話
- Hammerspoon導入とIceでメニューバー問題を解決した話
fail2ban導入後のもう一つのダイアログ:needrestart
fail2banのインストール完了後、さらにこんなダイアログが出た。
┌── Daemons using outdated libraries ──┐
│ Which services should be restarted?
│ [*] containerd.service
│ [*] cron.service
│ [ ] dbus.service
│ [ ] docker.service
│ [*] systemd-journald.service
│ ...
これは needrestart というUbuntu 22.04以降の標準ツールによるもの。
なぜ出るか
apt で共有ライブラリ(libc, libssl等)がアップデートされたとき、既に起動中のプロセスは古いライブラリをメモリに保持したままになる。新しいライブラリを使うには、対象プロセスを再起動する必要がある、と教えてくれている。
何を再起動すべきか
デフォルト選択のまま進めるのが正解。
Ubuntuが気を利かせて、安全に再起動できないもの(docker, dbus, logind等)は最初からチェックを外している。つまりDify/n8n/Ollamaが動いているDockerは触られない設計。
| サービス | チェック | 理由 |
|---|---|---|
| docker.service | OFF | 運用中のコンテナに影響するため |
| dbus.service | OFF | システム基盤、フル再起動相当が必要 |
| systemd-logind | OFF | ログインセッション管理 |
| cron, journald, udevd等 | ON | 再起動しても影響が少ない |
needrestartの賢さ
needrestart は /proc/[pid]/maps を調べて「古いライブラリをロードしているプロセス」を自動検出している。
昔のLinuxサーバーは「upgradeしたら何も通知なく動作不良」なんてこともあったので、こういう丁寧な確認ダイアログが標準装備されていること自体が、Ubuntuがサーバー運用を考慮した設計になっている証拠。
地味だが、サーバー運用者にとっては非常にありがたい機能。Enterで進めて問題なし。
fail2ban の設定:実戦投入編
fail2ban のパッケージインストールが終わったら、次は設定。
ここでハマりがちなポイントも含めて、実際にやったことをまとめる。
設定ファイルの鉄則:jail.conf は触らない
fail2banは設定ファイルが二層構造になっている。
1. /etc/fail2ban/jail.conf ← パッケージ提供(触らない)
2. /etc/fail2ban/jail.d/*.conf ← 個別設定ファイル
3. /etc/fail2ban/jail.local ← 自分のカスタム設定(←ここに書く)
4. /etc/fail2ban/jail.d/*.local ← より細かい自分のカスタム
後から読まれたものが上書きする仕組み。jail.conf を直接編集するとパッケージアップデート時に消えるので、必ず jail.local に書く。
jail.local は初期状態では存在しない。新規作成が正解。
sudo vi /etc/fail2ban/jail.local
実際に採用した設定
[DEFAULT]
bantime = 600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
backend = systemd
[sshd]
enabled = true
maxretry = 3
設定の意図
| 項目 | 値 | 意図 |
|---|---|---|
bantime |
600 (10分) | 自分が誤BANされても10分待てば戻れる |
findtime |
600 | 直近10分間の失敗を観測 |
maxretry (DEFAULT) |
5 | 将来他のjailを有効にしたときの保険値 |
maxretry (sshd) |
3 | SSHは攻撃が多いので厳しめ |
ignoreip |
localhost only | 動的IP環境なので自IPは入れない |
backend |
systemd | Ubuntu 22.04以降の推奨方式 |
動的IP環境でのホワイトリスト判断
自宅回線が動的IP(固定IPではない)の場合、
ignoreip に自分のグローバルIPを入れると、IPが変わった瞬間に自分が弾かれるリスクがある。
そのため今回の運用方針は:
ignoreipには自分を入れない(localhost のみ)- その代わり
bantimeを短めの600秒(10分)にする - 誤BANされても10分待つか、XserverのVPSコンソールから
fail2ban-client set sshd unbanip <IP>で即解除
この**「待てば戻れる」設計**が、固定IPを持たない環境では現実的。
起動手順
# 文法チェック
sudo fail2ban-client -t
# 起動+自動起動有効化
sudo systemctl enable --now fail2ban
# 状態確認
sudo systemctl status fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd
systemctl status で Active: active (running) になっていれば成功。
締め出された時の復活コマンド(お守り)
Xserver VPSパネルの「コンソール」ボタンからログインすれば、
SSHを経由しない物理コンソール相当のアクセスが可能。
この経路なら、仮に自分がBANされていても入れる。
# BAN中のIP一覧確認
sudo fail2ban-client status sshd
# 自分のIPをBAN解除
sudo fail2ban-client set sshd unbanip <自分のIP>
# 緊急時:fail2ban一時停止
sudo systemctl stop fail2ban
この「最終ログイン経路が常にある」というのは、クラウドVPSの大きな利点。
自前サーバーだと物理アクセスが必要になるので、復旧の難易度が桁違いに上がる。
Enterキー誤爆対策としての3回制限
SSH接続時、うっかり間違ったキーでログインしようとしてEnter連打、
というミスは誰にでもある(パスワード認証を無効化している今となっては起こりにくいが)。
鍵認証のみの環境なら、maxretry = 3 でも十分。
攻撃者からすれば3回試せれば辞書の上位候補は全部投げられるので、
「ユーザー配慮で5回」みたいなパスワード時代の緩さはもう不要。
観測は30分後からが楽しい
起動直後は Total banned: 0 から始まるが、
30分〜1時間も経てば、攻撃者が自動的にBANされていく様子が見える。
sudo fail2ban-client status sshd
で:
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: XX
| `- Journal matches: _SYSTEMD_UNIT=ssh.service
`- Actions
|- Currently banned: X
|- Total banned: XX
`- Banned IP list: xxx.xxx.xxx.xxx ...
「Total banned」の数字が増えていくのが、謎の達成感を生む。
攻撃を受ける側から、自動で撃退する側に回った瞬間でもある。
リアルタイム監視
動作を眺めたい時は:
sudo tail -f /var/log/fail2ban.log
こんなログが流れてくる:
fail2ban.filter: INFO [sshd] Found xxx.xxx.xxx.xxx
fail2ban.actions: NOTICE [sshd] Ban xxx.xxx.xxx.xxx
Found は「怪しい試行を検知」、Ban は「実際にBAN実行」。
Ctrl+C で抜けられる。
ここまでで達成したこと
インターネットに開いた瞬間から戦場というこのサーバーは、
ようやく「自分の目と手で守られている」状態になった。
次はSSHポート変更とufwで、攻撃ノイズそのものを減らしていくフェーズに進む予定。