apt purge で VPN を消したのに、サービスは起動時にまだ立ち上がる
apt purge が消すのはパッケージのファイルであり、ベンダーのスクリプトが入れた unit ではない。unit と wants リンクを削除し、起動の担当を一つに絞る。
apt purge はパッケージが消えたと言うのに、起動のたびにトンネルがまた上がってくる。journal は増え続け、経路は戻り、マシンは何も削除していないかのように振る舞う。ベンダーのインストーラーはパッケージ管理が記録しなかったファイルを書き込めるので、パッケージを消すことと製品を消すことは別の作業である。以下の手順書は二つの記録を切り分け、削除を意図的に完了させる。
インストーラーが二つ、記録も二つ
Debian パッケージはファイル一覧を持ち、purge はその一覧が示すものとパッケージ設定だけを消す。シェルインストーラーや .run の束はそのような一覧を持たない。好きな場所、たいていは /opt、/usr/local、/etc/systemd/system に書き込む。これらの経路はどのパッケージのファイル一覧にも現れないため、dpkg は知りもしなければ報告もしない。結果として一つのファイルシステムにインストールの担当が二つ残り、帳簿を付けるのは片方だけになる。
消す前に、その経路について dpkg に問い合わせる。dpkg が否認した経路は自分で削除してよく、同じ答えが後の更新で戻されるかどうかも教えてくれる。
dpkg -S /etc/systemd/system/acmevpn.service
systemctl list-unit-files | grep -i acmevpn
unit と wants リンクは別々のファイル
有効化はインストールとは別の行為である。unit は [Install] 節で対象との関係を宣言し、systemctl enable がその宣言を各対象の .wants/ ディレクトリ内のシンボリックリンクに変える。ここに二つの経路ができる。unit のファイルと、それを起動対象へ引き込むリンクだ。ファイルだけ消すとリンクが何も指さず起動時に苦情が出る。リンクだけ消すと unit は名前で起動できるまま残る。どちらも消す必要がある。
順番に削除し、その後に再読み込みする。systemd が古い定義をメモリに抱え続けないようにするためだ。
systemctl disable --now acmevpn.service
systemctl show -p FragmentPath -p ExecStart acmevpn.service
sudo rm /etc/systemd/system/acmevpn.service
sudo rm /etc/systemd/system/multi-user.target.wants/acmevpn.service
systemctl daemon-reload
場所を決めつけずに FragmentPath を読む。ベンダーが /etc ではなく /usr/lib/systemd/system や /lib/systemd/system に書いている場合がある。想定した複製を消しても実際に効いている方が動き続け、次の再読み込みまで修正は成功したように見える。再読み込みが終わったら unit ファイルをもう一度一覧し、名前がもう出ないことを確認する。
この配置を生かし続けるバイナリを探す
有効な unit は、パッケージデータベースが何を信じていてもデーモンを起動し続ける。そのプロセスはトンネルを上げ直し、経路を追加し直し、kill-switch の規則を復元できる。配置を調べるまで挙動は超自然的に見える。ExecStart の経路をたどり、バイナリがまだそこにあるか確認する。/opt の下に残った実行ファイルだけでこの配置は足りてしまう。unit を消しても予定された起動が止まるだけで、プログラムは消えない。
状態ディレクトリも同じ注意で扱う。アカウントのトークン、生成されたプロファイル、キャッシュされたサーバー一覧は /var/lib の下やホームディレクトリにあり、これは捨てるキャッシュではなく資格情報である。サービスを止めた後に意図して削除し、何を持ち去ったか記録する。
sudo ls -l /opt/acmevpn /usr/local/bin/acmevpn
sudo find /var/lib -maxdepth 2 -iname "*acme*"
systemctl is-enabled acmevpn.service
起動の担当を一つ選び、試す
起動を主張できる候補は三つあり、三つとも入れると二重の安全ではなく競争になる。デスクトップの自動起動ファイルは誰かがログインした後でしか動かず、systemd の unit はより早く動くが画面のないクライアントが要り、cron の @reboot 項目はネットワーククライアントには不適切かもしれない時刻に発火する。ベンダーの CLI はサーバー引数をさらに要求することがある。起動時には続けるべき前のセッションがないからだ。クライアントの認証方法に合う候補を選び、残りは捨てる。
マシンを再起動し、三つの独立した事実を見て試す。unit の状態、プロセス一覧、そして検証できる何かへの新しい接続だ。キーリングや対話的なログインを要するクライアントは、正しい unit ファイルがあっても起動時に失敗する。journal を読めばそう書いてある。サービスの状態だけを見ていては分からない。
問い合わせなしで動く資格情報をクライアントに渡すか、ログイン時に起動するかだ。どちらも筋は通る。両方を混ぜると、一つのトンネルに二人の担当ができて奪い合う。
次の purge が何を予期すべきか書き残す
手作業で削除した後は二つの事実を記す。どのインストーラーがこの配置を作ったか、いま誰が起動しているか。その覚え書きは次の purge を不意打ちから確認項目に変え、作業をした人より長く残る。
個人用ディレクトリではなくサービスの設定の隣に置き、日付を入れる。ベンダーの更新は消した unit を復元しうるからだ。数か月ごとに systemctl list-unit-files をその覚え書きと比べれば、修正がまだ安いうちに復元を捕まえられる。