Včera tunel fungoval a dnes ráno je unit mrtvý s Failed at step EXEC spawning /usr/local/bin/vpnclient v journalu. Přihlašovací údaje, trasy ani účet do té věty nikdy nevstupují. systemd přeložil ExecStart na cestu, požádal jádro o její spuštění a jádro odmítlo — nejčastěji proto, že na dané cestě nic není nebo soubor není spustitelný. Berte závadu jako problém souboru a argv, dokud analyzovaný příkaz neprokáže opak.

Proč EXEC selhává dřív, než začne autentizace

systemd nespouští ExecStart přes shell. Vytvoří fork, sestaví z řádku vektor argumentů a zavolá execve na první prvek. Pokud toto volání selže, služba se nikdy nedostane k vlastnímu kódu klienta VPN, takže žádné úpravy profilu ani kontrola přihlašovacích údajů nepomohou.

Stavový kód odděluje tuto rodinu od běžných pádů. Unit, který skončí se status=203/EXEC, nikdy nespustil proces; unit, který se spustí a poté skončí se status=1 mohl selhat při autentizaci, DNS nebo směrování. Přečtěte kód dřív než log klienta, protože v prvním případě bude log klienta prázdný.

Cesta v journalu je ta, kterou použil systemd, ne nutně ta, kterou jste upravili. Drop-in soubory a výstup generátoru mohou přepsat soubor dodavatele a zastaralý daemon-reload nechává starý řádek v paměti. Proto je první diagnostikou zeptat se systemd, co považuje za příkaz, místo otevírání souboru, o kterém si pamatujete, že jste ho měnili.

Tři místa, kde je zapsaná cesta k binárce

Klient VPN nainstalovaný z distribučního balíčku, skriptu dodavatele a tarballu vydání může skončit ve třech různých prefixech. Unit, pravidlo oprávnění a instalátor každý zaznamenal jeden z těchto prefixů a závada se objeví, když se dva ze tří neshodují.

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/

Konkrétní nesoulad vypadá takto: skript dodavatele instaluje do /usr/local/bin, tarball byl rozbalen pod /opt a unit stále uvádí starou cestu /usr/bin z předchozího balíčku. Existuje jen jeden z těchto souborů a málokdy je to ten v ExecStart.

Než změníte kterýkoli ze souborů, rozhodněte, která cesta je kanonická. Pokud binárku vlastní distribuční balíček, kanonická je cesta balíčku a unit ji má následovat. Pokud ji vlastní skript dodavatele, platí stejné pravidlo pro prefix dodavatele. Kopírování binárky mezi prefixy vytváří dvě kopie, které se při další aktualizaci rozejdou.

Když se z citovaného příkazu stane název souboru

Druhý režim selhání se skrývá za stejným řádkem v journalu. Někdo zapíše do unitu toto a očekává, že se klient při startu připojí:

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

systemd rozdělí hodnotu na slova, takže první slovo se stane /usr/bin/vpnclient connect office a uvozovky se odstraní jako syntaxe seskupení. Jádro pak hledá soubor, jehož název obsahuje mezery, a takový neexistuje. Chyba je stejná jako u chybějící binárky, i když je binárka nainstalovaná.

Podporovanou formou je skutečný spustitelný soubor následovaný samostatnými argumenty, například ExecStart=/usr/bin/vpnclient connect office. Pokud pracovní postup opravdu potřebuje rouru nebo řetěz, dejte ho do skriptu a zavolejte skript, protože ExecStart nemá &&, nemá roury ani glob expanzi.

Opravte unit, ne souborový systém

Lákavá oprava je symbolický odkaz z očekávané cesty na skutečnou binárku. Funguje, dokud se balíček neaktualizuje a odkaz nezačne ukazovat na odstraněný soubor, nebo dokud se štítek SELinux přestane shodovat s cílem. Raději upravte unit pomocí drop-in, aby soubor dodavatele zůstal nedotčený a aktualizace zůstaly předvídatelné.

Pravidlo oprávnění musí odpovídat stejné cestě i stejným podpříkazům. Záznam v sudoers pro /usr/local/bin/vpnclient neopravňuje /usr/bin/vpnclient a záznam, který povoluje jakýkoli argument, umožní libovolnému místnímu procesu překonfigurovat nebo zastavit tunel. Dejte unitu obě potřebná slovesa a ponechte demontáž mimo něj, pokud zastavení služby už zavírá rozhraní.

Tady se porovnání vyplatí: unit, databáze balíčků i sudoers by měly uvádět jednu cestu. Když se tak stane, chyba EXEC zmizí bez restartu a bez kopie binárky na třetím místě.

Dokažte analyzovaný příkaz před restartem

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 je rozhodující výstup, protože vypisuje argv, které systemd skutečně analyzoval, včetně případného drop-in. systemd-analyze verify zachytí chybějící nebo nespustitelnou cestu ještě před restartem a stavový příkaz po restartu řekne, zda proces nyní existuje.

Pokud se unit spustí a potom selže, opustili jste rodinu EXEC a vstoupili do vlastního zpracování chyb klienta. Hledejte v journalu zprávu klienta, ne další selhání execve. Držte diff unitu a diff sudoers společně v záznamu změny, aby příští aktualizace tiše nevrátila rozdělenou cestu.

Související články