Čtyřicet souborů .ovpn pro jednu zemi dorazí od poskytovatele a plán vypadá zřejmě: zkopírovat náhodný na místo a pak spustit tunel. Zapsaný jako roura uvnitř ExecStart tenhle plán umírá při startu se stavovým kódem, který nic nevysvětluje. Soubor jednotky není skript a oprava spočívá buď v tom, že výslovně přivedete shell, nebo v tom, že přestanete soubory vybírat úplně.

Příznak: jednotka, která nastartuje a hned umře

Stav ukazuje, že služba selhala ve zlomku sekundy, a řádek v žurnálu uvádí příkaz, který by žádný shell nikdy nedostal. O tunelu se nic neobjeví, protože tunel nikdy nezačal.

Přečíst přesný příkazový řádek ze žurnálu je nejrychlejší způsob, jak to potvrdit. systemd znovu vypisuje, co spustil, a řetězec ukáže znaky roury jako doslovné argumenty, nikoli jako operátory.

Proč ExecStart nemůže spustit rouru

systemd sám rozděluje příkazový řádek, uplatní vlastní citování a substituci proměnných a výsledek pak spustí přímo. Roury, přesměrování, globování shellem a substituce příkazů prostě nejsou součástí té gramatiky. Je to designová volba, ne opomenutí: jednotka deklarativně říká, co spustit, a logika shellu patří do skriptu, který jednotka volá. Z toho plynou obě opravy a ani jedna nevyžaduje dát jednotce více oprávnění.

Také to vysvětluje zavádějící návratový kód. První argument se považuje za binárku, takže vadný řádek selže dřív, než se spustí jakákoli vaše logika.

Volbu proveďte v ExecStartPre

Přímou opravou je přípravný krok, který výslovně volá shell a drží volbu i start v téže jednotce, aby selhání jednoho nemohlo nechat druhé běžet samotné.

[Service]
Type=notify
ExecStartPre=/bin/bash -c "ls /etc/openvpn/profiles/au* | shuf -n 1 | xargs -r -I{} cp -f {} /etc/openvpn/%i.conf"
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/%i.conf

O tom, zda tohle přežije střet se skutečným strojem, rozhodují tři detaily. Pojmenování je první past: systemd nahrazuje %i už při parsování jednotky, dávno předtím, než se spustí bash, takže specifikátor musí stát mimo jednoduché uvozovky. xargs -r brání tomu, aby se cp spustil bez argumentu, když glob nic nenajde. A neúspěšný přípravný krok jednotku zastaví, což je správný výsledek: startovat se včerejší konfigurací je horší než nestartovat. Náhodný výběr, který vždy vezme stejný soubor, obvykle není smůla. Zkontrolujte, zda kopírovaný soubor nepřepisuje aktualizace balíčku, nebo zda glob neodpovídá jediné zastaralé položce.

Nebo nechte OpenVPN vybrat koncový bod

OpenVPN už umí držet několik koncových bodů v jednom profilu a vybírat mezi nimi náhodně, což úplně odstraňuje shell, kopírování i právo zápisu z cesty startu.

client
remote-random
<connection>
remote au1.example.net 1194 udp
</connection>
<connection>
remote au2.example.net 1194 udp
</connection>

Tato forma drží volbu serveru uvnitř souboru, který vytvořil poskytovatel, takže rotace, jež přejmenovává soubory, jednotku nerozbije. Také činí seznam kandidátů auditovatelným, čímž glob nad adresářem nikdy není. Míchání si stále zaslouží své místo, když se profily liší víc než koncovým bodem: jiné certifikáty, jiné směrovací direktivy, jiná šifrová politika. V takovém případě je volba mezi celými konfiguracemi a krok se shell znamená poctivý způsob, jak to vyjádřit.

Přečtěte si instanci a soubor, který použila

Než začnete ladit obsah, potvrďte, která jednotka skutečně běží. Zabalená meta služba a šablona jsou infrastruktura; jednotky odpovídající vašim souborům jsou instance pojmenované po nich.

systemctl list-units 'openvpn*'
systemctl cat [email protected]
journalctl -u openvpn@au -n 30 --no-pager

Instance, o níž si nikdo nepamatuje, že ji zapnul, obvykle vede k zatoulanému konfiguračnímu souboru v adresáři balíčku. Vypněte právě tuto pojmenovanou instanci, ne šablonu, jinak ji příští aktualizace balíčku vrátí. Po změně jednotku spusťte a přečtěte příkazový řádek, který hlásí žurnál, a pak potvrďte cestu ke konfiguraci, kterou skutečně otevřela. Tahle jediná kontrola odděluje fungující náhodný výběr od služby, která tiše znovu používá soubor z minulého měsíce.

Související články