apt purge сообщает, что пакета больше нет, а туннель всё равно поднимается при каждом запуске. Journal продолжает расти, маршруты возвращаются, и машина ведёт себя так, будто ничего не удаляли. Установщик поставщика может записывать файлы, которые менеджер пакетов никогда не учитывал, поэтому удалить пакет и удалить продукт — две разные задачи. Runbook ниже разделяет две записи и осознанно завершает удаление.

Два установщика, две разные записи

Пакеты Debian хранят список файлов, и purge удаляет ровно то, что названо в списке, плюс конфигурацию пакета. Установщик shell или комплект .run такого списка не хранит; он пишет куда угодно, обычно в /opt, /usr/local и /etc/systemd/system. Ни один из этих путей не встречается в списке файлов ни одного пакета, поэтому 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 вместо того, чтобы предполагать расположение, ведь поставщик мог записать в /usr/lib/systemd/system или /lib/systemd/system, а не в /etc. Удаление ожидаемой копии оставляет действующую работать, и исправление выглядит успешным до следующей перезагрузки. Перечислите файлы 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

Выберите одного владельца загрузки и проверьте его

Три кандидата могут претендовать на загрузку, и установка всех трёх даёт гонку, а не двойную защиту. Файл автозапуска рабочего стола срабатывает только после входа, unit systemd запускается раньше, но требует клиента, работающего без экрана, а запись cron @reboot срабатывает по расписанию, которое может быть неверным для сетевого клиента. CLI поставщика может дополнительно требовать аргумент сервера, потому что при загрузке нет предыдущей сессии для продолжения. Выберите кандидата, соответствующего способу аутентификации клиента, и отбросьте остальных.

Проверьте это, перезагрузив машину и посмотрев на три независимых факта: состояние unit, список процессов и новое соединение с чем-то, что можно проверить. Клиент, которому нужен связок ключей или интерактивный вход, терпит неудачу при загрузке даже с правильным файлом unit, и journal говорит об этом, если читать его, а не только статус службы.

Либо дайте клиенту учётные данные, работающие без запроса, либо запускайте его при входе. Оба варианта защитимы, а их сочетание это как один туннель приобретает двух владельцев, которые за него ссорятся.

Запишите, чего ждать следующему purge

Отметьте два факта после любого ручного удаления: какой установщик создал схему и какой владелец теперь её запускает. Эта заметка превращает следующий purge из неожиданности в пункт контрольного списка, и она живёт дольше человека, который выполнил работу.

Держите её рядом с конфигурацией службы, а не в личном каталоге, и ставьте дату, потому что обновление поставщика может вернуть удалённый unit. Сравнение systemctl list-unit-files с этой заметкой раз в несколько месяцев ловит восстановление, пока исправление ещё дёшево.

Похожие статьи