OpenVPN rulează într-un shell, dar eșuează sub cron: problema este PATH
O comandă care se conectează dintr-un shell poate eșua sub cron sau subprocess înainte ca tunelul să pornească. Corectați calea, directorul de lucru și terminalul.
Aceeași comandă se conectează dintr-un shell și moare sub cron, așa că concluzia naturală este că tunelul sau certificatul este vinovat. Uitați-vă la ce au în comun cele două rulări înainte de a crede asta: binarul, configurația și credențialele sunt identice, iar singurul lucru care s-a schimbat este mediul procesului. Când programul nici măcar nu este găsit, nicio cantitate de depanare TLS nu ajută. Corectarea stă de obicei într-o cale, un director de lucru și o mână de variabile de mediu, nu în VPN.
command -v openvpn în shell-ul care funcționează tipărește răspunsul, și foarte des este /usr/sbin/openvpn, un director pe care cron nu îl pune pe calea sa de căutare.
Tunelul nu a pornit niciodată, deci tunelul nu este problema
Sarcinile pornite de cron moștenesc un mediu minimal, în mod convențional PATH=/usr/bin:/bin, și niciun terminal de control. Tot ce este instalat în /usr/sbin sau /usr/local/sbin lipsește pur și simplu din acea listă, iar linia din jurnal spune openvpn: not found în loc de o eroare TLS. subprocess din Python eșuează altfel: moștenește mediul procesului interpretorului, care poate fi mai bogat decât al cron-ului, dar încă nu are un shell care să extindă un tilde. Ambele eșecuri se întâmplă înainte ca un singur pachet să părăsească gazda.
O modalitate rapidă de a le distinge este să citiți prima eroare în loc de ultima. Un binar lipsă produce o excepție de shell sau Python, o configurație proastă produce un mesaj de utilizare OpenVPN, iar un eșec real de strângere de mână produce mărci de timp și adrese de parteneri. Doar al treilea este o problemă de tunel.
Căile absolute și listele de argumente bat șirurile de shell
Transmiteți calea rezolvată în mod explicit și săriți complet peste shell. subprocess.run(["/usr/sbin/openvpn", "--config", config, "--verb", "4"], capture_output=True, text=True, timeout=90) vă dă un cod de retur, stdout și stderr fără a cere unui shell să interpreteze un nume de fișier. Construiți calea configurației din os.environ["HOME"] sau din pathlib.Path.home(), astfel încât un tilde să nu trebuiască niciodată extins. Când chiar aveți nevoie de sintaxă de shell, folosiți shlex.split, pentru că împărțirea unui șir cu .split() se rupe pe orice cale care conține un spațiu. Citiți și stderr, deoarece OpenVPN scrie acolo majoritatea eșecurilor sale timpurii.
Aceeași regulă se aplică și pentru cron: linia sarcinii nu este un shell de autentificare, așa că nimic nu vă încarcă profilul și nimic nu vă definește aliasurile.
Ce moștenește de fapt un crontab
O intrare din crontab primește mediul pe care crontab-ul îl declară plus câteva valori implicite moștenite, așa că scrieți calea întreagă sau înfășurați apelul într-un script care face asta. O linie ca PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin în capul fișierului acoperă locațiile obișnuite de instalare, iar redirecționarea cu >> cron.log 2>&1 este ceea ce face următorul eșec lizibil. Un profil protejat cu frază de acces nu are cum să își ceară secretul sub cron, deoarece nu există niciun terminal pe care să întrebe. O astfel de conexiune aparține unei unități systemd care poate citi un fișier de credențiale, nu unei sarcini care se preface că este interactivă.
Căile relative sunt a doua capcană. O rulare de test are loc adesea din folderul care ține profilul, iar o sarcină cron pornește cu totul altundeva, așa că --config office.ovpn se rezolvă într-un fișier care nu este acolo și mesajul se citește ca fișier lipsă, nu ca tunel lipsă.
Demonstrați jupindu-vă mediul
Reproduceți mediul minimal direct: env -i PATH=/usr/bin:/bin /usr/sbin/openvpn --config office.ovpn --verb 4. O plângere despre cale sau terminal din acea rulare înseamnă că problema stă deasupra VPN-ului. Dacă se conectează, suspecții rămași sunt contul sub care rulează, directorul din care pornește și credențialul pe care îl prezintă. Oricare dintre cele două rezultate este mai bun decât încă o rundă de verificare a certificatelor.
Apoi faceți ambalajul să raporteze singur. Ieșiți cu un status distinct pentru fiecare etapă atinsă, înregistrați ambele fluxuri cu o marcă de timp, iar următorul eșec vă va spune dacă a murit la rezolvarea căii, la autentificare sau după ce tunelul era deja ridicat.
O unitate are nevoie și de ordinea corectă: Wants=network-online.target plus After=network-online.target împiedică clientul să pornească înainte de a exista o adresă sau o rută implicită, ceea ce altfel apare ca erori de certificat sau TLS care dispar la o repornire manuală.