Същата команда се свързва от шел и умира под cron, затова естественият извод е, че тунелът или сертификатът е виновен. Вижте какво е общо между двата пуска, преди да повярвате на това: изпълнимият файл, конфигурацията и идентификационните данни са еднакви и се е променила само средата на процеса. Когато програмата дори не бъде намерена, никакво отстраняване на грешки в TLS не помага. Поправката обикновено се крие в път, работна директория и шепа променливи на средата, а не във VPN.

command -v openvpn в шела, който работи, отпечатва отговора и той много често е /usr/sbin/openvpn, директория, която cron не слага в пътя си за търсене.

Тунелът така и не стартира, значи проблемът не е в тунела

Задачите, стартирани от cron, наследяват минимална среда, по конвенция PATH=/usr/bin:/bin, и без управляващ терминал. Всичко, инсталирано в /usr/sbin или /usr/local/sbin, просто липсва в този списък и редът в журнала гласи openvpn: not found вместо грешка в TLS. Python subprocess се проваля по друг начин: наследява средата на процеса на интерпретатора, която може да е по-богата от тази на cron, но все още няма шел, който да разгърне тилда. И двата отказа се случват, преди един пакет да напусне хоста.

Бърз начин да ги различите е да четете първата грешка, а не последната. Липсващият изпълним файл дава изключение в шела или Python, лошата конфигурация дава съобщение за употреба на OpenVPN, а истинският отказ при ръкостискане дава времеви марки и адреси на партньори. Само третият е проблем на тунела.

Абсолютните пътища и списъците с аргументи бият шел низовете

Подавайте разрешения път явно и пропуснете шела напълно. subprocess.run(["/usr/sbin/openvpn", "--config", config, "--verb", "4"], capture_output=True, text=True, timeout=90) ви дава код за връщане, stdout и stderr, без да иска от шел да тълкува име на файл. Изградете пътя до конфигурацията от os.environ["HOME"] или pathlib.Path.home(), така че тилда никога да не трябва да се разгръща. Когато наистина ви трябва шел синтаксис, използвайте shlex.split, защото разделянето на низ с .split() се чупи при всеки път с интервал. Четете и stderr, защото OpenVPN записва там повечето от ранните си откази.

Същото правило важи и за cron: редът на задачата не е логин шел, така че нищо не зарежда профила ви и нищо не дефинира псевдонимите ви.

Какво всъщност наследява crontab

Записът в crontab получава средата, която crontab декларира, плюс няколко наследени стойности по подразбиране, затова изпишете пътя или обвийте извикването в скрипт, който го прави. Ред като PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin в началото на файла покрива обичайните места за инсталация, а пренасочването с >> cron.log 2>&1 е това, което прави следващия отказ четим. Конфигурация, защитена с парола, няма как да поиска тайната си под cron, тъй като няма терминал, на който да попита. Такава връзка принадлежи на systemd модул, който може да чете файл с идентификационни данни, а не на задача, която се преструва на интерактивна.

Относителните пътища са вторият капан. Тестовият пуск често се случва от папката, която държи конфигурацията, а задачата на cron стартира съвсем другаде, затова --config office.ovpn се разрешава до файл, който не е там, и съобщението се чете като липсващ файл, а не като липсващ тунел.

Докажете го, като отрежете средата

Възпроизведете минималната среда директно: env -i PATH=/usr/bin:/bin /usr/sbin/openvpn --config office.ovpn --verb 4. Оплакване за път или терминал от този пуск означава, че проблемът е над VPN. Ако се свърже, останалите заподозрени са акаунтът, под който работи, директорията, от която стартира, и идентификационните данни, които представя. Всеки от двата резултата е по-добър от поредния кръг проверка на сертификати.

След това направете обвивката самоотчитаща се. Излизайте с отделен статус за всеки достигнат етап, записвайте двата потока с времева марка и следващият отказ ще ви каже дали е умрял при разрешаване на пътя, при удостоверяване или след като тунелът вече е бил вдигнат.

Модулът също се нуждае от правилен ред: Wants=network-online.target плюс After=network-online.target не позволяват на клиента да стартира, преди да съществува адрес или маршрут по подразбиране, което иначе се проявява като грешки със сертификат или TLS, изчезващи при ръчно рестартиране.

Подобни статии