systemctl disable sshd.service したら sshd を起動できなくなった話
Linux の勉強を体系的にしようと思って, LPIC の勉強をしてます。
systemctl の勉強中, 実際にいくつかコマンドを実行して結果を見ようと思い, systemctl disable sshd.service を実行したら, sshd.service の Unit ファイルが消えて操作できなくなりました。
Linux 勉強中 sudo systemctl disable sshd.service を実行
— kangetsu121 (@kangetsu_121) April 29, 2020
シンボリックリンクが消えると期待したら実ファイルが消え enable もできなくなった
別 VM で調査したら sshd.service は ssh.service と実質同じと分かったので Unit ファイルコピーで復旧
変に時間かけた…disable は迂闊に打てないもの?
※このツイート時点では検証できてないので, ツイート中には不正確な推測が入ってます
正しく復旧するには systemd Unit ファイルの知識が必要で, 原因がちょっとコーナーケースじみていておもしろかったので記録しておきます。
なお, 動作環境は VirtualBox 上の Ubuntu 18.04.4 LTS です。
kangetsu@ubuntu18:~$ uname -a
Linux ubuntu18 4.15.0-99-generic #100-Ubuntu SMP Wed Apr 22 20:32:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
kangetsu@ubuntu18:~$
systemctl disable と enable の機能
そもそも systemctl disable と systemctl enable は何かと言うと, システム起動時のサービスの自動起動設定の有効・無効化 を行うコマンドです。
今回検証の白羽の矢が立った sshd.service は, enable されていないと困る筆頭です。
もし sshd.service の自動起動が無効化されていたら, システムメンテナンスでパッケージアップグレードなどして再起動した後, ssh 以外の手段でログインした後に手動で sshd.service の起動をしないと ssh できなくなってしまいます。
なお, 自動起動の有効状態は, systemctl status や systemctl is-enabled で確認できます。
systemctl disable で何が起きたか
disable コマンド実行時のログを引用します。
kangetsu@ubuntu18:~$ sudo systemctl disable sshd.service
[sudo] password for kangetsu:
Removed /etc/systemd/system/sshd.service.
Removed /etc/systemd/system/multi-user.target.wants/ssh.service.
kangetsu@ubuntu18:~$
原因が分かった今だから気づけるのですが, このログをよく見ると, sshd.service を disable したにもかかわらず /etc/systemd/system/multi-user.target.wants/ssh.service が削除 されています。
sshd ではなく ssh です。
ここが今回の重要な点でした。
systemctl enable で元に戻そうとしたら戻らない
disable の挙動が確認できたので, 元に戻そうと思って systemctl enable sshd.service と実行しました。すると,
kangetsu@ubuntu18:~$ sudo systemctl enable sshd.service
Failed to enable unit: Unit file sshd.service does not exist.
kangetsu@ubuntu18:~$
このように Failed の文字が。
sshd.service という Unit ファイルが存在しないと。
不審に思ってログを見ましたが, 上にある通り Removed と書いてあるので, disable コマンドで Unit ファイルが削除されたのだと思いました。
この段階で, sshd.service を引数にした systemctl のどのコマンドも実行できなくなっていました。
ここで, 勉強したての記憶から, 「Unit ファイルのオリジナルは /lib/systemd/system 配下にある」という情報を思い出し, 確認しました1。
しかし, /lib/systemd/system/sshd.service は見つかりませんでした。
さらに find で sshd.service という Unit ファイルを探しても, 何も見つかりませんでした。
「disable は可逆じゃないのか? オリジナルの Unit ファイルも消すのか?2」と驚き, 「disable は迂闊に実行できないのか」というツイートにつながっています3。
オリジナルの Unit ファイルを確認する
ssh できなくなるのは困るので, 別の VM を起ち上げてその VM で sshd.service の Unit ファイルを systemctl cat で確認しました。
kangetsu@dev_persona:~$ systemctl cat sshd.service
# /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.service
kangetsu@dev_persona:~$
最悪これをコピーしようと考えながら, 次に元の VM では disable 時に削除されてしまった /etc/systemd/system/sshd.service を確認しました。すると,
kangetsu@dev_persona:~$ ll /etc/systemd/system/sshd.service
lrwxrwxrwx 1 root root 31 Apr 30 00:55 /etc/systemd/system/sshd.service -> /lib/systemd/system/ssh.service
kangetsu@dev_persona:~$
このように, ssh.service へのシンボリックリンクであることが分かりました。
ちゃんと理解していたらこの段階で真相がわかったのでしょうが, この時私は「disable 前には /lib/systemd/system/sshd.service という Unit ファイルがあったはず」と思い込んでいたので, この情報を見て「ssh.service へのシンボリックリンクなら, systemctl cat sshd.service とこれの diff を取って, /lib/systemd/system/sshd.service を作ればいい」などと考えていました。
こっちの VM にも /lib/systemd/system/sshd.service なんてないのだから, 慌てて混乱していたのが分かります。
diff は以下の通りでした。
# kangetsu@dev_persona:~$ diff <(systemctl cat sshd.service) /lib/systemd/system/ssh.service
1d0
< # /lib/systemd/system/ssh.service
コメントしか差分がないなら, /lib/systemd/system/ssh.service を /lib/systemd/system/sshd.service としてコピーすれば enable できるようになるはず! と考えて, 元の VM に戻ってその通り実行しました。
すると, 今度は enable でき, sshd.service を systemctl で操作できるようになりました。
同じイメージで構築した VM で確認、なるほど
— kangetsu121 (@kangetsu_121) April 29, 2020
そもそも /lib/systemd/system/sshd.service はなく、/etc/systemd/system/sshd.service は /lib/systemd/system/ssh.service へのシンボリックリンクだった
/etc 以下の ssh.service と sshd.service にコメント以外 diff がないのも納得 pic.twitter.com/5go87F2MhF
真相: Unit ファイルの Alias
これで昨日は安眠したのですが, せっかくだし記録に残しておこうと思って昨夜と今朝, この記事を書いてました。
改めて Unit ファイルの確認などしていたところ, ssh.service の Unit ファイルに Alias=sshd.service という記述がある ことに気づきました4。
kangetsu@ubuntu18:~$ systemctl cat ssh.service
# /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.service
kangetsu@ubuntu18:~$
つまり, 最初から /lib/systemd/system/sshd.service なんて存在しなかったのです。
ssh.service の別名として sshd.service が指定されていたので, disable 後に復旧したかったら systemctl enable ssh.service でよかったのでした。
kangetsu@dev_persona:~$ sudo systemctl enable ssh.service
Synchronizing state of ssh.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable ssh
Created symlink /etc/systemd/system/sshd.service → /lib/systemd/system/ssh.service.
kangetsu@dev_persona:~$
これで, sshd.service というシンボリックリンクが作成され, systemctl で sshd.service を操作できるようになりました。
kangetsu@ubuntu18:~$ ll /etc/systemd/system/sshd.service
lrwxrwxrwx 1 root root 31 Apr 30 11:08 /etc/systemd/system/sshd.service -> /lib/systemd/system/ssh.service
kangetsu@ubuntu18:~$
sshd.service は ssh.service の単なるエイリアスで, 実体は同じ ssh.service なので, この状態だと ssh.service も sshd.service も起動しています。
kangetsu@ubuntu18:~$ systemctl is-active ssh.service
active
kangetsu@ubuntu18:~$ systemctl is-active sshd.service
active
kangetsu@ubuntu18:~$
daemon ということを強調するためにわざわざ sshd.service という名前にしているのでしょうか。
systemd Unit なら sshd.service だけでよいのでは, とも思いますが, 何か理由があるのでしょうか。
ご存じの方は是非教えていただけると嬉しいです。
結論
systemctl disableは必ずしも可逆じゃないので注意- 特に Unit を編集してもいないのに
systemctl enableできなくなったら,/lib/systemd/system以下のファイルをAlias=でgrepすると見つかるかも
おまけ
systemd の Unit ファイルは, /lib/systemd/system や /usr/lib/systemd/system 以下に格納されます。
それらを変更したいときは直接編集するのではなく, /etc/systemd/system 以下に編集した Unit ファイルを置くようにするようです。
RedHat 公式の説明の表を以下引用します5。
| ディレクトリー | 詳細 |
|---|---|
| /usr/lib/systemd/system/ | インストール済みの RPM パッケージで配布された systemd unit ファイル |
| /run/systemd/system/ | ランタイム時に作成された systemd unit ファイル。このディレクトリーは、インストール済みのサービス unit ファイルのディレクトリーに優先します。 |
| /etc/systemd/system/ | systemctl enable で作成された systemd unit ファイルおよびサービス拡張向けに追加された unit ファイル。このディレクトリーは、runtime unit ファイルのディレクトリーに優先します。 |
オリジナルと設定を変えていないなら, 同じ内容のファイルを複製するのも効率が悪いのでオリジナルへのシンボリックリンクを張っているようです。
systemd が参照する Unit ファイルは /etc/systemd/system 以下のファイルを基本的に参照するようです。