はじめに

平素は大変お世話になっております。
クイックガードのパー子です。

昨年の 12月9日頃、弊社で管理している複数のサーバにおいて、突如 SSH によるログインができなくなる事象が発生しました。

サーバ自体は応答しているものの、SSH接続を試みても接続が即座に閉じられてしまい、ログインできません。
マネジメント・コンソールから直接繋いで調査したところ、リアルタイム監視ツールである Netdata のバグが発端であることが判明しました。

本記事ではその顛末をご紹介します。

ログ

問題となったサーバの OS はいずれも Rocky Linux 9.4 (Blue Onyx) でした。

複数台で同時に SSHログインできなくなったため、サーバ単体の偶発的な過負荷などではないことは明白です。
ネットワークや基盤レベルの障害か、または、特定の属性を持つ同質なサーバ群に仕込まれた時限爆弾の両面を疑いつつ、とりあえずマネジメント・コンソール経由で対象サーバに直接ログインして各種ログを確認しました。

まず、/var/log/messages を確認したところ、03:17 以降に open /dev/null: no such file or directory というエラーが多数記録されていました。

Dec  9 00:00:03 ***masked-hostname*** systemd[1]: Starting Rotate log files...
Dec  9 00:00:03 ***masked-hostname*** systemd[1]: Started Update a database for mlocate.
Dec  9 00:00:03 ***masked-hostname*** systemd[1]: logrotate.service: Deactivated successfully.
Dec  9 00:00:03 ***masked-hostname*** systemd[1]: Finished Rotate log files.
Dec  9 00:00:03 ***masked-hostname*** systemd[1]: mlocate-updatedb.service: Deactivated successfully.
Dec  9 03:17:18 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:18.004726184+09:00" level=warning msg="Failed to retrieve default runtime version" error="failed to retrieve runc version: open /dev/null: no such file or directory"
Dec  9 03:17:18 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:18.005732027+09:00" level=warning msg="Failed to retrieve /usr/libexec/docker/docker-init version" error="open /dev/null: no such file or directory"
Dec  9 03:17:19 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:19.001069171+09:00" level=warning msg="Failed to retrieve default runtime version" error="failed to retrieve runc version: open /dev/null: no such file or directory"
Dec  9 03:17:19 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:19.001588657+09:00" level=warning msg="Failed to retrieve /usr/libexec/docker/docker-init version" error="open /dev/null: no such file or directory"
Dec  9 03:17:20 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:20.001472706+09:00" level=warning msg="Failed to retrieve default runtime version" error="failed to retrieve runc version: open /dev/null: no such file or directory"
Dec  9 03:17:20 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:20.001942234+09:00" level=warning msg="Failed to retrieve /usr/libexec/docker/docker-init version" error="open /dev/null: no such file or directory"
Dec  9 03:17:21 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:21.001004605+09:00" level=warning msg="Failed to retrieve default runtime version" error="failed to retrieve runc version: open /dev/null: no such file or directory"
Dec  9 03:17:21 ***masked-hostname*** dockerd[16979]: time="2025-12-09T03:17:21.001474668+09:00" level=warning msg="Failed to retrieve /usr/libexec/docker/docker-init version" error="open /dev/null: no such file or directory"

さらに /var/log/cron を見ると、同時刻にちょうど “netdata-updater” が実行されていました。

Dec  9 03:17:01 ***masked-hostname*** anacron[221735]: Job `cron.daily' started
Dec  9 03:17:01 ***masked-hostname*** run-parts[256801]: (/etc/cron.daily) starting netdata-updater
Dec  9 03:17:17 ***masked-hostname*** run-parts[257460]: (/etc/cron.daily) finished netdata-updater
Dec  9 03:17:17 ***masked-hostname*** anacron[221735]: Job `cron.daily' terminated (produced output)
Dec  9 03:17:17 ***masked-hostname*** anacron[257461]: Can't open /dev/null on file-descriptor 1: そのようなファイルやディレクトリはありません
Dec  9 03:17:17 ***masked-hostname*** anacron[221735]: Tried to mail output of job `cron.daily', but mailer process (/usr/sbin/sendmail) exited with status 1
Dec  9 03:17:17 ***masked-hostname*** anacron[221735]: Normal exit (1 job run)

netdata-updater は Netdata の自動アップデータで、systemd.timer や cron によって日次で実行されます。
我々は Netdata を rpm でインストールしており、アップデータは新バージョンの rpm がリリースされていれば dnfコマンドでバージョンアップを試みる、という動作をします。

そして、このアップデータも rpm に含まれるファイルの 1つなのです。
日々の自動バージョンアップによってアップデータ自体が更新され、いつの間にかバージョンアップ処理のロジックがガラッと変化してしまう可能性もあるわけです。

以上の事実から、

  1. 前日の自動バージョンアップにより、Netdata のアップデータに何らかのバグが混入した。
  2. 翌日の定期実行時 (= 2025-12-09 03:17) にバグ入りアップデータが猛り狂って /dev/null を削除した。
  3. その結果、sshd を含む、/dev/null に依存するプロセスが正常に動作しなくなった。

という事態に陥ったものと推察されました。

Issue調査

Netdata の GitHubリポジトリを覗いてみたところ、今回の問題と酷似した Issue (netdata/netdata#21427) が起票されていました。

今回の事象の発生日と Issue の起票日時が一致しています。
Issue記載のディストロは AlmaLinux 9.7 である一方で弊社の環境は Rocky Linux 9.4 という差異はありますが、どちらも RHEL の系譜なので、原因はこの Issue で間違いなさそうです。

Issue によると、バグは netdata/netdata@2246955 で混入したようです。

報告を受けたメンテナの対応は迅速でした。
当該Issue の起票から 2〜3時間のうちに netdata/netdata#21428 で修正されました。

コードを読み解く

実際のコードを確認してみましょう。

問題となったコードは、netdata/netdata@2246955 の 1170行目 です。

    _safe_download "${check_url}" /dev/null

_safe_download() は指定の URL からファイルをダウンロードする関数で、第2引数でその保存先を指定します。

上記の行では保存先を /dev/null としています。
ここでは単に URL の存在チェックがやりたいだけであって、その中身は不要なので保存せずに捨てて構わないという意図なのでしょう。

続いて _safe_download() の実装を見てみます。

https://github.com/netdata/netdata/blob/22469558bd735a9821ca39d632bf0b22c9bed063/packaging/installer/netdata-updater.sh#L559-L601

_safe_download() {
  url="${1}"
  dest="${2}"
  succeeded=0
  checked=0

  ...(snip)...

  if [ -n "${curl}" ]; then
    checked=1

    if "${curl}" -fsSL --connect-timeout 10 --retry 3 "${url}" > "${dest}"; then
      succeeded=1
    else
      rm -f "${dest}"
    fi
  fi

  if [ "${succeeded}" -eq 0 ]; then
    if command -v wget > /dev/null 2>&1; then
      checked=1

      if wget -T 15 -O - "${url}" > "${dest}"; then
        succeeded=1
      else
        rm -f "${dest}"
      fi
    fi
  fi

  ...(snip)...
}

この関数は、まず curl でダウンロードを試行し、失敗した場合は wget で再試行する構造になっています。

問題は、どちらのコマンドが失敗した場合にも、後処理として rm -f "${dest}" が実行される点です。
${dest} は第2引数、つまり今回のケースでは /dev/null になります。
そのため、ダウンロードに失敗すると /dev/null が削除されてしまうのです。
(第1引数の ${url} には https://repository.netdata.cloud/repos/edge/centos/9.7/x86_64/.currently.published のような URL が渡されますが、これが 404 となるようです)

直接的には “ファイルのダウンロード” という _safe_download() の本来の用途から外れた流用が招いた事故だと言えそうです。
ただし、その背後には、重厚長大なシェルスクリプトのメンテナンスのしづらさやテスト/検証プロセスの不足など、解決困難な根本的課題が見え隠れしていそうですが。

なお、Netdata のリリース・チャネルには stablenightly がありますが、今回の事象は nightly でのみ発生します。
公式のインストール・スクリプトである kickstart.sh を使うと、デフォルトでは nightlyチャネルからパッケージがインストールされます。
弊社の環境では Netdata の安定性はさほど重要ではなく、デフォルトの nightly のままとしていたため、本件の影響を受けることとなりました。

修正

この問題は netdata/netdata#21428 で修正済みです。
${dest}/dev/null の場合は削除しないように変更されました。

さらに netdata/netdata#21432 では、/dev/null が適切に存在できていない状態となってしまった場合に、これを自動で修復する後処理も追加されています。

再現してみる

原理を理解したところで、事象を再現してみます。

コンテナ・イメージは Locky Linux 9系の最新版 rockylinux/rockylinux:9.7.20251123 を使用します。
SSH でログインできなくなったことを再現するため、SSH用のポートを 10022:22 でマッピングしておきます。

$ docker run -d -p 10022:22 rockylinux/rockylinux:9.7.20251123 sh -c 'while :; do sleep 3600; done'

コンテナが起動したら、docker exec でシェルを立ち上げます。

事象再現の前に、まずは SSH で正常にログインできることを確かめます。
openssh-serverパッケージをインストールして、ホスト鍵を手動で生成します。
(Systemd経由なら sshd の起動時に自動で鍵が生成されますが、当該コンテナには Systemd が入っていないので手動で鍵を生成する必要があるのです)

bash-5.1# dnf install openssh-server

bash-5.1# /usr/libexec/openssh/sshd-keygen 'rsa'
bash-5.1# /usr/libexec/openssh/sshd-keygen 'ecdsa'
bash-5.1# /usr/libexec/openssh/sshd-keygen 'ed25519'

sshd を起動します。
-e でログ出力先を stderr に向けつつ、-D でフォアグラウンドに残るようにします。
(なお、コマンドは /usr/sbin/sshd のようにフルパスで指定しないと怒られます)

bash-5.1# /usr/sbin/sshd -eD
Server listening on 0.0.0.0 port 22.
Server listening on :: port 22.

sshd が起動したら接続テストです。
ホスト側から繋いでみます。

$ ssh -p 10022 root@localhost
The authenticity of host '[localhost]:10022 ([::1]:10022)' can't be established.
ED25519 key fingerprint is SHA256:0aEM/zdgG1g5TAM8cHbXe5IUqWCCwepRg3YQJ82me6Y.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:10022' (ED25519) to the list of known hosts.
root@localhost's password: [Ctrl+C]

パスワード入力をキャンセルして接続を切ると、sshd側では以下のログが出力されます。

bash-5.1# /usr/sbin/sshd -eD
...
Connection closed by authenticating user root ::1 port 39926 [preauth]

以上でひとまず (認証周りは置いておいて、基盤としては) SSH が正常に機能する環境は整いました。

続いて Netdata をインストールします。

Netdata の自動アップデータが有効化されるためには systemd.timer または cron が必要なので、ここではお手軽に cron をインストールしておきます。
(sshd を起動しているターミナルはそのままにして、別窓を開いて作業を進めます)

bash-5.1# dnf install cronie

前述のとおり Netdata にはインストール・スクリプトが用意されているので、それを使います。
本来であれば問題のあるバージョンを指定してインストールできればよいのですが、入手できるのは 直近2バージョンまで とのことなので、諦めて最新版をインストールすることにします。

bash-5.1# curl https://get.netdata.cloud/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh

補足

netdata/netdata-nightliesリポジトリ で該当するバージョン (v2.8.0-93-nightly) が公開されていますが、このパッケージからインストールすると rpm ではなく直接的にファイルが配置されます。

rpm でインストールした環境でないと自動アップデータの問題箇所を通過しないため、残念ながら今回の検証にはこのリポジトリのパッケージを使用できません。

適宜プロンプトに応答しつつインストールを完了すると、以下のように自動アップデータが cron に登録されていることを確認できます。

bash-5.1# ls -lh /etc/cron.daily/
total 0
lrwxrwxrwx 1 root root 39 Dec 17 01:28 netdata-updater -> /usr/libexec/netdata/netdata-updater.sh

現時点では不具合はすでに修正されているため、修正前のバージョンの自動アップデータを取得します。

bash-5.1# curl -L -O https://raw.githubusercontent.com/netdata/netdata/22469558bd735a9821ca39d632bf0b22c9bed063/packaging/installer/netdata-updater.sh

bash-5.1# chmod a+x ./netdata-updater.sh

先のセクションで見たように、curlwget にフォールバックするため、wget をインストールします。

bash-5.1# dnf install wget

以上で準備ができました。
本来は cron経由で実行されるところですが、手動で自動アップデータを実行してみます。

bash-5.1# ./netdata-updater.sh
Wed Dec 17 01:46:27 UTC 2025 : INFO: netdata-updater.sh:  Checking if a newer version of the updater script is available.
Netdata                                                                                                                                                     995  B/s | 833  B     00:00
Netdata Repository Config                                                                                                                                   1.5 kB/s | 833  B     00:00
Extra Packages for Enterprise Linux 9 - aarch64                                                                                                             8.4 kB/s |  11 kB     00:01
Extra Packages for Enterprise Linux 9 - aarch64                                                                                                              10 MB/s |  20 MB     00:01
Extra Packages for Enterprise Linux 9 openh264 (From Cisco) - aarch64                                                                                       2.2 kB/s | 994  B     00:00
Rocky Linux 9 - BaseOS                                                                                                                                      8.5 kB/s | 4.3 kB     00:00
Rocky Linux 9 - AppStream                                                                                                                                   5.3 kB/s | 4.8 kB     00:00
Rocky Linux 9 - Extras                                                                                                                                      5.9 kB/s | 3.1 kB     00:00
Metadata cache created.
Wed Dec 17 01:46:34 UTC 2025 : INFO: netdata-updater.sh:  Checking if native packages are still being published for this platform.
curl: (22) The requested URL returned error: 404
--2025-12-17 01:46:35--  https://repository.netdata.cloud/repos/edge/centos/9.7/aarch64/.currently.published
Resolving repository.netdata.cloud (repository.netdata.cloud)... 172.66.170.216, 104.20.22.2, 2606:4700:10::ac42:aad8, ...
Connecting to repository.netdata.cloud (repository.netdata.cloud)|172.66.170.216|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-12-17 01:46:35 ERROR 404: Not Found.

Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:  NETDATA CANNOT BE UPDATED ON THIS SYSTEM!
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:  Native packages are no longer being published for this platform.
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:  To update to the latest version of Netdata, you will need to switch to a different install type.
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:  For details on how to do so, see https://learn.netdata.cloud/docs/netdata-agent/installation/linux/switch-install-types-and-release-channels
Wed Dec 17 01:46:35 UTC 2025 : ERROR: netdata-updater.sh:
Wed Dec 17 01:46:35 UTC 2025 : FATAL: netdata-updater.sh: FAILED TO UPDATE NETDATA:  Unable to update due to native packages no longer being published for this platform

報告された Issue と同じく、curl が 404 となりました。

そして、

bash-5.1# ls -lh /dev/null
ls: cannot access '/dev/null': No such file or directory

確かに /dev/null が消えていることを確認できました。

再度ホスト側から SSH で繋いでみても、接続を張れなくなっています。

$ ssh -p 10022 root@localhost
Connection closed by ::1 port 10022

sshd のログには /dev/null が存在しない旨が表示されています。

bash-5.1# /usr/sbin/sshd -eD
...
Couldn't open /dev/null: No such file or directory

再現できました。

まとめ

弊社管理の複数のサーバに突然 SSH接続できなくなった事象について、その顛末をまとめました。

調査の結果、リアルタイム監視ツール Netdata の自動アップデータが原因であることが判明しました。
日次でスケジュール実行されたアップデータが /dev/null を削除してしまったのです。
/dev/null の消失により、sshd を含む多くのプロセスが正常に動作しなくなりました。

GitHub上での Issue報告から 2〜3時間のうちに迅速に修正されたものの、長大なシェルスクリプトの保守性について課題が浮き彫りになる事例であったように思います。
弊社も業務の性質上、自作のシェルスクリプトを多用しつつ幅広いシステム環境に関与するため、他人事ではない一件でした。

今後ともよろしくお願い申し上げます。