🍆

dnf(yum) updateをサボったせいでサーバに接続できなくなった

2024/12/03に公開

Microsoft Azure上のLinuxサーバをいじっていたところ、とつぜんSSH接続ができなくなってしまいました。
色々と原因を調査したところ、サーバ構築時にdnf updateをサボったことが原因だと分かったので、調査内容や対処方法をまとめています。

SSH接続ができなくなるまで

サーバはAzure上で構築したVM、OSイメージはRockyLinux9を利用。
コマンドとしては以下を実行するだけで、SSH接続ができなくなります。
(環境によっては再現しない可能性があります)

sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -y install postgresql15 postgresql15-contrib

上記コマンドを実行した後でSSHセッションを閉じると新規でSSH接続ができなくなります。
今回はAzureだったので何とかなりましたが、SSH以外にサーバへの接続手段がないと詰みます。
ちなみにこの状況でVMにSSH接続しようとすると、以下のエラーが発生します。

$ ssh -i ssh-key.pem pesi@xxx.xxx.xxx.xxx
ssh: connect to host xxx.xxx.xxx.xxx port 22: Connection refused

トラブルシューティング

ただの調査内容なので対応策が早く見たい方は読み飛ばしてください。
調査結果と応急対応

発生時の状況としては、上記のPostgreSQLに加えて様々なミドルウェアのインストール作業を行っていました。
一通りインストールが完了した後で再起動を行い、改めてSSH接続をしようとしたところ接続ができなくなりました。

ネットワーク調査

Azure VMのコンソールからトラブルシューティングツールで確認したところ、セキュリティグループなどのAzureのネットワーク設定には問題がありませんでした。
ということはOS側で何かうまくいっていない可能性があります。

念のためnmapを実行したところ、一応22番ポートには疎通できているもよう。
ただしcloseとなっているのでサーバ側ではリッスンされていない。

$ nmap -sT xxx.xxx.xxx.xxx
Starting Nmap 7.80 ( https://nmap.org ) at 2024-12-02 23:17 JST
Nmap scan report for xxx.xxx.xxx.xxx
Host is up (0.0069s latency).
Not shown: 998 filtered ports
PORT   STATE  SERVICE
22/tcp closed ssh
80/tcp closed http

OS側調査

幸いなことに、Azure VMではシリアルコンソール機能があるので、SSH接続ができなくてもサーバに接続が可能です。
また、シリアルコンソール接続を行うにあたってパスワード認証が必要となりますが、これもコンソールから設定が行えるようになっています。

というわけで無事にシリアル接続ができたら、まずはsshdの状態を確認します。
どうやらOpenSSHの起動に失敗しているみたいです。

$ systemctl status sshd
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: ena>
     Active: activating (auto-restart) (Result: exit-code) since Mon 2024-12-02>
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 1980 ExecStart=/usr/sbin/sshd -D $OPTIONS (code=exited, status=255>
   Main PID: 1980 (code=exited, status=255/EXCEPTION)
        CPU: 7ms
Dec 02 14:27:58 pesi-rocky systemd[1]: sshd.service: Main process exited, code=>
Dec 02 14:27:58 pesi-rocky systemd[1]: sshd.service: Failed with result 'exit-c>
Dec 02 14:27:58 pesi-rocky systemd[1]: Failed to start OpenSSH server daemon.

systemctl startコマンドを実行しても当然上がってきません。
journalctlコマンドなどでログを見てみてもイマイチ原因は分からず。。。

次にsshコマンドを実行してみたところ、次のようになりました。

$ ssh -V
OpenSSL version mismatch. Built against 30000070, you have 30200020
$ openssl version
OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)

どうやらOpenSSLのバージョンに問題があるようです。
OpenSSLのバージョンを確認すると3.2.2と表示されましたが、「you have 30200020」というのが3.2.2のことを指しているものと思われます。
本来は「30000070」、つまり3.0.7のバージョンがインストールされているべき状況のようです。
特にOpneSSLのバージョンをあげた覚えはないのですが、rpmコマンドで確認してみます。

$ rpm -qa --last | grep openssl
openssl-3.2.2-6.el9_5.x86_64       Mon Dec  2 13:21:59 2024
openssl-libs-3.2.2-6.el9_5.x86_64  Mon Dec  2 13:21:52 2024

がっつりバージョンアップされています。
grep opensslを外して、どのパッケージインストールのさいにOpenSSLがアップグレードされてしまったかを確認したところ、PostgreSQLと一緒に入ってきていたことが分かりました。

調査結果と応急対応

というわけで、PostgreSQLをインストールした時にOpenSSLが巻き添えでバージョンアップされてしまい、OpenSSHからOpenSSLを参照できなくなったためにsshdが起動できなくなったというのが原因のようです。
まずは応急処置として、OpenSSHを再インストールしていきたいと思います。

sudo yum remove openssh-server openssh-clients
sudo yum install openssh-server openssh-clients
sudo systemctl daemon-reload
sudo systemctl start sshd

上記コマンドで再びSSH接続ができるようになりました。とりあえずは一安心です。
本来はOpenSSLをダウングレードしたかったのですが、dnfからうまくダウングレードすることができなかったためOpenSSHの再インストールで対応しています。
もしかしたらssh以外のところでも同様のエラーが出る可能性がありますが、その際は同様に再インストールで対応すればいいんじゃないかと思います。

根本的な回避策

今回SSH接続ができなくなった要因としては先ほど説明した通りなのですが、今回のトラブルについては最初にdnf updateを行っていれば回避できるものでした。
ミドルウェアインストール時に参考にしていた手順にも、最初にupdateを行うよう記載があったのですが、特に気にせず作業を行っていました。

試しに、改めてVMを作成して最初のPostgreSQLインストールコマンドの前にdnf updateを行ってみます。
ちなみにサーバ構築時点でのopensslバージョンは3.0.7です。

$ openssl version
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
$ sudo dnf -y update
$ sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
$ sudo dnf -y install postgresql15 postgresql15-contrib
$ openssl version
OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)

dnf updateを挟んだおかげでコマンド実行後も問題なく新規SSHセッションを張ることができました。
次に、また別で新規VMを作って、OpenSSLだけをアップグレードした場合を確認します。

$ openssl version
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
$ sudo dnf update openssl
$ ssh -V
OpenSSL version mismatch. Built against 30000070, you have 30200020

OpenSSLのバージョンだけ上がっているためにエラーとなります。
上記よりPostgreSQLが悪いというよりは、OpenSSLをアップグレードした際に依存関係のあるOpenSSHがそれに追従できていないことが問題なようです。

ということで、dnfを利用してパッケージをインストール・更新する際には、他のミドルウェアとの依存関係にも注意する必要があります。
このようなトラブルを避けるためにも、作業を行う際にはとりあえずdnf updateで一括更新しておいた方が賢明かと思います。

終わりに

PostgreSQLをインストールすることで巡り巡ってSSH接続できなくなるというのは盲点でした。
AWS EC2では構築時にcloud-initで勝手にdnf updateがかかるようになっているのもあり、手動でアップデートする習慣がなかったため、今後はサーバを建てたらまずアップデートすることにします。

dnf updateを都度行うデメリットとしては、単に時間がかかるという他に、
➀アップデートによって予期しないアプリのエラーなどが発生する可能性
➁細かいバージョンの管理ができない
があげられると思います。

一番目に関しては、検証環境を用意して事前検証できるようにすれば良いですし、そもそも気軽にセキュリティアップデートもかけられないような環境を本番運用することに問題があるかと思います。
二番目に関しては、dnfなどのパッケージマネージャを利用する以上、細かいバージョン管理はあきらめた方がよいかと思います。どうしても細かいバージョン管理が必要であれば、ソースからビルドする方が確実です。

あくまで理想論ではありますが、サーバを構築・運用していくうえでパッケージマネージャによるアップデートがいつでもできるよう環境を整備していきたいと今まで以上に感じました。

Discussion