Salut les gens 🙂. Cette semaine, une carte rĂ©seau toute bĂŞte nous a fait un peu nous arracher les cheveux : une VM Oracle Linux 8 dont la config rĂ©seau disparaissait Ă chaque reboot. Pas d’erreur, pas de message, juste un fichier ifcfg-ens160 qui s’Ă©vapore dans le /etc/sysconfig/network-scripts. Au final, l’enquĂŞte m’a fait dĂ©couvrir (et utiliser) deux outils que je connaissais mal : le mode debug de NetworkManager et surtout auditd. C’est d’eux que je veux vous parler aujourd’hui. Spoiler : auditd est monstrueusement puissant.
Rappel du contexte
La VM en question était la suivante :
- Oracle Linux 8.10, noyau UEK (5.4.17-2136.x), NetworkManager 1.40.16
- VM VMware, carte vmxnet3 nommée
ens160, importĂ©e Ă partir d’une OVA (gardez ça en tĂŞte, ça aura son importance) auditdactif et verrouillĂ© en mode immutable (-e 2), signe d’un durcissement de type STIG/CIS (l’OVA vient d’un de nos fournisseurs, et dĂ©jĂ soupçonner un hardening CIS, c’est pas mal : tout le monde ne le fait pas).
Les symptĂ´mes
Dans l’ordre, voilĂ comment se comportait la VM :
- On boote la VM. La carte
ens160est bien vue par le noyau, mais n’a pas d’adresse IPv4. nmtui, option « Activer une connexion » : liste vide.nmcli device connect ens160: quelques secondes, aucune erreur.- Retour dans
nmtui: la connexion existe maintenant (chouette), on la configure, on l’active, et ça marche. - Un petit reboot pour checker (toujours une règle de base : rebooter après un changement de configuration)
- Heu …
ifcfg-ens160a disparu, la carte est de nouveau dĂ©connectĂ©e (et nmtui se comporte comme si on n’avait rien fait, du coup).
Un petit dĂ©tail explique le point 2 : nmtui liste des profils de connexion, pas des cartes. Liste vide, donc aucun profil associĂ© Ă ens160. Et nmcli device connect, lui, crĂ©e un profil Ă la volĂ©e quand il n’en trouve pas. Du coup si vous relancez nmtui juste après, vous vous retrouvez avec une carte vierge, mais prĂ©sente.
Les fausses pistes (et comment on les a écartées)
Avant de sortir le panzer et la grosse Bertha, on a vérifié les suspects habituels :
| Hypothèse | Test | Verdict |
|---|---|---|
| Disque restaurĂ© Ă chaque boot (snapshot, mode non persistant) | Le disque de la VM est un delta …-000001.vmdk (donc un snapshot existe), mais une copie du fichier placĂ©e dans /root survivait Ă chaque reboot | ÉcartĂ© : un revert aurait aussi emportĂ© /root, or on l’utilisait comme backup de nos fichiers importants |
| Profil hĂ©ritĂ© de l’initramfs | cat /proc/cmdline : ni ip=, ni rd.neednet | ÉcartĂ© |
| cloud-init / customisation VMware qui réécrit le rĂ©seau | Pas de trace dans le journal | ÉcartĂ© … vraiment ? pourtant … |
Bref, quelque chose supprime bien le fichier. Et lĂ , on change de braquet ou on dĂ©marre l’assistance Ă©lectrique ^^… oui parce que je ne vous le dirai qu’une fois mais j’Ă©tais aussi avec mon Jarvis, ce qui a grandement accĂ©lĂ©rĂ© le passage en revue de toutes les pistes possibles.
Auditd, la boîte noire du Kernel
Vous connaissez auditd ? C’est le dĂ©mon d’audit de Linux : il enregistre les Ă©vĂ©nements que le noyau lui remonte (appels système, accès Ă des fichiers, etc.) selon des règles que vous dĂ©finissez. Sur le principe c’est simple mais ça se rĂ©vèle Ă©norme pour savoir qui touche Ă un fichier. C’est mĂŞme l’outil idĂ©al : le noyau lui-mĂŞme vous donne le nom du processus, son PID, son utilisateur d’origine, et mĂŞme la ligne de commande.
Poser une surveillance
Une règle de type « watch » surveille un fichier ou un répertoire :
|
1 |
auditctl -w /etc/sysconfig/network-scripts/ -p wa -k nicprof |
avec -w, le chemin Ă surveiller, -p wa, les Ă©vĂ©nements Ă tracer (allez voir dans la doc, c’est tout bien espikĂ©) 🙂 … et enfin -k nicprof : une clĂ©, une Ă©tiquette libre qui permet de retrouver facilement ces Ă©vĂ©nements ensuite.
On vérifie les règles chargées avec :
|
1 2 |
auditctl -l auditctl -s # état du sous-système (enabled, lost, backlog...) |
Le piège : le mode immutable
Chez nous, la première commande a répondu :
The audit system is in immutable mode
Un auditctl -s plus tard : enabled 2. C’est le mode immutable (-e 2). Une fois activĂ©, les règles ne peuvent plus ĂŞtre modifiĂ©es avant le prochain reboot (rtfm auditctl). C’est un durcissement volontaire, très courant dans les profils de sĂ©curitĂ©.
Pour s’en sortir proprement, il faut dĂ©clarer la règle de façon persistante, avant la ligne -e 2, puis rebooter. Sur Oracle Linux/RHEL 8, les règles vivent dans /etc/audit/rules.d/*.rules, lues dans l’ordre alphabĂ©tique et assemblĂ©es par augenrules dans /etc/audit/audit.rules (qui compile tout cela dynamiquement). Le -e 2 se trouve en gĂ©nĂ©ral dans 99-finalize.rules, donc un fichier 50-… passe avant, bref vous avez compris.
Deux pièges croisés en route : si vous éditez audit.rules directement, il sera régénéré à partir de rules.d/ au prochain démarrage. Mettez toujours vos règles dans rules.d/. Après le reboot, vérifiez que la règle est bien dans le noyau (auditctl -l | grep nicprof) et pas seulement dans le fichier.
|
1 2 3 4 5 |
cat > /etc/audit/rules.d/50-nicprof.rules <<'EOF' -w /etc/sysconfig/network-scripts/ -p wa -k nicprof -w /etc/NetworkManager/system-connections/ -p wa -k nicprof EOF augenrules --check # signale que rules.d a changé |
Lire les événements avec ausearch
Un Ă©vĂ©nement d’audit n’est pas une ligne mais un bloc de plusieurs lignes (PROCTITLE, PATH, CWD, SYSCALL…) qui partagent le mĂŞme identifiant. D’oĂą un piège classique : un grep ligne Ă ligne coupe l’Ă©vĂ©nement, et le nom du processus (dans la ligne SYSCALL) ne se retrouve pas Ă cĂ´tĂ© du nom du fichier (dans la ligne PATH). Laissez plutĂ´t ausearch filtrer, il affiche l’Ă©vĂ©nement en entier : ausearch -i -f ifcfg-ens160 -ts boot (par exemple)
Les options qui m’ont le plus servi :
| Option | RĂ´le |
|---|---|
-i | interprète les valeurs (noms au lieu de numéros) |
-k <clé> | filtre sur la clé de la règle |
-f <fichier> | filtre sur un nom de fichier |
-sc <syscall> | filtre sur un appel système (unlink, rename…) |
-p <pid> | tous les Ă©vĂ©nements d’un processus |
-m <type> | filtre sur un type d’enregistrement (USYS_CONFIG…) |
-ts boot | depuis le dernier démarrage (existe aussi today, recent, une date…) |
Après un reboot avec la règle chargée :
|
1 2 3 |
type=PATH ... name=/etc/sysconfig/network-scripts/ifcfg-ens160 ... nametype=DELETE type=PATH ... name=/etc/sysconfig/network-scripts/ nametype=PARENT type=SYSCALL ... syscall=unlink success=yes ... comm=NetworkManager exe=/usr/sbin/NetworkManager |
Le fichier est supprimĂ© par NetworkManager lui-mĂŞme (syscall=unlink, nametype=DELETE). Notre coupable, donc ? Pas si vite : l’exĂ©cutant n’est pas forcĂ©ment le demandeur (merci Jarvis). NM peut très bien supprimer un profil parce qu’un autre processus le lui a demandĂ© via D-Bus. L’audit nous dit qui a exĂ©cutĂ©, pas pourquoi.
Le mode debug de NetworkManager
NetworkManager sait être très bavard, à condition de le lui demander. On peut le faire à chaud : nmcli general logging level DEBUG domains ALL ou de façon persistante, dans /etc/NetworkManager/conf.d/99-debug.conf :
|
1 2 3 4 |
[logging] audit=true level=DEBUG domains=SETTINGS,CORE,DBUS_PROPS,DEVICE |
Trois choses Ă savoir :
leveletdomainsrèglent la verbositĂ© et les sous-systèmes tracĂ©s.audit=truefait Ă©crire Ă NM ses propres Ă©vĂ©nements dans le sous-système d’audit (typeUSYS_CONFIG). Chez nous,NetworkManager --print-configmontrait# audit=false, donc c’Ă©tait dĂ©sactivĂ©. Sans ce rĂ©glage, impossible de voir qui demandait la suppression.- Le DEBUG est verbeux : pensez Ă retirer le fichier une fois l’enquĂŞte terminĂ©e.
On redémarre, on relit le journal (journalctl -b -u NetworkManager -o short-precise), et on suit la vie de notre profil grâce à son UUID. Voici la séquence utile, anonymisée :
|
1 2 3 4 5 |
16:00:57.367 ++ connection.id = 'ens160' <- le profil est lu au démarrage 16:00:57.384 policy: auto-activating connection 'ens160' 16:00:57.405 Activation: successful, device activated. <- la carte fonctionne 16:00:57.599 Deleting secrets for connection ... (ens160) 16:00:57.599 device (ens160): state change: activated -> deactivating (reason 'connection-removed') |
Le fichier est donc bien prĂ©sent et bien lu au dĂ©marrage de NM, et la carte s’active sans souci. Environ 190 ms plus tard, le profil est supprimĂ© alors qu’il est actif. C’est beaucoup trop rapide pour un humain ou un cron : il y a un dĂ©clencheur automatique quelque part.
Qui a demandé la suppression ?
C’est lĂ que audit=true nous a aidĂ©s. L’enregistrement USYS_CONFIG de NM contient le PID du demandeur :
|
1 2 3 4 |
ausearch -m USYS_CONFIG -i -ts boot type=USYS_CONFIG ... msg='op=connection-delete uuid=f697ed14-... name=ens160 pid=1311 uid=root result=success exe=/usr/sbin/NetworkManager' (...) |
(pid=1025 en tête de ligne est NM ; pid=1311, dans msg=, est le client qui a envoyé la requête.)
Le coupable : un script de first boot
On remonte donc le processus 1311 : ausearch -i -p 1311 -ts boot
Les enregistrements PROCTITLE nous donnent la ligne de commande exacte :
|
1 |
proctitle=nmcli con delete ens160 pid=1311 ppid=1310 uid=root |
Les règles d’audit durcies dĂ©jĂ prĂ©sentes sur la machine avaient capturĂ© des appels système de ce processus, et PROCTITLE en profite pour nous livrer sa commande. Il reste Ă trouver son parent (ppid=1310). Le journal gĂ©nĂ©ral, sur la mĂŞme fenĂŞtre de temps, rĂ©pond tout de suite :
|
1 2 3 4 5 6 |
journalctl -b -o short-precise --since "16:00:57" --until "16:00:58" ootb.sh[1322]: o Applying IP network settings ... ootb.sh[1343]: --> ERROR : échec modify ipv4.gateway: adresse de passerelle « ipv4.method » non valide. ootb.sh[1349]: o OOTB script exited with error systemd[1]: Started xxx vApp first boot (OOTB Script). |
Un service xxx-vapp-firstboot.service lance un script ootb.sh, embarquĂ© dans l’OVA par son fournisseur.
Le reste est facile Ă trouver dans le script. Il y a un bug dans ce script. Si net.gateway est vide, la variable non quotĂ©e disparaĂ®t de la ligne de commande, qui devient gw4 ipv4.method manual .... NetworkManager prend alors ipv4.method pour la valeur de la passerelle, d’oĂą le message « adresse de passerelle « ipv4.method » non valide ». Le script s’arrĂŞte après avoir tout supprimĂ© et avant d’avoir recréé quoi que ce soit.
Joli bug messieurs (on va les appeler ne vous inquiĂ©tez pas 🙂 ). Ceci dit, ma rĂ©serve vis-Ă -vis des ingĂ©nieurs ou techniciens qui ont pondu ce script m’interdit de vous dire de qui il s’agit, mais sachez juste que le fournisseur en question fait partie des gros pourvoyeurs d’applications mĂ©dico/administratives dans la SantĂ© …
Root-cause et remédiation
Ă€ l’import de l’OVA, la propriĂ©tĂ© vApp net.gateway n’avait pas Ă©tĂ© renseignĂ©e (notre faute, pour le coup). Le script de first boot interprète mal cette absence : plutĂ´t que de s’arrĂŞter proprement, il supprime tous les profils rĂ©seau, plante Ă la crĂ©ation du nouveau, et laisse la VM sans configuration. Tant qu’il Ă©choue, il se relance Ă chaque dĂ©marrage, et c’est exactement ce qu’on observait.
Au passage, un dĂ©tail qui m’a fait perdre du temps : dans le vSphere Client rĂ©cent, les vApp Options ne sont plus dans Edit Settings mais dans VM → Configure → Settings → vApp Options …. et dix minutes de perdues en plus !
J’ai donc corrigĂ© la cause, puis repris la main sur le script. CĂ´tĂ© donnĂ©es : renseigner les propriĂ©tĂ©s vApp, VM Ă©teinte, gateway ajoutĂ©e dans les vApp Options, VM relancĂ©e. CĂ´tĂ© script : le service systemd qui le lance est dĂ©sactivĂ©, et je l’ai exĂ©cutĂ© une fois, Ă la main. Cette fois il est allĂ© au bout et a finalisĂ© toute la VM. Petit tip si vous ne connaissez pas : on peut vĂ©rifier ce que la VM reçoit rĂ©ellement avec vmtoolsd --cmd "info-get guestinfo.ovfEnv".
Pourquoi ne pas simplement laisser le service tourner une fois la gateway corrigĂ©e ? Parce qu’un script qui supprime tout le rĂ©seau avant de planter, je prĂ©fère le lancer moi-mĂŞme plutĂ´t que de le laisser s’exĂ©cuter Ă chaque boot. Et pourquoi ne pas se contenter de dĂ©sactiver le service sans le lancer ? Parce que ce script ne fait sans doute pas que du rĂ©seau : il applique probablement des configurations « mĂ©tier » propres Ă la VM, et on serait passĂ© Ă cĂ´tĂ©.
Bilan : on sait pourquoi ens160 disparaissait (salopiaud !), et la configuration prĂ©vue par l’OVA a bien Ă©tĂ© appliquĂ©e. La preuve par l’absurde : une fois le service dĂ©sactivĂ©, ifcfg-ens160 n’est plus supprimĂ© au reboot, et le script ne repassera plus derrière.
Conclusion
Cette histoire Ă©tait passionnante mĂŞme si elle m’a occupĂ© une après-midi entière. Les enseignements sont Ă©vidents, je pense. Usez et abusez mĂŞme d’auditd. Cet outil est tout simplement gĂ©nial notamment dans un troubleshooting avancĂ© de ce type. Ensuite, un « first boot » ne tourne pas forcĂ©ment qu’une seule fois : allez lire le(s) script(s) et faites-vous une idĂ©e ! Et enfin, quand une VM issue d’une OVA se comporte bizarrement, regardez ses propriĂ©tĂ©s vApp avant de chercher ailleurs. RTFM, logs, et un peu de paranoĂŻa, comme toujours 🙂
Bref, Ă©puisĂ© mais content, car j’ai appris des choses ce mercredi ! Un grand merci Ă Brieuc pour m’avoir aidĂ© pendant la première heure et surtout m’avoir laissĂ© investiguer mĂŞme si ce n’Ă©tait pas gagnĂ© au dĂ©part !
Références
– RTFM Kernel, auditd, Linux etc … vous savez
