home
GNU/cfengine
Un logiciel (libre) pour l'administration de parcs hétérogènes de machines
> date de dernière modification : 25/04/2003
"Sysadmin is a social science"
Mark Burgess, in 'Analytical System Administration, 1'
Ce document est la version "en ligne" de mon mémoire sur GNU/cfengine réalisé dans le cadre d'un stage de dix semaines au LaBRI (Laboratoire Bordelais de Recherche en Informatique), bouquet final d'une année de formation de niveau III en Informatique de Gestion à l'AFPA de Pessac, Gironde, France.
J'ai pensé qu'il pourrait être de quelque utilité à tout sysadmin francophone désireux de se faire une idée de cet excellent outil, avant de l'étudier plus avant. Il n'existe à ce jour, et à la connaissance de Google, aucune autre documentation en français (hormis celle de Ronan Keryell, nettement plus "abrupte" que celle-ci) sur cfengine.
Last but not least, une telle documentation m'aurait, personnellement, permis de gagner un temps précieux ;-)
Haut, Sommaire
note : retrouvez tous les liens utiles sur GNU/cfengine à la section Références - Liens
Voici les meilleures sources auxquelles je me suis abreuvé :
- plusieurs articles de l'auteur, Mark Burgess, traitant (ou non !) de son approche de l'administration système en général, et de cfengine en particulier ;
- une étude de cas, passionnante à tous points de vue, réalisée depuis l'Université de Chicago par David Ressman & John Valdes. Un papier dont je ne saurais trop recommander la lecture : un modèle du genre ;
- un seul et unique article en français, dû à Ronan Keryell, de l'Enst-Bretagne. Intéressant, mais trop peu convivial à mon goût... et pour mes compétences :-[]
Haut, Sommaire
Mark Burgess est né en 1966. Il est diplômé en Sciences Physiques de l'Université de Newcastle (UK).
Féru (entre autre) de biologie et... d'informatique, il est chercheur et professeur associé à l'University College d'Oslo.
Il se définit lui-même avant tout comme un scientifique ; et, dans le domaine informatique, comme un théoricien de l'administration système.
Il est l'auteur de nombreux articles et contributions, parmi lesquels on peut citer 'Computer Immunology' (Usenix Best Paper Award, 1998), de tutoriaux d'excellente facture ('Unix Programming Environnement', 'C Programming Tutorial', etc), ainsi que de plusieurs ouvrages dont 'Principles of Network and System Administration' (J.Wiley & Sons).
Il est également développeur, et à ce titre principal auteur et 'mainteneur' du logiciel GNU/cfengine.
Haut, Sommaire
Le logiciel GNU/cfengine, que nous appellerons plus sobrement ici cfengine, est un puissant outil, écrit en C, pour l'administration de parc hétérogène de machines.
Par administration, j’entends : gestion des configurations et des utilisateurs, maintenance logicielle des systèmes et des applicatifs, veille au bon fonctionnement du réseau et à l'intégrité des paquets qui y transitent, exécutions de tâches courantes telles que l'édition de fichiers, les sauvegardes, etc.
Par hétérogène, j’entends les systèmes Unix et dérivés (Système V et BSD, Mac OSX compris), et aussi Windows NT.
> Concernant ce dernier, très peu de fonctionnalités sont disponibles. En fait, le portage a été réalisé par deux des étudiants de Mark Burgess : Bjørn Gustafson et Jørgen Kjensfi, et non par Burgess lui-même. Il semble que la chose (le portage, pas NT !) n'ait pas évolué depuis plusieurs numéros de version de cfengine.
>> À contrario d'un logiciel de type SNMP, cfengine s'appuie sur la couche TCP de TCP-IP, et sur les outils sécurisés (et libres, et gratuits) que sont OpenSSH, OpenSSL, et MD5checksum.
Haut, Sommaire
Le principe général de base consiste à définir, à l'aide d'un fichier principal de configuration ('cfagent.conf'), hébergé sur la machine locale ou sur un hôte distant désigné comme "serveur de stratégie" (que nous appellerons souvent ici 'policyhost'), quel doit être le "bon" comportement, voire le comportement "idéal" de la machine locale.
On utilisera pour cela un langage spécifique, de (très) haut-niveau, et de type déclaratif. Bien que sa syntaxe possède certaines caractéristiques "standard" (chaînes, expressions régulières, méta-caractères), il nécessite un certain temps d'adaptation — relativement court, même pour un non-programmeur comme moi.
Comme dans tout langage, nous retrouvons la notion de variables. Il existe un certain nombre de variables internes, et il est bien entendu possible de définir nos propres variables.
> Une des idées maîtresses consiste à définir des classes, proche en cela des langages orientés objet, tel C++ ou Java. Ces classes serviront à spécifier les hôtes qui seront concernés par les actions à entreprendre. Certaines classes sont définies automatiquement par cfengine, d'autres seront définies par l'administrateur (elles prendront alors le nom de 'groups').
Une fois les classes définies, des actions pourront être entreprises (vérifications diverses et corrections éventuelles, éditions de liens, création / copie / mises à jour de fichiers, etc).
Enfin, un agent résidant sur chacun des hôtes sera chargé, périodiquement ou non, d'exécuter les actions définies.
> Ce dernier point est important car il implique que les actions seront entreprises par l'hôte lui-même, évitant par là de solliciter le 'serveur de stratégie'. Dans le cas d'une copie de fichiers, par exemple, à partir du 'policyhost', ce dernier se contentera surtout d'authentifier (ou non) le 'localhost'.
> On parle de technique de 'pull', par opposition au 'push' d'un programme comme 'rdist', dans lequel c'est le serveur qui envoie les copies de fichiers vers les hôtes.
Haut, Sommaire
Cfengine installe (par défaut dans /usr/local/sbin) un certain nombre d'exécutables, certains indispensables, d'autres non. Ce sont, dans l’ordre d’importance :
- cfagent
indispensable. Sa mission consiste en premier lieu à lire un fichier nommé 'update.conf' sur la machine locale. Ce fichier sert essentiellement à mettre à jour le fichier 'cfagent.conf' de la machine locale à partir de celui présent sur la machine désignée comme serveur de stratégie ('policyhost'). Cela fait (ou en l’absence du fichier 'update.conf'), 'cfagent' lira le fichier 'cfagent.conf' local, et les instructions (actions) qu'il contient seront exécutées. Comme pour tout exécutable sous Unix, on fait appel à 'cfagent' en invoquant son nom, éventuellement suivi d'arguments. Exemple :
[localhost] $ cfagent -pv
collectera et renverra un certain nombre de renseignements, dont la liste exhaustive des classes "dures" auxquelles appartient 'localhost'.
- cfkey
indispensable. Son rôle consiste à générer une paire de clés publique/privée. S'appuie pour cela sur /dev/random s'il est présent (Linux, BSD), ou bien sur 'cfenvd' décrit plus bas.
Sa mise en œuvre est semblable au 'keygen' de ssh :
[localhost] # cfkey
Making a key for cfengine, please wait, this could take a minute...
Writing private key to /var/cfengine/ppkeys/localhost.priv
Writing private key to /var/cfengine/ppkeys/localhost.pub
[localhost] #
- cfservd
indispensable. Ce démon doit tourner sur chaque hôte afin d'exploiter les (nombreuses) fonctionnalités réseau de cfengine. Se configure à l'aide de 'cfservd.conf', dont la syntaxe est semblable à celle de 'cfagent.conf'. Pour autant, ce sont deux fichiers de conf différents. Dans 'cfservd.conf', nous ne spécifierons pas d'actions à entreprendre, mais nous définirons certaines règles d'authentification, d'encryptage, de vérification d'intégrité, etc.
Comme il doit tourner en permanence, on le placera avantageusement dans un script de démarrage, style /etc/rc.d/init.d/cfengine (le fichier /etc/rc.local est à éviter, d'une façon générale).
- cfexecd
optionnel, pratique, mais trop peu documenté. Aussi nommé 'scheduler' (programmateur). En théorie, un petit démon 'à la cron', chargé de lancer 'cfagent' périodiquement, et localement. Il a également pour tâche de vérifier les permissions d'accès (700) des sous-répertoires clefs de /var/cfengine, corriger les erreurs et faire son petit rapport. Les sorties ainsi générées se logent dans /var/cfengine/outputs, mais, comme nous le verrons plus loin, également dans /var/log/.
> Deux solutions pour qu'il se lance, par exemple, toutes les heures :
> À déplorer l'absence d'un fichier de configuration pour cet exécutable.
> Petite facétie : 'cfexecd' recherche l'exécutable 'cfagent' dans /var/cfengine/ où il ne se trouve pas, bien évidemment.
Pour y remédier, soit on crée sur chacun des hôtes (cfengine sait très bien faire ça !) un lien symbolique pointant vers le bon emplacement (/usr/local/sbin/cfagent) ; ou bien encore on l'invoquera de la façon suivante :
[localhost] # cfexecd -F -f /usr/local/sbin/cfagent
- cfrun
optionnel, mais bien pratique. Ce petit programme sert à exécuter 'cfagent' sur un ou plusieurs hôtes distants, au cas où cet (ces) hôte(s) ne pourrai(en)t pas, ou plus, exécuter eux-même leur 'cfagent' local — ou encore en cas de modification urgente à propager. On spécifie une ou plusieurs classes d'hôtes comme argument. Si aucune classe n'est spécifiée, tous les hôtes présents dans le fichier 'cfrun.hosts' seront contactés. Dans ce fichier sont listés en effet tous les hôtes susceptibles d'être contactés de cette manière.
- cfenvd
optionnel, mais discret... Ce démon a pour tâche de collecter des données statistiques sur le comportement de l'hôte ; des renseignements qui pourront être exploités ensuite par 'cfagent'. Pour cela, il lui faut tourner plusieurs semaines sur l'hôte. Son auteur l'appelle un 'détecteur d'anomalies'.
> Dans un premier temps, il remplacera surtout /dev/random pour la génération de nombres aléatoires (utiles à la création de la paire de clés publique/privée) sur les systèmes qui en sont dépourvus.
> À placer, lui aussi, dans notre script de démarrage /etc/rc.d/init.d/cfengine, où il sera en bonne compagnie.
- cfenvgraph
très optionnel. Tellement optionnel à vrai dire que je ne le mentionne ici qu'à titre purement indicatif.
Pour plus de détails :-) la page qui lui est consacrée
Cfengine distingue trois sortes de classes : les classes "dures" (hard classes), les classes "évaluées" (evaluated classes), et celles que nous définirons nous-mêmes.
# Une classe "dure" est attribuée automatiquement, et à un instant 't', par cfengine pour un hôte donné. Elle s'appuie sur :
- son architecture matérielle (linux_i686, 32bits) ;
- son système d'exploitation (redhat_linux) ;
- la version du noyau de l'OS (2_4_18_3) ;
- son adresse IP (192.168.0.12) ;
- le nom du domaine auquel il appartient (labri.fr) ;
- son nom DNS, non qualifié (stage2) ;
- le même, pleinement qualifié (stage2.labri.fr) ;
- une classe 'any', à laquelle toute machine appartient ;
- les minutes et heures du jour (Min15, Hr09...), le jour de la semaine (Thursday), le jour du mois (Day25), le mois (July), l'année (Yr2002), etc.
# Les classes "évaluées" se construisent à partir de variables internes, équivalant à la commande 'test' du shell ; exemple :
classes:
shadowers = ( FileExists(/etc/shadow) )
# Nous créerons nos propres classes en utilisant la section 'groups'.
> Créons un groupe dont sera exclu 'stage5' :
groups:
mongrouparfait = ( +mongroupamoi -stage5 )
Comme tout outil d'administration qui se respecte, cfengine propose de nous informer sur ce qu'il fait (ou tente de faire) — mais également, pour peu qu'on le lui demande, sur ce que les hôtes font (ou sont censés faire), voire enfin sur ce que les pirates sont en train de réussir (ou ont déjà réussi) à faire...
Par défaut, les logs sont placés dans /var/cfengine :
[stage2] $ ls /var/cfengine | grep log
cfagent..log
cfengine.localhost.runlog
cfengine.stage2.labri.fr.runlog
cfengine.stage2.runlog
> Il est dit dans le 'reference manual' que nous avons la possibilité de changer l'emplacement des logs, dans 'cfagent.conf', par la directive :
LogDirectory = ( /my/new/log_directoy )
mais ce n'est plus vrai. Comme nous en informe 'cfagent -v', cela doit se faire désormais dès l'étape './configure' de l'installation avec l'option :
configure --with-logdir=/my/new/log_directory
>> Aucun de ces logs ne contient de message d'erreur ou d'alerte :-(. Il faudra aller les traquer quelque part dans /var/log/(cf Simulation de Déploiement).
Dans la mesure où cfengine est un outil qui repose sur un langage, autant se familiariser dès à présent avec sa syntaxe.
Celle-ci est (très) schématiquement structurée ainsi :
section:
classes::
variable = ( valeur ou liste de valeurs )
Pour aller un peu plus loin, voici un 'cfagent.conf' basique, tout juste fonctionnel :
###################################################################
#
# 'cfagent.conf' basique, tout juste fonctionnel,
# que nous retrouverons d'ailleurs pratiquement à l'identique
# au chapitre "INSTALLATION & CONFIGURATION".
#
###################################################################
control:
# Pas d'espace avant les deux-points, sinon ça ne marche pas.
# Nous venons de définir une section. Les sections sont prédéfinies,
# il n'est pas possible d'en créer de nouvelles.
# La section 'control' est la plus importante de toutes. Sans elle,
# aucune action ne pourra être entreprise.
domain = ( labri.fr )
actionsequence = ( shellcommands )
# Variables internes (builtin).
# Ici les espaces sont chaudement recommandés.
# Avant d'entreprendre une action, il faut prévenir !
# Autrement dit, si pas d''actionsequence' (ou si aucune
# action n'est spécifiée), il ne se passera rien du tout.
shellcommands:
any::
# Voici une classe, reconnaissable à ses doubles deux-points.
# On ne la déclare bien sûr qu'APRES avoir déclaré l'action à entreprendre.
# La classe "any" est générique. N'en spécifier aucune revient au même.
"/bin/echo cfagent en sait assez !"
# Le chemin de la commande à exécuter se doit d'être absolu.
# Toute 'shellcommand' doit être placée entre guillemets.
#
#
# fin de cfagent.conf basique, tout juste fonctionnel.
Haut, Sommaire, Présentation Détaillée
Pour mener à bien ma mission, je disposais :
- d'une salle de cours, claire, et divinement silencieuse :-)
- de six machines de type PC i686 en parfait état de fonctionnement
- de l'aide et du soutien de Patrick (ingénieur système au Labri, de mon stage le tuteur)
Les machines de test, déjà interconnectées et protégées du reste du réseau (à moins que ce ne soit le reste du réseau qui ne soit protégé d'elles !!), ont été nommées, dans l'ordre d'installation : 'stage2', 'stage3', etc.
Pour l'adressage IP, nous utilisons l'adresse privée 192.168.0. Ainsi les machines, dans le même ordre, ont les adresses successives 192.168.0.12, 192.168.0.13, etc.
L'adresse de la passerelle et celle du DNS ne font qu'une, ce qui ajoute le confort logiciel au confort matériel :-) : 192.168.0.1.
Sur la machine 'stage2' (notre 'policyhost'), j'ai installé une Linux RedHat 7.3.
Sur 'stage3', une (toujours Linux) Mandrake 8.2.
Sur 'stage4', il s'agit d'une FreeBSD 4.5.
Sur 'stage5', je me suis contenté de décompresser un Windows 2000 'Pro' (pour 'Protubérant', peut-être ?), déjà présent sur un des deux disques locaux.
Sur 'stage6', j'ai installé une Turbolinux 6.5 Server.
Enfin, sur 'stage7', qui ne servira que de serveur de logs, une (Linux) Slackware 8.0 minimale.
En résumé, cela nous donne le fichier /etc/hosts suivant :
# fichier /etc/hosts suivant
127.0.0.1 stage2.labri.fr stage2 # important pour 'cfagent'!
192.168.0.12 stage2.labri.fr stage2 # RedHat 7.3
192.168.0.13 stage3.labri.fr stage3 # Mandrake 8.2
192.168.0.14 stage4.labri.fr stage4 # FreeBSD 4.5
192.168.0.15 stage5.labri.fr stage5 # Ouine_Dose_2k
192.168.0.16 stage6.labri.fr stage6 # Turbolinux 6.5
192.168.0.17 stage7.labri.fr stage7 # Slackware 8.0
192.168.0.1 gateway dns
> à distribuer à tous les hôtes, à l'aide de... 'cfagent', pourquoi pas ?
Haut, Sommaire, Environnement de Tests
- Téléchargement des différentes sources — puis installation, configuration, et test de cfengine sur une machine, ici baptisée 'stage2'.
- Copie des différentes sources nécessaires (via ssh) depuis 'stage2' vers chacun des hôtes. Installation de cfengine sur ceux-ci. Puis pollenisation des différents fichiers de configuration, de 'stage2' vers chacun des hôtes (cf Simulation de Déploiement).
- Étudier un certain nombre de fonctionnalités de cfengine, à travers l'écriture d'un fichier 'cfagent.conf' un peu plus élaboré (cf Explorations).
L'installation de cfengine nécessite l'installation (ou la présence) préalable de :
- OpenSSL
- Berkeley DB version 3.2, ou supérieure
> Lorsqu'il n'était pas présent sur la machine, OpenSSL fut installé à partir des sources (www.openssl.org) :
[stage2] $ tar xzf openssl-0.96c.tar.gz
[stage2] $ cd openssl-0.96c && ./config
[stage2] $ make
[stage2] # make install
[stage2] $ make clean # (optionnel)
> Idem pour Berkeley DB (www.sleepycat.com) :
[stage2] $ tar xzf db-3.2.9.tar.gz
[stage2] $ cd db-3.2.9/build_unix
[stage2] $ ../dist/configure
[stage2] $ make
[stage2] # make install
[stage2] $ make clean # (optionnel)
Installation de cfengine à partir des sources (www.cfengine.org) :
[stage2] $ tar xzf cfengine-2.0.3.tar.gz
[stage2] $ cd cfengine-2.0.3 && ./configure
[stage2] $ make
[stage2] $ make check # (optionnel)
[stage2] # make install
[stage2] $ make clean # (optionnel)
L'installation n'ouvrant pas toute seule de socket pour cfengine, vérifions sa présence préalable :
[stage2] $ less /etc/services | grep cfengine
cfengine 5308/tcp
cfengine 5308/udp
> la présence du port udp n'est à priori pas indispensable.
> Il faudra naturellement faire de même pour chacun des hôtes, et créer l'entrée en cas de besoin.
>> make install a, principalement, installé les exécutables suivants (passés en revue à la section Présentation Détaillée) :
/usr/local/sbin/cfagent
/usr/local/sbin/cfenvd
/usr/local/sbin/cfexecd
/usr/local/sbin/cfkey
/usr/local/sbin/cfrun
/usr/local/sbin/cfservd
> tout un tas de fichier '*.example' dans :
/usr/local/share/cfengine/
> de la (précieuse) documentation :
/usr/local/share/cfengine/html/cfengine-Reference.html
/usr/local/share/cfengine/html/cfengine-Tutorial.html
>> Le programme a également créé le répertoire /var/cfengine, vide pour le moment.
> La toute première chose à faire est de lancer :
[stage2] # /usr/sbin/cfkey
qui générera une paire de clés publique/privée, nécessaire à l'authentification :
/var/cfengine/ppkeys/localhost.priv
/var/cfengine/ppkeys/localhost.pub
> Ensuite, on lance un autre programme :
[stage2] # /usr/sbin/cfexecd -F
qui créera pour nous (encore qu'il soit possible de les créer manuellement !) les sous-répertoires (vides) suivants :
/var/cfengine/inputs
/var/cfengine/outputs
/var/cfengine/bin
Le premier hébergera les fichiers *.conf ; le second accueillera les logs générés par 'cfexecd'. On logera éventuellement dans le troisième un lien symbolique pointant sur /usr/local/bin/cfagent, pour le même 'cfexecd'.
Haut, Sommaire, Installation & Configuration
> L'étape suivante consiste à créer (en tant que 'root') nos fichiers de conf.
Commençons par un 'cfagent.conf' basique, tout juste fonctionnel :
################################################################
#
# cfagent.conf basique, tout juste fonctionnel.
# Le même qui nous a servi à étudier la syntaxe de cfengine.
#
################################################################
control:
domain = ( labri.fr )
actionsequence = ( shellcommands )
shellcommands:
"/bin/echo cfagent en sait assez !"
# Dans un premier temps, cela suffit !
# En effet, cfagent requiert essentiellement la présence d'une
# 'actionsequence' dans son fichier de conf'.
#
#
# fin de cfagent.conf, basique tout juste fonctionnel.
# Ensuite, nous créerons le fichier 'update.conf', lequel, de par son rôle, se doit de rester simple et fonctionnel :
##############################################################
#
# Ceci est le fichier update.conf, simple et fonctionnel,
# servant à tenir à jour tous les fichiers de conf,
# (soit l'intégralité du répertoire /var/cfengine/inputs)
# sur les machines locales.
# En tout état de cause, le garder simple et fonctionnel !
#
##############################################################
control:
domain = ( labri.fr )
actionsequence = ( copy )
# En fait, l'action 'copy' sera utilisé ici pour 'update',
# mais comme cette dernière n'existe pas...
# De toute façon, basiquement, c'est bien une copie de fichiers que l'on demande.
policyhost = ( stage2 )
masterdir = ( /var/cfengine/inputs )
localdir = ( /var/cfengine )
# Bien entendu, 'stage2' existe dans le fichier /etc/hosts de la machine locale.
# En-dehors de ça, nous venons de déclarer nos propres variables.
# Elles ne tarderons pas à servir...
copy:
!stage2::
# Inutile de spécifier une classe, 'any' ou autre,
# car nous voulons que TOUS les hôtes soient à jour de leurs fichiers de conf...
# Tous, sauf le ‘policyhost’, bien sûr !
# Précédée du signe '!', toute machine ou classe de machine sera
# exclue de l'action entreprise.
$(masterdir) dest=$(localdir)/inputs
# L'action 'copy' est structurée de cette façon.
# L'usage de l'espace autour de la source et de la destination est libre,
# mais pas d'espace à l'intérieur de celles-ci.
# Règle d'or : si la source est un fichier, la destination doit être un fichier.
# Ici, la source est un répertoire, la destination doit en être un aussi.
# Tout ce que contient l'un sera copié dans l'autre.
server=$(policyhost)
# Soit le répertoire /var/cfengine/inputsde 'stage2', notre 'policyhost',
# pas la peine de préciser ;-)
r=inf
# Option obligatoire car copie de répertoire.'r' pour récursif
# (peut également s'écrire 'recurse'). 'inf' pour niveau maximum de récursivité,
# ici pas vraiment nécessaire, mais bon.
purge=true
# Sans cette option, 'copy' générerait des fichiers *.cfsaved
# dans le répertoire de destination.
# Ici, nous obtiendrons une réplication exacte et fidèle du répertoire source, sans plus.
type=binary
# Le fichier 'dest' sera comparé bit à bit à son homologue source.
# Il existe d'autres moyens de comparaison (cf 'ref.man.').
mode=644
# Là, 'copy' se "contente" de vérifier les permissions d'accès des fichiers
# présents dans $(localdir)/inputs, et de corriger le tir en cas de besoin.
trustkey=true
# Le point sensible.
# Ici, nous voulons que $(policyhost) fasse confiance à la machine locale,
# et accepte sa clef publique lors du premier contact entre les deux hôtes.
# Cela nous évitera d'avoir à faire cette échange de clefs manuellement,
# mais bien évidemment nous créons ainsi une p’tite faille de sécurité...
#
#
# Fin du fichier update.conf, simple et fonctionnel.
# Enfin, nous créerons le fichier de conf pour 'cfservd' :
################################################################
#
# cfservd.conf
# La présence de ce fichier, et du démon qui s'y réfère,
# sont indispensables au bon fonctionnement de cfengine.
#
################################################################
control:
domain = ( labri.fr )
AllowConnectionsFrom = ( 192.168.0 )
TrustKeysFrom = ( 192.168.0 )
Access = ( root moi )
# Variables internes, pour faciliter les choses.
cfrunCommand = ( "/usr/local/sbin/cfagent" )
# Utile pour que 'cfrun' sache ce qu'il a à faire...
grant:
# Peut aussi s'appeler 'admit'. Une section très importante,
# car c'est ici que nous allons spécifier quels hôtes ont droit d'accès
# à quels fichiers, voire sytèmes de fichiers...
# Je ne cache pas que la plupart des messages d'erreur que j'ai essuyé venaient de là !
any::
/usr/local/sbin $(policyhost)
# Requis par 'cfrun' (lancé depuis 'stage2', bien sûr).
# Notons que les variables déclarées dans 'update.conf' restent valables ici :-).
stage2::
/var/cfengine/inputs stage*.labri.fr
# Sans cette spécification-là, c'est l'action 'copy' de 'update.conf'
# qui échouerait lamentablement.
#
#
# fin de cfservd.conf.
Pour finir, notre '$(masterdir)' de référence ressemble à ceci :
[stage2] # ls -l /var/cfengine/inputs
total 12
-rw-r--r-- 1 root root 168 Aug 12 10:13 cfagent.conf
-rw-r--r-- 1 root root 237 Aug 9 13:36 cfservd.conf
-rw-r--r-- 1 root root 750 Aug 8 18:02 update.conf
Il ne nous reste plus qu'à essaimer l'ensemble de notre configuration :-)
Haut, Sommaire, Les fichiers .conf
- pour mémoire, notre serveur de stratégie ('policyhost') est la machine baptisée 'stage2', son invite étant sobrement [stage2].
Dans un souci de clarté (préoccupation majeure, voire obsessionnelle du rédacteur de documentation), 'stage3' représentera par convention tous les autres hôtes, et son invite ([stage3], donc) apparaîtra chaque fois qu'une commande sera lancée depuis n'importe lequel d'entre eux.
- il a été décidé (à l'unanimité puisque j'étais d'accord) que, pour simplifier les échanges entre les machines, chacune d'elles serait à la fois cliente et serveur SSH (pour Secure SHell, www.Openssh.org)
Exemple, avec le 'tarball' de cfengine que l'on a conservé dans notre répertoire utilisateur sur 'stage2' :
[stage2] $ cd /home/moi
[stage2] $ scp cfengine-2.0.3.tar.gz moi@stage3:~/cfengine-2.0.3.tar.gz
moi@stage3's password:
cfengine-2.0.3.tar.gz 100% |******************| 1114 KB 00:00
[stage2] $
> il en ira de même pour les sources d'OpenSSL et de BerkeleyDB, lorsque ces derniers ne seront pas présents sur les hôtes.
- comme certains fichiers nécessitant d'être copiés à distance appartiennent à 'root', j'ai dû décommenter la ligne suivante du fichier /etc/ssh/sshd_config de chacun des hôtes :
PermitRootLogin yes
Je vous sens nerveux, mais pas d'inquiétude : nous boucherons ce vilain trou de sécurité, à la section Explorations, promis ;-)
Il s'agit maintenant de polleniser les hôtes, à partir de 'stage2'.
Une fois l'installation de cfengine réalisée sur chacun d’entre eux (créations de la paire de clé publique/privée (via 'cfkey') et des répertoires nécessaires dans /var/cfengine comprises), il leur faudra récupérer leurs fichiers de conf à partir du 'policyhost' ('stage2', en l'occurence).
Commençons par 'update.conf', puisque c'est grâce à ses instructions que les autres fichiers pourront être copiés sur la machine locale :
[stage2] # cd /var/cfengine/inputs
[stage2] # scp update.conf root@stage3:/var/cfengine/inputs/update.conf
root@stage3's password:
update.conf 100% |***************************| 750 00:00
[stage2] #
> Si l’on a préféré ne pas utiliser l'option 'trustkey=true' du même 'update.conf', il conviendra de procéder à la copie brutale de la clef publique de la machine locale vers le serveur de stratégie :
[stage3] # cd /var/cfengine/ppkeys
[stage3] # scp localhost.pub root@stage2:/var/cfengine/ppkeys/root-192.168.0.13.pub
root@stage2's password:
localhost.pub 100% |***************************| 426 00:00
[stage3] #
> Cela fait, et avant de lancer localement 'cfagent' pour la première fois, nous avons pour nous résumer la paire de clés publique/privée générée par 'cfkey' :
[stage3] # ls -l /var/cfengine/ppkeys
total 8
-rw------- 1 root root 1743 Aug 12 10:01 localhost.priv
-rw------- 1 root root 0426 Aug 12 10:01 localhost.pub
> ainsi que le seul fichier de conf' que nous avons eu besoin de récupérer :
[stage3] # ls -l /var/cfengine/inputs
total 4
-rw-r--r-- 1 root root 750 Aug 12 11:01 update.conf
# Lançons localement 'cfagent' (la première fois, c'est toujours un peu émouvant) :
[stage3] # cfagent
stage3.labri.fr: Trusting server identity and willing to accept key from stage2=192.168.0.12
cfengine:stage3: cfagent en sait assez !
[stage3] #
> Après quoi, puisque tout s'est bien passé :-), on se retrouve avec la clef publique du policyhost en plus de notre paire publique/privée :
[stage3] # ls -l /var/cfengine/ppkeys
total 12
-rw------- 1 root root 1743 Aug 12 10:01 localhost.priv
-rw------- 1 root root 0426 Aug 12 10:01 localhost.pub
-rw-r--r-- 1 root root 0426 Aug 12 12:01 root-192.168.0.12.pub
> et tous les autres fichiers de conf, grâce aux bons soins d''update.conf', ont bien été copiés dans /var/cfengine/inputs :
[stage3] # ls -l /var/cfengine/inputs
total 12
-rw-r--r-- 1 root root 968 Aug 12 12:01 cfagent.conf
-rw-r--r-- 1 root root 237 Aug 12 12:01 cfservd.conf
-rw-r--r-- 1 root root 750 Aug 12 11:01 update.conf
> Enfin sur notre $(policyhost), une fois que tous les hôtes ont effectué les différentes étapes, on trouve bien sûr ceci :
[stage2] # ls -l /var/cfengine/ppkeys
total 12
-rw------- 1 root root 1743 Jul 19 13:01 localhost.priv
-rw------- 1 root root 0426 Jul 19 13:01 localhost.pub
-rw-r--r-- 1 root root 0426 Aug 12 12:01 root-192.168.0.13.pub
-rw-r--r-- 1 root root 0426 Aug 12 12:11 root-192.168.0.14.pub
-rw------- 1 root root 0426 Aug 12 12:21 root-192.168.0.16.pub
> les droits d'accès des clefs publiques telles que l'option 'trustkey=true' de'update.conf' en a autorisé la propagation (les hôtes concernés ici sont 'stage3' et 'stage4') sont passés pendant l'opération de 600 à 644, pour une raison qui échappe à ma compréhension (limitée, je le reconnais).
En ce qui concerne 'stage6', en revanche, sa clef publique a été copiée manuellement vers 'stage2'. C'est ce qui explique que ses droits, à elle, n'aient pas été modifiés.
>> On peut à présent se préoccuper de rediriger les logs vers le serveur de logs ('stage7'), puisqu'on en a un :-).
Pour cela, rajoutons dans le fichier /etc/syslogd.conf de chaque hôte quelque chose comme :
*.info;mail.none;cron.none @stage7
> et, sur 'stage7', dans le même fichier :
*.=info;*.=notice /dev/tty5 # par exemple
> sans oublier de relancer syslogd à chaque fois :
[stage3] # killall -HUP syslogd
>> Sous freeBSD, il faut désactiver l'option -s de 'syslogd', de façon à ce qu'il accepte d'envoyer ses logs à distance. Sous Linux, l'option -r du même syslogd devra être passée au serveur (de logs), afin qu'il accepte de les recevoir.
>> Pour nous tenir au courant des actions réalisées par 'cfagent', il faudra activer les variables 'Syslog' et 'Inform' dans la section 'control' de 'cfagent.conf' (cf la version étendue de ce dernier à la section Explorations).
>> Certaines commandes, telles 'copy', ou 'disable', possèdent leurs propres options 'syslog' et 'inform' (sans majuscules initiales pour elles).
La chasse aux messages d'erreur est ouverte !
Haut, Sommaire, Simulation de Déploiement
Avant de se lancer dans nos expérimentations débridées, prenons un moment pour configurer 'cfrun.hosts', le but étant de tester l'effet qu'aura chaque nouvelle action sur nos hôtes, sans quitter notre 'policyhost', en tapant simplement :
[stage2] # cfrun
qui fera s'exécuter 'cfagent --no-splay' (voir la variable 'Splaytime', un peu plus bas), sur chaque hôte présent dans 'cfrun.hosts', ou bien :
[stage2] # cfrun stage3
qui le fera s'exécuter uniquement sur 'stage3'.
> petite précision utile : la commande 'cfrun' doit être exécutée depuis le répertoire où réside 'cfrun.hosts', lequel peut se trouver à peu près n'importe où (pourvu que cela soit spécifié dans la section 'grant' de 'cfservd.conf':-)
# Allons-y donc :
#############################################################
#
# cfrun.hosts
# Voici les hôtes qui pourront être contactés à distance
#
#############################################################
domain=labri.fr
access=root
# simple mesure de précaution.
stage3
stage4
stage6
#
#
#
# C'est tout (mais il fallait bien le faire quand même :-)
Notre système étant à présent bien en place, et fonctionnel, nous pouvons reprendre notre principal fichier de conf, 'cfagent.conf', et explorer quelques-unes de ses (nombreuses) fonctionnalités.
#############################################################
#
# cfagent.conf.
# à tester sans modération !
#
#############################################################
control:
domain = ( labri.fr )
Syslog = ( on )
Inform = ( on )
# Si on veut se tenir au courant...
smtpserver = ( stage2 )
sysadm = ( root@stage2 )
# 'cfexecd', grand épistolier, exige ceci.
schedule = ( Min00_05 Min30_35 )
# pour que 'cfexecd' s'exécute chaque demi-heure.
SplayTime = ( 2 )
# Une variable interne pour dire aux hôtes d'attendre un laps de temps
# (nombre aléatoire généré par cfengine, et différent pour chaque hôte),
# ici compris entre 0 et 2 minutes, avant d'exécuter 'cfagent'.
# Ceci afin d'éviter une surcharge du $(policyhost)
# si de trop nombreux hôtes le sollicitent au même moment.
# note : Pour exécuter 'cfagent' avec le SplayTime à zéro
# (i.e. sans attendre jusqu'à deux minutes qu'il veuille bien se décider),
# il conviendra de lui passer l'option '-q' (équivalent à '--no-splay').
IfElapsed = ( 2 )
# Option anti-spam. L'intervalle de temps entre deux exécutions de 'cfagent'
# ne pourra être inférieur à 2 minutes.
# Par défaut, elle est d'une minute — ce qui suffirait amplement ici.
ChecksumDatabase = ( /var/cfengine/cf.db )
# Créera un fichier contenant les 'checksums' des différents fichiers (ou répertoires)
# dont nous demanderons à 'cfagent' de vérifier l'intégrité.
# Cf l'action 'files', un peu plus bas.
import:
stage5::
cf.stage5.conf
# Imaginons que nous ayons un hôte TRÈS particulier (ou plusieurs :-{
# auquel cas on en ferait un 'group' et ça reviendrait au même) ;
# un hôte si particulier,
# qu'aucune des actions entreprises ici ne le concerneraient vraiment,
# et que les actions qui le concerneraient ne concerneraient que lui...
# Eh bien, à supposer qu’un tel hôte existe, le mieux serait encore d'écrire
# un fichier de conf' rien que pour lui, et de spécifier à 'cfagent',
# grâce à la section 'import', d'aller lire le fichier de conf' en question.
# Accessoirement, ça nous évite d'avoir des fichier de conf' sambaïques
# (qui n'en finissent pas).
actionsequence = ( links files editfiles disable )
# important : les actions spécifiées seront exécutées
# DANS CET ORDRE-LÀ.
links:
any::
/var/cfengine/bin/cfagent -> /usr/local/sbin/cfagent
# Histoire de rafraîchir la mémoire défaillante de 'cfexecd'.
# L'action 'links' est l'équivalent de la commande 'ln -s'.
# La structure : fichier_dest -> fichier_source est, quant à elle,
# identique à ce que retourne la commande 'ls -l'.
files:
linux::
/etc/shadow mode=400 owner=root action=fixall
# L'action 'files' est un bon moyen de préserver l'intégrité
# de certains fichiers sensibles...
any::
/var/cfengine/inputs mode=755 action=fixall
/var/cfengine/outputs mode=755 action=fixall
# ... et de contrôler les effets incontrôlables du même 'cfexecd' !
# En l'occurence, 'cfexecd' colle 700 à ces deux répertoires.
# Moi, je trouve pratique de pouvoir les consulter en tant que simple user.
/sbin checksum=md5 recurse=inf
# Si quelque chose change dans ce répertoire, à chaque exécution de 'cfagent'
# un message d'alerte nous en préviendra, jusqu'à ce que nous ayons fixé le problème,
# reconstruit la base (i.e. supprimé le fichier /var/cfengine/cf.db,
# lequel se recréera tout seul à la prochaine exécution de 'cfagent'), etc.
editfiles:
# Cette action permet d'éditer des fichiers ASCII,
# et possède de nombreuses options, dont celle-ci,
# qui va nous permettre de boucher le vilain trou de sécurité
# que nous avons 'volontairement' créé plus haut :
{ /etc/ssh/sshd_config
CommentLinesStarting "PermitRootLogin"
}
# Et la ligne commençant par la chaîne placée entre guillemets sera (à nouveau) commentée.
# Mieux : 'cfagent' veillera désormais à ce qu'elle le reste !
# Par défaut, les commentaires sont les mêmes que ceux employés ici ('#'),
# mais il est possible d'en spécifier d'autres (cf 'ref.man.', comme d'hab').
{ /etc/rc.d/init.d/cfengine
AppendIfNoSuchLine "# start cfservd"
AppendIfNoSuchLine "/usr/local/sbin/cfservd"
AppendIfNoSuchLine "# start cfexecd"
AppendIfNoSuchLine "/usr/local/sbin/cfexecd"
AppendIfNoSuchLine "# start cfenvd"
AppendIfNoSuchLine "/usr/local/sbin/cfenvd"
}
# Sans commentaires, si j'ose dire :-)
# Si l'on change d'avis, par exemple pour 'cfexecd', un simple :
{ /etc/rc.d/init.d/cfengine
DeleteLinesContaining "cfexecd"
}
# devrait suffire.
{ /etc/hosts
AppendIfNoSuchLine "192.168.0.17 stage7.labri.fr stage7"
}
# Bienvenue au nouvel hôte !
# Allons, un jour ou l'autre, il faudra se faire une raison :
disable:
# rajoutera l'extension .cfdisabled...
any::
/usr/bin/sudo syslog=on inform=true
# ...à ce fichier, en conséquence de quoi il deviendra inopérant :-(
#
#
#
# fin (toute provisoire) de cfagent.conf.
Haut, Sommaire, Explorations
Je souhaite vivement que la lecture de ce papier vous a intéressé et vous a donné envie d'en savoir plus sur cfengine, voire de l'installer, ce qui resterait, à mon sens, la meilleure façon d'en savoir plus.
Pour ma part, j'ai pris beaucoup de plaisir à travailler sur ce logiciel (spécialement dans les conditions décrites plus haut).
Cet épilogue parlera de trois choses :
Ce que j'ai apprécié :
- l'éventail de fonctionnalités offertes par le logiciel, éventail que je n'ai fait qu'entrouvrir ici, et qui me paraît pouvoir s'élargir presque à l'infini. Ceci est dû non pas tant à la quantité d'actions possibles, qu'à la possibilité de les combiner afin de répondre au plus près aux besoins de l'administrateur.
- son efficacité. Associé à des outils comme Openssh, xinetd, nmap et quelques autres, il rendra d'immenses services en centralisant, rationnalisant et simplifiant les multiples tâches quotidiennes à accomplir, en rendra de nouvelles possibles — et contribuera ainsi à améliorer l'efficience, voire la sécurité du système.
- sa relative simplicité d'utilisation. Même un sysadmin débutant, à condition de posséder quelques notions de base d'un système unix, peut tirer grand profit de cfengine. Personnellement, il m'a beaucoup apporté dans l'approfondissement de mes connaissances.
- sa fiabilité. Je n'ai eu aucune mauvaise surprise de ce côté-là ; aucun bug à déplorer. Mais bien sûr je n'ai pas poussé son étude très loin. Surtout je ne l'ai pas utilisé en situation 'réelle'...
Ce que j'ai moins apprécié :
- l'absence d'un papier comme celui que vous avez entre les mains :-).
Si les deux principaux fichiers fournis, le 'Tutorial' et le 'Reference Manual', font à eux deux le tour de la question, on ne peut pas vraiment parler de visite guidée ! Bon, mais j'étais là aussi un peu pour ça...
- personnellement, j'aimerais bien que l'édition de fichiers soit plus élaborée, notamment en ce qui concerne les interventions possibles sur les champs. En fait, j'aimerais bien que l'action 'editfiles' devienne une sorte de front-end pour 'gawk' :-)
Ce dont je n'ai pas parlé :
- NT ! Je n'ai pas eu le temps de m'occuper de lui :----- o
De toute manière, comme nous l'apprend la page qui lui est consacrée, seule l'action 'copy' est actuellement supportée (ça c'est la vérité !).
Si vous voulez malgré tout tenter l'aventure, celle-ci commence avec le téléchargement, puis l'installation de cygwin ("GNU + Cygnus + Windows") disponible ici : www.cygwin.com.
Ce dernier vous installe un shell et tous les outils de base, après quoi vous téléchargez les différentes sources et vous les installez comme si vous étiez sous unix. Ensuite... je ne sais pas.
- De nombreuses fonctionnalités de cfengine concernent l'utilisation de NFS (Network File System), ou système de fichiers distribués. Préférant concentrer mes efforts sur d'autres fonctionnalités, il ne m'a pas paru essentiel de le mettre en œuvre...
Jusqu’à ce que j’apprenne (à la dernière minute !) que c'est précisément sur NFS que repose tout le système mis en place au Labri :-[].
Cela dit, Patrick m'a dispensé de creuser plus avant la question, se réservant, entre deux appels (à l'aide), l'étude de la chose (en question).
- Une autre fonctionnalité de cfengine, de relative importance, dont je n'ai pas touché un mot ici, concerne les Access Control Lists (Solaris, DFS, NT).
Par bonheur, celles-ci n'ont pas cours ici, au Labri. Je renverrai donc le lecteur (non sans un certain soulagement), une ultime fois, à l'incontournable 'Reference Manual'.
Haut, Sommaire
- En premier lieu, je tiens à remercier mon formateur principal, Gérard Massol.
Celui-ci, en effet, au-delà de nos approches, disons, différentes de la chose informatique, a fait tout son possible pour m'aider dans ma démarche — même lorsque celle-ci ne se conformait plus guère au programme initial de la formation. Il a en outre obtenu auprès de la direction qu'un intervenant extérieur (Nicolas Dubrocas, que je profite pour remercier également ici) vienne m'instruire personnellement de quelques aspects fondamentaux d'un système Unix / Linux.
- Je remercie aussi bien sûr Patrick, ingénieur système au Labri, pour la confiance et l'écoute qu'il a su m'accorder, ainsi que pour les encouragements qu'il m'a adressé — toutes choses qui ont contribué au bon, voire à l'excellent déroulement de mon stage.
- Merci enfin à l'ensemble de la communauté si vivante et si riche du logiciel libre, sans qui mon engouement pour l'informatique ne serait pas ce qu'il est. Sans qui l'informatique, disons le tout net, ne serait QUE ce qu'elle est encore, hélas, trop souvent : une chose ennuyeuse et sans âme, entre des mains cupides...
Haut, Sommaire
La page regroupant la documentation de cfengine
La page de Mark Burgess consacrée à l'immunologie
"Porting cfengine to Windows NT", Bjørn Gustafson et Jørgen Kjensfi
"Utilisation du logiciel d'administration automatique Cfengine", Ronan Keryell
"Use of Cfengine for Automated, Multi-Platform Software and Patch Distribution", David Ressman & John Valdès, University of Chicago
Haut, Sommaire
© Thierry Lhomme, 2002.
home