Qu’un script de hook atteigne l’autre côté du tunnel dépend de trois conditions : l’événement auquel il est rattaché, le fait qu’OpenVPN soit encore dans sa séquence de démarrage quand le script s’exécute, et le fait que le travail ait été détaché de cette séquence. La plupart des scripts échouent sur la troisième. Ajouter une pause traite un problème de cycle de vie comme un problème de timing, et cela fonctionne juste assez souvent pour tromper.

Décidez d’abord l’événement, puis le mécanisme de report du travail, puis l’endroit où le travail reporté s’exécute. Ces trois décisions sont indépendantes, et les confondre est la façon dont un hook de deux lignes devient un après-midi entier.

Pourquoi le hook attend un tunnel qui l’attend

OpenVPN appelle ses hooks de façon synchrone, dans le même processus qui déplace les paquets. up se déclenche dès que l’appareil tun ou tap existe, mais avant l’ajout d’une route, et route-up se déclenche après les routes ; aucun de ces moments ne promet qu’un pair répondra. Tant qu’un script de l’un ou l’autre hook s’exécute encore, le démon ne transfère pas, donc une commande qui attend que le trafic traverse le tunnel attend un tunnel qui attend la commande. Cette attente circulaire explique pourquoi une exécution manuelle passe et pourquoi le hook expire.

Le correctif évident, dormir quelques secondes, fonctionne parfois parce que l’extrémité distante est montée d’elle-même, non parce que la pause a réparé quoi que ce soit. Un assistant qui interroge la disponibilité est une autre réponse : le hook rend la main, le transfert commence, et l’assistant obtient ce dont il a besoin quand c’est réellement là.

Choisissez le hook selon l’événement, pas selon la commodité

Rattachez up au travail qui n’a besoin que de l’appareil, route-up au travail qui a besoin des routes, et réservez client-connect et client-disconnect aux tâches par client côté serveur. Le nettoyage à la déconnexion doit supporter d’être exécuté deux fois, car les reconnexions sur une interface persistante ne suivent pas la même séquence qu’un redémarrage complet. Sur le serveur, client-connect se déclenche quand un client s’authentifie, non quand le démon démarre, ce qui mérite d’être confirmé avant d’y déplacer la logique. Si le travail relève du cycle de vie du service plutôt que d’une connexion, une unité systemd est un meilleur foyer que n’importe quel hook.

Les commandes de hook sont exécutées directement par le démon, sans shell intermédiaire, donc ifconfig_remote dans une ligne up arrive comme texte littéral. Enveloppez la commande dans un shell explicite, ou gardez la logique dans un script qui lit l’environnement. ipchange est une directive qui ajoute ses propres arguments : elle remet l’adresse et le port à la commande, donc un script écrit pour aucun en voit deux inattendus.

Trois façons de détacher le travail

La première option est de mettre l’assistant en arrière-plan depuis l’intérieur du hook : lancez-le avec setsid ou systemd-run --scope, fermez les descripteurs hérités et sortez immédiatement avec zéro. La deuxième est de laisser le hook écrire un petit fichier d’état et de laisser une minuterie systemd ou une unité de chemin agir dessus, ce qui garde le hook trivial et place la politique de nouvelle tentative là où il y a des journaux. La troisième est d’abandonner complètement le hook et de déplacer le travail dans une unité ordonnée après le service VPN, ce qui est la bonne réponse quand le travail n’est pas du tout par connexion. Les trois partagent une règle : le hook doit rendre la main avant d’avoir besoin du tunnel.

Quoi que vous choisissiez, rendez l’assistant idempotent et observable. Deux invocations qui se chevauchent ne devraient pas exécuter deux fois la même action irréversible, et un assistant qui échoue doit laisser quelque chose qu’un humain peut lire.

Distinguez les échecs de bac à sable des échecs de cycle de vie

Quand un script s’exécute depuis une invite root et échoue en tant que service, la cause est généralement un bac à sable plutôt que le réseau. Un ProtectHome restrictif, un ProtectSystem en lecture seule, ou un répertoire de travail qui n’existe pas peuvent rendre le script ou ses données inaccessibles, et sur un hôte SELinux le fichier a aussi besoin d’un contexte qui autorise l’exécution. Lisez l’unité avec systemctl cat openvpn-server@server et le journal avec journalctl -u openvpn-server@server -n 80, puis cherchez une erreur de permission ou de contexte plutôt qu’un délai de connexion. Désactiver le bac à sable devrait être le dernier geste, pas le premier.

Un échec de cycle de vie a une autre allure : le hook démarre, l’assistant tourne, et la socket n’est simplement pas encore là. Écrivez une ligne horodatée depuis le hook lui-même, déconnectez puis reconnectez, et lisez les événements dans l’ordre. Quand l’assistant a vraiment besoin du tunnel, laissez-le interroger la disponibilité au lieu de dormir pendant une durée fixe.

Trois champs rendent le prochain rapport utile : quel hook a tourné, quel compte il a utilisé et où il s’est arrêté. Sans eux, chaque enquête ultérieure repart de zéro.

Articles similaires