Serviciul VPN eșuează la EXEC: comparați unit, binarul și sudoers
Eșecul la pasul EXEC înseamnă că systemd nu a putut porni binarul. Comparați calea din unit, calea instalată și permisiunea din sudoers înainte de a modifica ceva.
Ieri tunelul funcționa, iar azi dimineață unit este mort cu Failed at step EXEC spawning /usr/local/bin/vpnclient în journal. Acreditările, rutele și contul nu intră niciodată în acea propoziție. systemd a rezolvat ExecStart într-o cale, a cerut kernelului să o execute, iar kernelul a refuzat, cel mai des pentru că pe acea cale nu există nimic sau fișierul nu este executabil. Tratați defectul ca pe o problemă de fișier și argv până când comanda analizată demonstrează contrariul.
De ce eșuează EXEC înainte de a începe autentificarea
systemd nu rulează ExecStart printr-un shell. Face un fork, construiește un vector de argumente din linie și apelează execve pe primul element. Dacă acel apel eșuează, serviciul nu ajunge niciodată la codul propriu al clientului VPN, așa că nicio editare a profilului sau verificare a acreditărilor nu poate ajuta.
Codul de stare separă această familie de căderile obișnuite. O unit care iese cu status=203/EXEC nu a pornit niciodată un proces; o unit care pornește și apoi iese cu status=1 poate a eșuat la autentificare, DNS sau rutare. Citiți codul înaintea jurnalului clientului, pentru că în primul caz jurnalul clientului va fi gol.
Calea din journal este cea folosită de systemd, nu neapărat cea pe care ați editat-o. Fișierele drop-in și ieșirea generatorului pot înlocui fișierul furnizorului, iar un daemon-reload învechit lasă linia veche în memorie. De aceea primul diagnostic este să întrebați systemd ce consideră el a fi comanda, în loc să deschideți fișierul pe care vă amintiți că l-ați modificat.
Trei locuri unde este scrisă calea binarului
Un client VPN instalat dintr-un pachet de distribuție, dintr-un script al furnizorului și dintr-un tarball de versiune poate ajunge în trei prefixe diferite. Unit, regula de privilegii și instalatorul au înregistrat fiecare câte unul dintre aceste prefixe, iar defectul apare când două dintre cele trei nu se potrivesc.
| Record | Common value | How to read it |
|---|---|---|
| Unit or drop-in | /usr/local/bin/vpnclient | systemctl cat vpnclient.service |
| Installed binary | /usr/bin/vpnclient | command -v vpnclient, then ls -l the result |
| Sudoers grant | /opt/vpnclient/bin/vpnclient | sudo grep -r vpnclient /etc/sudoers.d/ |
O nepotrivire concretă arată așa: scriptul furnizorului instalează în /usr/local/bin, tarball-ul a fost dezarhivat sub /opt, iar unit numește încă vechea cale /usr/bin dintr-un pachet anterior. Doar unul dintre acele fișiere există, și rareori este cel din ExecStart.
Decideți care cale este canonică înainte de a modifica oricare dintre fișiere. Dacă pachetul de distribuție deține binarul, calea pachetului este canonică și unit trebuie să o urmeze. Dacă o deține scriptul furnizorului, aceeași regulă se aplică prefixului furnizorului. Copierea unui binar între prefixe creează două copii care se despart la următoarea actualizare.
Când o comandă între ghilimele devine un nume de fișier
Un al doilea mod de eșec se ascunde în spatele aceleiași linii din journal. Cineva scrie asta în unit și se așteaptă ca clientul să se conecteze la pornire:
ExecStart=/usr/bin/"vpnclient connect office"systemd împarte valoarea în cuvinte, așa că primul cuvânt devine /usr/bin/vpnclient connect office, iar ghilimelele sunt eliminate ca sintaxă de grupare. Apoi kernelul caută un fișier al cărui nume conține spații, și nu există. Eroarea este identică cu cea a unui binar lipsă, deși binarul este instalat.
Forma acceptată este un executabil real urmat de argumente separate, de exemplu ExecStart=/usr/bin/vpnclient connect office. Dacă fluxul de lucru chiar are nevoie de o conductă sau de un lanț, puneți-l într-un script și apelați scriptul, pentru că ExecStart nu are &&, nu are conducte și nu are expansiune glob.
Reparați unit, nu sistemul de fișiere
Reparația tentantă este o legătură simbolică de la calea așteptată la binarul real. Funcționează până când pachetul se actualizează și legătura indică un fișier eliminat, sau până când o etichetă SELinux nu mai corespunde țintei. Prefer să editați unit printr-un drop-in, astfel încât fișierul furnizorului să rămână neatins și actualizările să rămână previzibile.
Regula de privilegii trebuie să corespundă aceleiași căi și acelorași subcomenzi. O intrare sudoers pentru /usr/local/bin/vpnclient nu autorizează /usr/bin/vpnclient, iar o intrare care permite orice argument lasă orice proces local să reconfigureze sau să oprească tunelul. Acordați cele două verbe de care are nevoie unit și țineți demontarea în afara ei dacă oprirea serviciului închide deja interfața.
Aici comparația își merită efortul: unit, baza de date a pachetelor și sudoers ar trebui să numească o singură cale. Când o fac, eroarea EXEC dispare fără repornire și fără o copie a binarului într-un al treilea loc.
Dovediți comanda analizată înainte de repornire
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-pagersystemctl show este ieșirea decisivă pentru că tipărește argv-ul pe care systemd l-a analizat efectiv, inclusiv orice drop-in. systemd-analyze verify prinde o cale lipsă sau neexecutabilă înainte de a reporni, iar comanda status după repornire vă spune dacă procesul există acum.
Dacă unit pornește și apoi eșuează, ați părăsit familia EXEC și ați intrat în tratarea erorilor proprie clientului. Căutați în journal mesajul clientului, nu încă un eșec execve. Păstrați diff-ul unit și diff-ul sudoers împreună în registrul de modificări, ca următoarea actualizare să nu reintroducă în tăcere calea divizată.