Hetzelfde commando verbindt vanuit een shell en sterft onder cron, dus de natuurlijke conclusie is dat de tunnel of het certificaat schuldig is. Kijk naar wat de twee runs gemeen hebben voordat je dat gelooft: de binary, de configuratie en de inloggegevens zijn identiek, en alleen de procesomgeving is veranderd. Wanneer het programma niet eens wordt gevonden, helpt geen enkele hoeveelheid TLS-debugging. De oplossing zit meestal in een pad, een werkmap en een handvol omgevingsvariabelen, niet in de VPN.

command -v openvpn in de shell die werkt print het antwoord, en dat is heel vaak /usr/sbin/openvpn, een map die cron niet op zijn zoekpad zet.

De tunnel startte nooit, dus de tunnel is niet het probleem

Taken die door cron worden gestart, erven een minimale omgeving, gewoonlijk PATH=/usr/bin:/bin, en geen sturende terminal. Alles wat in /usr/sbin of /usr/local/sbin is geïnstalleerd, ontbreekt simpelweg in die lijst, en de logregel zegt openvpn: not found in plaats van een TLS-fout. Pythons subprocess faalt anders: het erft de omgeving van het interpreterproces, die rijker kan zijn dan die van cron maar nog steeds geen shell heeft om een tilde uit te breiden. Beide fouten gebeuren voordat er één pakket de host verlaat.

Een snelle manier om ze te onderscheiden is de eerste fout lezen in plaats van de laatste. Een ontbrekende binary geeft een shell- of Python-exceptie, een slechte configuratie geeft een OpenVPN-gebruiksbericht, en een echte handshake-fout geeft tijdstempels en peer-adressen. Alleen de derde is een tunnelprobleem.

Absolute paden en argumentlijsten verslaan shell-strings

Geef het opgeloste pad expliciet door en sla de shell volledig over. subprocess.run(["/usr/sbin/openvpn", "--config", config, "--verb", "4"], capture_output=True, text=True, timeout=90) geeft je een retourcode, stdout en stderr zonder een shell te vragen een bestandsnaam te interpreteren. Bouw het configuratiepad uit os.environ["HOME"] of pathlib.Path.home(), zodat een tilde nooit hoeft te worden uitgebreid. Wanneer je echt shellsyntaxis nodig hebt, gebruik shlex.split, want een string splitsen met .split() breekt op elk pad met een spatie. Lees ook stderr, want OpenVPN schrijft daar de meeste van zijn vroege fouten.

Dezelfde regel geldt voor cron: de taakregel is geen login-shell, dus niets laadt je profiel en niets definieert je aliassen.

Wat een crontab werkelijk erft

Een crontab-regel krijgt de omgeving die de crontab aangeeft plus een paar geërfde standaardwaarden, dus schrijf het pad volledig uit of wikkel de aanroep in een script dat dat doet. Een regel als PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin bovenaan het bestand dekt de gebruikelijke installatielocaties, en omleiden met >> cron.log 2>&1 is wat de volgende fout leesbaar maakt. Een met een wachtwoordzin beveiligd profiel heeft onder cron geen manier om zijn geheim te vragen, omdat er geen terminal is om op te vragen. Zo’n verbinding hoort in een systemd-unit die een inloggegevensbestand kan lezen, niet in een taak die zich interactief voordoet.

Relatieve paden zijn de tweede valkuil. Een testrun gebeurt vaak vanuit de map met het profiel, en een cron-taak begint ergens anders, dus --config office.ovpn wijst naar een bestand dat er niet is en het bericht leest als een ontbrekend bestand in plaats van een ontbrekende tunnel.

Bewijs het door de omgeving af te pellen

Reproduceer de minimale omgeving direct: env -i PATH=/usr/bin:/bin /usr/sbin/openvpn --config office.ovpn --verb 4. Een klacht over pad of terminal uit die run betekent dat het probleem boven de VPN zit. Als het verbindt, zijn de overgebleven verdachten het account waaronder het draait, de map waarin het start en de inloggegevens die het presenteert. Beide resultaten zijn beter dan nog een ronde certificaatcontrole.

Maak daarna de wrapper zelfrapporterend. Stop met een aparte status voor elke bereikte fase, log beide stromen met een tijdstempel, en de volgende fout vertelt je of het stierf bij padresolutie, bij authenticatie of nadat de tunnel al overeind stond.

Een unit heeft ook de juiste volgorde nodig: Wants=network-online.target plus After=network-online.target houden de client tegen om te starten voordat er een adres of standaardroute bestaat, wat zich anders uit als certificaat- of TLS-fouten die bij een handmatige herstart verdwijnen.

Gerelateerde artikelen