guest@6admin.io:~$

Le client FOG: python 2, cron toutes les cinq minutes et une flotte qui se rebootait en place

Avant les autres pièces d’archéologie de ce blog, il y a celle-ci. Des années avant l’ère apthogen , sous l’identité gobel1, je faisais tourner des flottes de postes clonées avec FOG (Free Opensource Ghost). L’histoire client de FOG était Windows-first, donc le côté Linux avait besoin d’un petit client à lui: voici FOG.py, version 0.2, python 2, environ 150 lignes plus une poignée de hooks shell. Il date d’avant 2013; le repo git n’a été ré-initialisé qu’en mai 2013, un commit en français, six commits au total. L’archéologie est authentique.

Ce qu’il faisait

Trois devoirs, tous pilotés par cron ou hooks:

  • Poll des jobs: etc/crontab lance /usr/bin/fogtasks toutes les cinq minutes en root. FOG.py demande jobs.php?mac=<mac> pour les tâches d’imaging en attente; s’il y en a une et que who ne montre personne sur tty7, la machine se reboot elle-même vers l’imaging. Un poste de classe non utilisé est un poste qui attend d’être re-imagé.
  • Hostname par MAC: au boot, init.d/hostname appelle fogsethostname, qui interroge le serveur FOG sur hostlookupbymac.php et réécrit /etc/hostname et /etc/hosts, puis /bin/hostname -F. L’identité de la flotte vit dans la base FOG, pas sur les disques.
  • Tracking utilisateurs: etc/gdm/PostLogin/Default et PostSession/Default déclenchent fogtracker LOGIN / fogtracker LOGOUT en root, renvoyant MAC, action et nom d’utilisateur encodés en base64 vers usertracking.report.php. Qui a utilisé quelle machine, pour la piste d’audit de l’époque.

Le protocole: les codes #!

Le protocole du service FOG est une étude de minimalisme 2012: le serveur répond par des codes de statut à deux caractères, un par ligne: #!ok, #!db (erreur de base), #!im (MAC invalide), #!nj (pas de jobs), #!ih (hostname invalide)… Le client les mappe en messages et sort. Pas de JSON, pas de XML, pas de versioning. C’est un contrat de l’ère TFTP et honnêtement, pour une flotte de machines qui demandent seulement “des jobs pour moi?”, c’est suffisant.

Les péchés, disséqués gentiment

Le client précède toutes les conversations sécurité que j’ai eues depuis:

self.host="http://172.16.106.90/"

Une IP privée hardcodée dans les sources, HTTP, pas de TLS et aucun disponible. L’appel de tracking envoie ?mac=<b64>&action=<b64>&user=<b64>&date=<b64> avec date='', un champ jamais rempli (le paramètre a survécu à deux refactors sans que personne ne remarque). La découverte de MAC est une regex sur la sortie de /sbin/ifconfig cherchant HWaddr: l’interface qui allait être renommée, puis dépréciée, puis retirée de toutes les distros Linux en une décennie. Et les bonnes, mes préférées:

except:
    Self._exit('No MAC Found')

Self._exit avec un S majuscule: la typo dans le bloc except qui lèverait elle-même un NameError le jour où elle serait nécessaire. Et c.strip(' ') calculé puis jeté parce que l’affectation a été oubliée. Python 2 sans linters, c’était une fête.

Ce qu’il m’a appris, dans l’ordre où je l’ai appris

  • La config dans le code se compile dans chaque install. Le 172.16.106.90 flotte entière demandait une re-image pour changer. Une décennie plus tard, le firmware d’origine, hérité chez un ancien employeur et pas de moi, hardcodait lui aussi son adresse de serveur: sur une carte de ce type, des fichiers de config, c’est compliqué et l’adressage restait au DHCP de l’infra client. Même réflexe, contextes différents.
  • Ne parsez pas de sortie d’OS que vous ne possédez pas. La regex ifconfig marchait jusqu’au jour où eth0 est devenu enp3s0 et la découverte de MAC est morte avec.
  • Des GET pour des changements d’état, c’est un mensonge de commodité. Les endpoints de tracking écrivaient sur le serveur; chaque log de reverse proxy est devenu une base d’utilisateurs.
  • Et pourtant: ça a tourné. Cron cinq minutes, hooks shell, stdlib python2 plus httplib2: une flotte de machines s’est maintenue imagée et auditée pendant des années sans maintenance. La liste des péchés est longue; la liste des uptimes l’est plus.

Le projet FOG lui-même a fini par faire pousser un vrai client Linux. Celui-ci a pris sa retraite avec sa flotte et son repo (dotfiles bash, irssi , mutt, vim à côté) est resté comme une capsule temporelle de 2013 sous l’identité gobel1, l’époque voisine des années vhalholl .