Empêchez deux services Linux de se disputer /etc/resolv.conf
Déterminez qui possède le fichier avant de le modifier. L'attribut d'immuabilité masque le conflit, et les reconnexions ou les renouvellements DHCP annulent sans cesse une réparation DNS manuelle.
Le DNS revient sans cesse à son état précédent, la reconnexion VPN annule votre correction, et quelqu'un finit par proposer de verrouiller le fichier. Cette séquence est un symptôme, pas une solution : deux composants croient tous deux posséder /etc/resolv.conf, et le dernier à écrire gagne. Verrouiller le fichier ne résout pas le conflit de propriété, il ne fait que le masquer tout en cassant le composant que vous vouliez vraiment. Faites les quatre vérifications ci-dessous dans l'ordre et corrigez l'agencement au niveau qui le possède, pas au niveau du fichier.
Vérification 1 : est-ce un lien symbolique, et vers quoi
Commencez par le fichier lui-même, car le chemin que vous modifiez n'est peut-être pas le fichier du tout. Un lien symbolique vers le répertoire d'exécution d'un résolveur se comporte tout à fait différemment d'un fichier ordinaire qu'un script client réécrit :
ls -l /etc/resolv.conf
readlink -f /etc/resolv.conf
cat /etc/resolv.conf
Un lien vers un chemin stub de systemd-resolved signifie que le contenu du fichier est généré, et qu'une modification manuelle sera écrasée au prochain événement. Un fichier ordinaire avec des lignes nameserver est généralement écrit par le client ou le hook DHCP qui s'exécute à la connexion. Les deux sont légitimes ; ce qui compte, c'est de savoir lequel vous avez avant de changer quoi que ce soit.
Vérification 2 : quel démon est en cours d'exécution
Déterminez ce qui est réellement actif, et non ce qui est installé. Plusieurs résolveurs peuvent être présents sur une distribution moderne, et celui qui occupe le port 53 sur l'interface loopback est celui qui répond :
systemctl status systemd-resolved --no-pager
resolvectl status
ss -lunp | grep ':53'
La sortie de statut nomme les serveurs DNS par lien et les domaines de routage, ce qui est la carte dont vous avez besoin pour le DNS divisé. Si les noms internes doivent aller vers un serveur interne et tout le reste vers un serveur public, cette politique vit dans les domaines par lien, et l'aplatir en une liste statique de nameserver dans le fichier fait silencieusement perdre cette distinction.
La configuration propre à NetworkManager décide lequel de ces backends il utilise. Son réglage dns= dans le fichier de configuration principal choisit entre son résolveur interne, dnsmasq, systemd-resolved et none, et une distribution a peut-être choisi pour vous. Lisez ce réglage avant de décider ce que « réparer le DNS » veut dire sur cet hôte.
Vérification 3 : ce que le client VPN est autorisé à écrire
C'est là que le conflit vit généralement. Un client VPN qui réécrit directement le fichier du résolveur se battra contre tout service qui le maintient aussi, et le perdant est celui qui s'exécute en premier. Les versions récentes d'OpenVPN peuvent confier les réglages DNS à systemd-resolved au lieu de réécrire le fichier, ce qui est le comportement coopératif que vous voulez ; les scripts plus anciens et les hooks écrits à la main appellent souvent un utilitaire obsolète ou écrasent simplement le fichier.
La situation de WireGuard est similaire par sa forme : la configuration de l'interface ne gère pas le DNS, ce sont les outils qui l'entourent qui le font, via un utilitaire de résolveur ou l'intégration resolved. Si deux tels utilitaires sont installés, tous deux tenteront d'appliquer leur vision du monde à la connexion.
Lisez les hooks de montée et de descente du client plutôt que de deviner. Quelques lignes de shell dans /etc/openvpn/up.sh ou un répertoire équivalent expliquent un comportement qui semble sinon aléatoire, et c'est aussi là que vous pouvez faire coopérer le client au lieu de le laisser se battre.
Vérification 4 : corrigez chez le propriétaire, pas dans le fichier
Une fois le propriétaire connu, configurez le DNS via l'interface prise en charge par ce propriétaire. Pour systemd-resolved, cela signifie des réglages par lien et des domaines de routage plutôt qu'un fichier statique ; pour NetworkManager, les réglages DNS du profil de connexion et le backend choisi ; pour une distribution utilisant une intégration resolvconf, l'écriture via cet utilitaire.
Ne rendez pas le fichier immuable. L'attribut d'immuabilité arrête les renouvellements DHCP légitimes et les mises à jour VPN avec ceux que vous combattiez, et il laisse le système dans un état où la prochaine mise à jour de paquets ou reconnexion produit une panne que personne ne peut expliquer à partir de la configuration. Il dissimule le conflit de propriété, qui est précisément ce que vous cherchiez à résoudre.
Là où deux composants doivent vraiment coexister, donnez-leur des rôles différents. Laissez le gestionnaire de réseau posséder la configuration au niveau de l'interface, laissez le client VPN apporter le DNS et les domaines par lien via la même intégration, et supprimez le troisième script qui écrit le fichier directement. La documentation de systemd-resolved expose les modes qu'il prend en charge et ce que chacun expose dans /etc/resolv.conf. Choisissez le mode qui correspond à la quantité de politique à conserver. Si le DNS divisé compte, l'agencement stub avec des domaines par lien le préserve, tandis que le mode qui recopie les serveurs amont dans le fichier l'aplatit silencieusement.
Vérifiez après une reconnexion, pas après la modification
Un fichier qui semble correct juste après votre changement ne prouve rien, car l'événement suivant n'a pas encore eu lieu. Déclenchez la séquence qui le cassait auparavant : coupez puis remontez le tunnel, renouvelez le bail DHCP ou basculez l'interface, puis relisez les mêmes valeurs.
resolvectl query build.corp.example
resolvectl query example.com
resolvectl status | head -40
Confirmez ensuite la distinction qui compte : le nom interne se résout via le serveur interne tandis que le nom public fonctionne toujours. Une configuration qui envoie tout au serveur interne semble correcte jusqu'à ce que quelqu'un tente d'atteindre Internet par le tunnel, et une configuration qui envoie tout au serveur public laisse fuiter les noms internes.
Notez le propriétaire, le backend et le mode de lien symbolique dans les notes de déploiement. Ce seul paragraphe transforme une future mise à jour de paquets d'incident en point de revue, et il empêche la personne suivante de redécouvrir le même conflit avec une commande chattr.