Le service démarre, lit le profil sans se plaindre, puis meurt sur une ligne qui semble sans rapport : il ne parvient pas à ouvrir ca.crt. Rien n’a changé dans le certificat, et le chemin du profil dans l’unité est absolu. Cette combinaison est l’indice. Le chemin que vous avez passé localise un fichier, et les noms à l’intérieur de ce fichier sont résolus ailleurs tout à fait, si bien qu’un lancement qui paraît pleinement qualifié peut encore lire ses clés dans un répertoire que personne n’avait prévu.

L’échec a peu de causes, et il vaut la peine de les séparer dans l’ordre : le répertoire de base que le processus avait réellement, le contexte de lancement du service et les permissions ou le bac à sable autour du fichier une fois le chemin correct. Chacune laisse une trace différente : dans l’unité, dans le processus et sur le système de fichiers.

Le chemin du profil n’est pas un répertoire de base

Imaginez un processus qui démarre dans /tmp et reçoit /etc/vpn/prod/tenant-a.ovpn. À l’intérieur de ce profil, la ligne ca ca.crt nomme un chemin relatif. Rien dans l’argument qui a localisé le profil ne réécrit ce nom, donc le processus cherche /tmp/ca.crt et signale un fichier manquant.

Le plus déroutant, c’est l’ordre des erreurs. OpenVPN analyse d’abord la configuration et ouvre les fichiers référencés à mesure qu’il les atteint, si bien qu’une faute de frappe dans le profil est signalée avant que la moindre clé soit touchée, tandis qu’un certificat manquant n’apparaît que lorsque l’analyse arrive jusque-là. Un profil qui se charge est donc la preuve que la syntaxe est correcte, non que les fichiers qu’il nomme aient été trouvés.

Ce que le manuel sépare : --config et --cd

Le manuel d’OpenVPN 2.6 décrit deux options distinctes. L’une charge un fichier de configuration. L’autre change le répertoire du processus avant que la configuration, la clé, le script et les autres fichiers soient lus, et elle exige un répertoire absolu sans référence au répertoire courant.

openvpn --cd /etc/vpn/prod --config tenant-a.ovpn

Cette invocation est une forme, pas une consigne de lancer le profil de quelqu’un d’autre. Ce qu’elle apporte, c’est du déterminisme : la base de chaque nom relatif de la chaîne est énoncée dans la commande au lieu d’être héritée du shell ou du gestionnaire qui a démarré le processus. L’alternative est tout aussi valable et souvent meilleure pour un service : laissez le répertoire de travail tranquille et rendez absolu chaque fichier référencé dans le profil.

Quel que soit votre choix, auditez toute la chaîne plutôt que le premier échec. Un profil peut attirer un autre fichier de configuration, et les noms relatifs du fichier inclus sont résolus par rapport au même répertoire de processus, non par rapport au répertoire du fichier qui les a inclus. Les chemins de journalisation, les fichiers d’authentification et les scripts auxiliaires suivent la même règle.

Le service démarre ailleurs

Un shell interactif dans le répertoire du profil ne prouve rien sur une unité empaquetée. Les gestionnaires fixent leur propre répertoire de travail, et un script enveloppe peut le changer de nouveau avant l’exécution du binaire. L’unité qui échoue est le seul lancement sur lequel il vaut la peine de raisonner.

systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd

La seconde commande montre le répertoire que le processus vivant détient réellement, ce qui est plus fiable que le fichier d’unité lorsqu’une enveloppe est impliquée. Si les deux ne concordent pas, l’enveloppe est votre réponse et l’unité une fausse piste.

Après la recherche : permissions, chroot et bac à sable

Un répertoire de base correct peut encore produire le même message, car la résolution et l’accès sont des étapes distinctes. Le processus a besoin du droit d’exécution sur chaque répertoire parent et du droit de lecture sur le fichier, et une unité de service qui masque une partie du système de fichiers change ce que le processus peut voir tout court.

Trois variantes courantes produisent une erreur identique. Le fichier n’est lisible que par root alors que le service abandonne ses privilèges avant de l’ouvrir. L’unité met en place une vue protectrice du système de fichiers qui n’inclut pas le répertoire du profil. Le démon tourne avec chroot, si bien que le chemin qui existe sur l’hôte n’existe pas dans cette vue.

Diagnostiquez avec le propriétaire du fichier et l’identité du processus, pas en relâchant les permissions. Rendre une clé privée lisible par tous pour taire cette erreur échange un échec bruyant contre un silencieux, et la clé est la partie du répertoire que vous tenez le moins à exposer.

Prouvez la correction par le lancement maîtrisé

Redémarrez la vraie unité depuis un répertoire sans rapport avec le profil et observez la même ligne de démarrage. Confirmez ensuite que le processus détient le répertoire prévu pendant qu’il tourne et que le certificat chargé est bien celui que vous attendez.

systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager

Quand le chargement des chemins réussit, l’authentification et le routage restent des étapes distinctes avec leurs propres échecs, et une connexion qui monte sans faire passer de trafic est une autre enquête. La correction ici ne porte que sur le répertoire d’où viennent les fichiers, et elle devrait se voir dans l’invocation au lieu d’être mémorisée par qui l’a lancée à la main la dernière fois.

Si le profil est déployé par un gestionnaire de configuration, y placez aussi le répertoire de base ou les noms de fichiers absolus. Une correction manuelle sur un seul hôte est la version de ce problème qui revient après la reconstruction suivante.

Articles similaires