Учора тунель працаваў, а сёння раніцай unit мёртвы, і ў журнале стаіць Failed at step EXEC spawning /usr/local/bin/vpnclient. Уліковыя даныя, маршруты і акаўнт у гэтым сказе не з’яўляюцца ніколі. systemd ператварыў ExecStart у шлях, папрасіў ядро выканаць яго, і ядро адмовілася — найчасцей таму, што па гэтым шляху нічога няма або файл не з’яўляецца выканальным. Разглядайце збой як праблему файла і argv, пакуль разгледжаная каманда не дакажа адваротнае.

Чаму EXEC падае раней, чым пачынаецца аўтэнтыфікацыя

systemd не запускае ExecStart праз абалонку. Ён робіць fork, будуе вектар аргументаў з радка і выклікае execve для першага элемента. Калі гэты выклік не ўдаецца, служба ніколі не дасягае ўласнага кода кліента VPN, таму ніякае рэдагаванне профілю або праверка ўліковых даных не дапаможа.

Код стану аддзяляе гэтую сям’ю ад звычайных збояў. Unit, які выходзіць з status=203/EXEC, ніколі не запускаў працэс; unit, які запускаецца і потым выходзіць з status=1 мог пацярпець на аўтэнтыфікацыі, DNS або маршрутызацыі. Чытайце код перад чытаннем журнала кліента, бо ў першым выпадку журнал кліента будзе пусты.

Шлях у журнале — той, які выкарыстаў systemd, і не абавязкова той, які вы змянілі. Drop-in і вывад генератара могуць перавызначыць файл пастаўшчыка, а састарэлы daemon-reload пакідае стары радок у памяці. Менавіта таму першая дыягностыка — спытаць у systemd, чым ён лічыць каманду, а не адкрываць файл, які вы памятаеце як зменены.

Тры месцы, дзе запісаны шлях да бінарніка

Кліент VPN, усталяваны з пакета дыстрыбутыва, скрыпта пастаўшчыка або tarball выпуску, можа апынуцца ў трох розных прэфіксах. Unit, правіла прывілеяў і ўсталёўшчык кожны запісаў адзін з гэтых прэфіксаў, і збой праяўляецца, калі два з трох не супадаюць.

RecordCommon valueHow to read it
Unit or drop-in/usr/local/bin/vpnclientsystemctl cat vpnclient.service
Installed binary/usr/bin/vpnclientcommand -v vpnclient, then ls -l the result
Sudoers grant/opt/vpnclient/bin/vpnclientsudo grep -r vpnclient /etc/sudoers.d/

Канкрэтная няўзгодненасць выглядае так: скрыпт пастаўшчыка ўсталёўвае ў /usr/local/bin, tarball распакаваны ў /opt, а unit усё яшчэ называе стары шлях /usr/bin з папярэдняга пакета. Існуе толькі адзін з гэтых файлаў, і рэдка гэта той, што ў ExecStart.

Вызначце, які шлях кананічны, перш чым змяняць любы з файлаў. Калі бінарнік належыць пакету дыстрыбутыва, кананічны шлях пакета, і unit павінен ісці за ім. Калі ім валодае скрыпт пастаўшчыка, тое ж правіла дзейнічае для прэфікса пастаўшчыка. Капіраванне бінарніка паміж прэфіксамі стварае дзве копіі, якія разыходзяцца пры наступным абнаўленні.

Калі ўзятая ў лапкі каманда робіцца іменем файла

Другі рэжым збою хаваецца за тым жа радком журнала. Нехта піша гэта ў unit і чакае, што кліент падключыцца падчас загрузкі:

ExecStart=/usr/bin/"vpnclient connect office"

systemd разбівае значэнне на словы, таму першае слова робіцца /usr/bin/vpnclient connect office, а лапкі выдаляюцца як сінтаксіс групавання. Затым ядро шукае файл, імя якога ўтрымлівае прабелы, і такога файла няма. Памылка ідэнтычная адсутнаму бінарніку, хоць бінарнік усталяваны.

Падтрымліваецца форма, дзе сапраўдны выканальны файл ідзе з асобнымі аргументамі, напрыклад ExecStart=/usr/bin/vpnclient connect office. Калі рабочы працэс сапраўды патрабуе канвеера або ланцужка, змясціце яго ў скрыпт і выклічце скрыпт, бо ExecStart не мае &&, не мае канвеераў і не мае раскрыцця glob.

Выпраўляйце unit, а не файлавую сістэму

Спакуслівы рамонт — сімвалічная спасылка з чаканага шляху на сапраўдны бінарнік. Яна працуе, пакуль пакет не абновіцца і спасылка не пакажа на выдалены файл, або пакуль метка SELinux не перастане адпавядаць мэце. Лепш рэдагаваць unit праз drop-in, каб файл пастаўшчыка заставаўся некранутым, а абнаўленні — прадказальнымі.

Правіла прывілеяў павінна адпавядаць таму ж шляху і тым жа падкамандам. Запіс sudoers для /usr/local/bin/vpnclient не дазваляе /usr/bin/vpnclient, а запіс, які дазваляе любыя аргументы, дазваляе любому лакальнаму працэсу пераканфігураваць або спыніць тунель. Дайце два дзеясловы, якія патрэбны unit, і трымайце дэмантаж па-за ім, калі спыненне службы ўжо закрывае інтэрфейс.

Вось дзе параўнанне апраўдваецца: unit, база пакетаў і sudoers павінны называць адзін шлях. Калі так, памылка EXEC знікае без перазагрузкі і без копіі бінарніка ў трэцім месцы.

Дакажыце разгледжаную каманду да перазагрузкі

sudo systemctl daemon-reload
sudo systemctl cat vpnclient.service
systemctl show -p ExecStart vpnclient.service
systemd-analyze verify /etc/systemd/system/vpnclient.service
sudo systemctl restart vpnclient.service
systemctl status vpnclient.service --no-pager

systemctl show — вырашальны вывад, бо ён друкуе argv, які systemd сапраўды разабраў, уключаючы любы drop-in. systemd-analyze verify ловіць адсутны або невыканальны шлях да перазапуску, а каманда стану пасля перазапуску паведамляе, ці існуе працэс цяпер.

Калі unit запускаецца і потым падае, вы выйшлі з сям’і EXEC і ўвайшлі ва ўласную апрацоўку памылак кліента. Шукайце ў журнале паведамленне кліента, а не чарговы збой execve. Трымайце дыф unit і дыф sudoers разам у запісе змен, каб наступнае абнаўленне не ўвяло моўчкі раздвоены шлях.

Падобныя артыкулы