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 ;-)


Sommaire



Haut, Sommaire


Sources

note : retrouvez tous les liens utiles sur GNU/cfengine à la section Références - Liens

Voici les meilleures sources auxquelles je me suis abreuvé :

Haut, Sommaire


A propos de Mark Burgess

Mark Burgess

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


Définition

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


Principes Fondamentaux

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


Présentation Détaillée


Les exécutables

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 :

Les classes

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 :
# 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 )

Les fichiers logs

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).


Le langage

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


Environnement de Tests


Préambule

Pour mener à bien ma mission, je disposais :

Nommage et adressage des machines

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.


Systèmes d'exploitation

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é

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


Installation & Configuration


Stratégie


Pré-requis

L'installation de cfengine nécessite l'installation (ou la présence) préalable de : > 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

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)

Post-Installation

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.


Configuration

> 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


Les fichiers .conf


cfagent.conf

> 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.

update.conf

# 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.

cfservd.conf

# 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.

Résumé

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


Simulation de Déploiement


Préliminaires

  1. 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.

  2. 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.

  3. 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 ;-)

Vif du sujet

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


Explorations


Prélude

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 :-)

Fugue

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


Epilogue

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é :

Ce que j'ai moins apprécié :
Ce dont je n'ai pas parlé :

Haut, Sommaire


Remerciements



Haut, Sommaire


Références - Liens

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