Cfengine, cet outil qui facilite la vie des ASR…

[ 3 ] Commentaires
Share

En version 3, ce n’est plus pareil alors ?

Presque ! On peut voir déjà une subtile différence au niveau du nom des binaires, cf-servd au lieu de cfservd, cf-agent au lieu de cfagent,… Mais vous me direz, et vous n’avez pas tort, que ce ne sont que des détails.

D’abord, un petit état des lieux. Voici, après installation, ce qui vous a bourgeonné sur le filesystem. Notez en passant que Cfengine tourne sous root par défaut, mais il est possible de changer ce comportement en retouchant un peu le fichier de configuration du serveur. Toutefois, gardez à l’esprit qu’il lui faut certains privilèges pour pouvoir fonctionner normalement.

[kbux@policyhost cfengine]$ ls -lsa /var/lib/cfengine/
total 80
4 drwxr-xr-x 13 root root 4096 2010-01-13 11:43 ./
4 drwxr-xr-x 31 root root 4096 2010-01-12 12:44 ../
4 drwxr-xr-x  2 root root 4096 2010-01-12 12:44 bin/
4 -rw-------  1 root root 1252 2010-01-13 11:43 cf3.localhost.runlog
8 -rw-------  1 root root 8192 2010-01-13 11:43 cfengine_lock_db
4 -rw-------  1 root root    5 2010-01-13 11:43 cf-execd.pid
4 -rw-------  1 root root    5 2010-01-13 11:43 cf-monitor.pid
4 -rw-------  1 root root    5 2010-01-13 11:43 cf-serverd.pid
0 lrwxrwxrwx  1 root root   21 2010-01-12 12:44 inputs -> ../../../etc/cfengine/
4 drwxr-xr-x  2 root root 4096 2009-08-23 21:03 lastseen/
4 drwxr-xr-x  2 root root 4096 2009-08-23 21:03 modules/
4 drwxr-xr-x  2 root root 4096 2010-01-13 11:45 outputs/
4 drwx------  2 root root 4096 2010-01-12 12:45 ppkeys/
4 -rw-------  1 root root  185 2010-01-13 11:18 promise.log
4 drw-r--r--  2 root root 4096 2009-08-23 21:03 randseed/
4 drwxr-xr-x  2 root root 4096 2009-08-23 21:03 reports/
4 drwx------  2 root root 4096 2009-08-23 21:03 rpc_in/
4 drwx------  2 root root 4096 2009-08-23 21:03 rpc_out/
4 drwxr-xr-x  2 root root 4096 2009-08-23 21:03 rpc_state/
4 drwxr-xr-x  2 root root 4096 2010-01-13 11:43 state/

[kbux@policyhost cfengine]# ls -lsa /usr/sbin/cf*
72 -rwxr-xr-x 1 root root 72008 2009-08-23 21:03 /usr/sbin/cf-agent*
60 -rwxr-xr-x 1 root root 60256 2009-10-14 18:54 /usr/sbin/cfdisk*
32 -rwxr-xr-x 1 root root 30896 2009-08-23 21:03 /usr/sbin/cf-execd*
12 -rwxr-xr-x 1 root root 10116 2009-08-23 21:03 /usr/sbin/cf-key*
80 -rwxr-xr-x 1 root root 79908 2009-08-23 21:03 /usr/sbin/cf-know*
52 -rwxr-xr-x 1 root root 51272 2009-08-23 21:03 /usr/sbin/cf-monitord*
12 -rwxr-xr-x 1 root root  9960 2009-08-23 21:03 /usr/sbin/cf-promises*
56 -rwxr-xr-x 1 root root 55700 2009-08-23 21:03 /usr/sbin/cf-report*
20 -rwxr-xr-x 1 root root 18244 2009-08-23 21:03 /usr/sbin/cf-runagent*
76 -rwxr-xr-x 1 root root 76160 2009-08-23 21:03 /usr/sbin/cf-serverd*

[kbux@policyhost cfengine]$ ls -lsa /etc/cfengine/
total 32
4 drwxr-xr-x  2 root root 4096 2010-01-12 12:44 ./
4 drwxr-xr-x 91 root root 4096 2010-01-12 12:45 ../
4 -rw-r--r--  1 root root  204 2009-08-23 21:03 failsafe.cf
4 -rw-r--r--  1 root root 2518 2009-08-23 21:03 library.cf
4 -rw-r--r--  1 root root 1898 2009-08-23 21:03 promises.cf
8 -rw-r--r--  1 root root 6006 2009-08-23 21:03 site.cf
4 -rw-r--r--  1 root root  971 2009-08-23 21:03 update.cf

[kbux@policyhost cfengine]$ grep cfengine /etc/services
cfengine        5308/tcp                        # CFengine
cfengine        5308/udp                        # CFengine

Les éléments importants se situent au niveau de /var/lib/cfengine, ce sont les répertoires inputs, outputs et bin.

  • Inputs servira de référence pour ce qui est de passer les paramètres aux cf-agents, il faut donc faire en sorte que ce répertoire soit copié et maintenu à jour fréquemment par toutes les machines concernées. Vu que dans l’installation, le répertoire est en fait un lien symbolique vers /etc/cfengine, nous retrouvons dedans les 5 fichiers *.cf.
  • Outputs sert de conteneur de reports qui peuvent alors être entreposés et mailés via cf-execd, si on le souhaite.
  • Bin, en revanche, ne contient qu’un lien symbolique vers le binaire /usr/sbin/cf-promises.

Finalement, on retrouve une arborescence tout à fait comparable à celle de cfengine 2. Et oui, car la majeure différence en version 3 est palpable au niveau du langage en lui-même, ce qui amène à une réorganisation complète de la structure interne aux fichiers de configuration. Par ailleurs, de nouveaux composants se sont rajoutés et on nous parle de promises (oui, c’est de l’anglais).

Les promises synthétisent les états que l’on veut maintenir, autrement dit, ce sont vos fichiers de configuration. D’ailleurs, tout ce qui concerne leur gestion, vérification, tout cela dépend de cf-promises. Par exemple, pour vérifier que votre test.cf est correct, rien ne vaut un

[kbux@policyhost cfengine]# cf-promises -v -f /var/lib/cfengine/inputs/test.cf
cf3 Cfengine - autonomous configuration engine - commence self-diagnostic prelude
cf3 ------------------------------------------------------------------------
cf3 Work directory is /var/lib/cfengine
cf3 cf3: INFO: /var/lib/cfengine/inputs is a symbolic link, not a true directory!
cf3 Making sure that locks are private...
cf3 Checking integrity of the state database
cf3 Checking integrity of the module directory
cf3 Checking integrity of the input data for RPC
cf3 Checking integrity of the output data for RPC
cf3 Checking integrity of the PKI directory
cf3 Looking for a source of entropy in /var/lib/cfengine/randseed
cf3 Could not read sufficient randomness from /var/lib/cfengine/randseed
cf3 Loaded /var/lib/cfengine/ppkeys/localhost.priv
cf3 Loaded /var/lib/cfengine/ppkeys/localhost.pub
cf3 Setting cfengine default port to 5308 = 5308
cf3 Reference time set to Sun Jan 17 11:48:27 2010
cf3 Cfengine - 3.0.2 (C) Cfengine AS 2008-
cf3 ------------------------------------------------------------------------
cf3 Host name is: policyhost
cf3 Operating System Type is linux
cf3 Operating System Release is 2.6.31.5-desktop-1mnb
cf3 Architecture = i686
cf3 Using internal soft-class linux for host localhost
cf3 The time is now Sun Jan 17 11:48:27 2010
cf3 ------------------------------------------------------------------------
cf3 # Extended system discovery is only available in version Nova and above
cf3 Additional hard class defined as: 32_bit
cf3 Additional hard class defined as: linux_2_6_31_5_desktop_1mnb
cf3 Additional hard class defined as: linux_i686
cf3 Additional hard class defined as: linux_i686_2_6_31_5_desktop_1mnb
cf3 GNU autoconf class from compile time: compiled_on_linux_gnu
cf3 Address given by nameserver: 127.0.0.1
cf3 Interface 1: lo
cf3 Interface 2: eth0
cf3 Trying to locate my IPv6 address
cf3 Found IPv6 address inet6:
cf3 Found IPv6 address fe80::a00:27ff:fe24:ef9f
cf3 Found IPv6 address inet6:
cf3 Looking for environment from cf-monitor...
cf3 Loading environment...
cf3 Environment data loaded
cf3 This appears to be a mandriva system.
cf3 Looking for Mandriva linux info in "Mandriva Linux release 2010.0 (Official) for i586
"
cf3 This appears to be a LSB compliant system.
cf3 ***********************************************************
cf3  Loading persistent classes
cf3 ***********************************************************
cf3 ***********************************************************
cf3  Loaded persistent memory
cf3 ***********************************************************
cf3   > Parsing file /var/lib/cfengine/inputs/test.cf
cf3 Initiate variable convergence...
cf3 Initiate variable convergence...
cf3 # Knowledge map reporting feature is only available in version Nova and above
cf3  -> Defined hard classes = { any verbose_mode Sunday Hr11 Morning Min48 Min45_50 Q4 Hr11_Q4 Day17 January Yr2010 Lcycle_0 GMT_Hr10 linux localhost undefined_domain 32_bit linux_2_6_31_5_desktop_1mnb i686 linux_i686 linux_i686_2_6_31_5_desktop_1mnb linux_i686_2_6_31_5_desktop_1mnb__1_SMP_Fri_Oct_23_01_46_54_EDT_2009 compiled_on_linux_gnu net_iface_lo net_iface_eth0 ipv4_10_0_2_15 ipv4_10_0_2 ipv4_10_0 ipv4_10 inet6_ fe80__a00_27ff_fe24_ef9f rootprocs_high_normal syslog_low_normal messages_low_normal entropy_netbiosns_in_low entropy_netbiosdgm_in_low entropy_netbiosssn_in_low entropy_irc_in_low entropy_cfengine_in_low entropy_nfsd_in_low entropy_smtp_in_low entropy_www_in_low entropy_ftp_in_low entropy_ssh_in_low entropy_wwws_in_low entropy_netbiosns_out_low entropy_netbiosdgm_out_low entropy_netbiosssn_out_low entropy_irc_out_low entropy_cfengine_out_low entropy_nfsd_out_low entropy_smtp_out_low entropy_ftp_out_low entropy_ssh_out_low entropy_icmp_in_low entropy_udp_in_low entropy_dns_in_low entropy_tcpsyn_in_low entropy_tcpack_in_low entropy_tcpfin_in_low entropy_misc_in_low entropy_icmp_out_low entropy_udp_out_low entropy_dns_out_low entropy_tcpsyn_out_low entropy_tcpack_out_low entropy_tcpfin_out_low entropy_misc_out_low cfengine_3_0_2 cfengine_3_0 cfengine_3 Mandrake Mandriva mandriva mandriva_2010 mandriva_2010_0 lsb_compliant mandrivalinux mandrivalinux_adelie mandrivalinux_2010_0 mandrivalinux_2010 common }
cf3  -> Negated Classes = { }
cf3 Initiate variable convergence...
cf3 Initiate control variable convergence...
cf3 Inputs are valid

Le -v vous inonde d’informations. Ne vous perdez pas, seule la dernière ligne compte, puisqu’elle vous indique explicitement que votre configuration est valide.

Mais alors, comment faire votre cf, à proprement parler ? Et bien, comme vous avez pu lire, il faut d’abord revenir sur notre bon vieux concept concernant le composant central de Cfengine : les classes.

Les classes sont toujours présentes et organisées en 2 grandes familles, les hard et les soft, dont on peut d’ailleurs facilement en avoir la liste avec un cf-promises -v. Elles sont définies exclusivement au sein de bundle, ces derniers pouvant être typés.

Un bundle est un container qui permet de regrouper plusieurs déclarations et définitions de promesse. Le type d’un bundle est généralement le nom du composant auquel il est destiné. Le typage intervient sur la portée des classes et des traitements associés.

Concrètement, un type common invoquera des classes globales au sens de la portée de la classe, un type server sera destiné… Et bien à notre cf-servd, le serveur -que nous appellerons aussi le policy host-, tandis que le type agent impliquera une utilisation locale des classes, et sera destiné à nos cf-agents -nos hosts.

Il existe d’autres containers que le bundle, le body par exemple. Le body permet surtout d’organiser une série de paramétrages complexes sous une et une seule dénomination afin de faciliter son exploitation dans les divers bundles du fichier. Le nombre de body n’est pas limité, il est d’ailleurs recommandé d’en disposer plutôt que de lister toutes les variables, crûment, au sein du bundle en lui-même et de nuire ainsi à la lisibilité du fichier… Sans compter tout ce qui concerne la réutilisabilité du code…

En logique, nous avons ça :

type:
classes::
"promiser"->{"promisee1","promisee2",...}
attribute_1=>value_1,
attribute_2=>value_2,
...
attribute_n=>value_n;

La promesse la plus simple est la suivante.

commands:
"/bin/echo Hello World";

commands est un type de promiser qui est défini par défaut dans Cfengine. Le promiser en lui-même ne constitue qu’une entité aux yeux de Cfengine, n’override pas les paramètres par défaut qui lui sont appliqués et n’en prend aucun.

Et voici l’exemple illustrant le contenu d’un fichier /var/lib/cfengine/inputs/test.cf. L’association d’une variable avec sa valeur est faite via la syntaxe nomvariable => valeurs.

body common control {
bundlesequence => { "g", "tryclasse1", "tryclasse2" };
}

##
bundle common g {
classes:
"one" expression => "any" ;
}

##
bundle agent tryclasse1 {
classes:
"two" expression => "any" ;
}

##
bundle agent tryclasse2 {
classes:
"three" expression => "any";

reports:
one.three.!two::
"Success" ;
}

Ici, la classe one est globale à tout le fichier, alors que les two et three sont locales. Il en résulte que, dans le bundle tryclasse2, le report renvoie Success, car one est connue, three est connue, et two ne l’est pas.

Toujours des détails, rien que ça, mais ça permet de mettre en lumière 2-3 éléments clés.

Tout d’abord, la partie bundlesequence contenue dans la première déclaration, celle du body common control. C’est cette partie qui décide de l’enchainement des traitements. Ici, dans cet exemple succinct, nous demandons g, puis tryclasse1 et enfin tryclasse2.

Il se trouve que c’est le même ordre dans lequel les classes sont déclarées, ne vous y trompez pas, la déclaration n’a aucun rapport avec l’ordre d’invocation. Seule la bundlesequence fait foi. Et c’est d’ailleurs pour ça que sa déclaration est en common. Common est le type de déclaration globale.

Pour tester cette configuration, vous pouvez attaquer par un cf-promises -f <file> pour la syntaxe et enchainer sur un cf-agent -f <file> pour voir ce qui est vraiment fait.

[kbux@policyhost cfengine]# cf-promises -f /var/lib/cfengine/inputs/test0.cf
[kbux@policyhost cfengine]# cf-agent -f /var/lib/cfengine/inputs/test0.cf
R: Success

En un peu plus élaborée et demandant quelques argument, et nous avons :

bundle agent testbundle
{
files:
"/var/cfengine/inputs"

handle=>"update_policy",
depends_on=>"serve_updates",
perms=>system("600"),
copy_from=>mycopy("$(master_location)","$(policy_server)"),
depth_search=>recurse("inf"),
file_select=>input_files,
action=>immediate;
}

bundle server access_rules
{
access:
"/var/lib/cfengine/inputs"
admit=>{"192.168.0.*"};
}

body copy_from mycopy(from,server)
{
source=>"$(from)";
servers=>{"$(server)"};
copy_backup=>"true";
special_class::
purge=>"true";
}

Il est possible d’accéder aux variables définies au sein d’un bundle -soit dit en passant, si ce n’était pas le cas, où serait la fonctionnalité d’en disposer ?- pour peu qu’elles soient accessibles (mais si ! Rappelez-vous du scope des containers). Pour cela, la syntaxe rappelle celle du Java : nombundle.var.

Nous n’en sommes toujours pas au niveau de ce que je vous avais promis, à savoir l’interaction et les traitements automatisés entre les hosts et le policy host (notre serveur Cfengine), mais nous n’en sommes pas loin.

Comme je vous l’ai dit auparavant, Cfengine fonctionne sur un système de clé basé sur un modèle semblable à celui d’OpenSSH et mis en place par cf-keys. Ce qui implique donc la mise en place de l’infrastructure clé de Cfengine (vous savez, les copies des .pub sur le serveur et les hosts concernés), et naturellement, puisque Cfengine, par défaut, est imperméable aux sollicitations des nouvelles clés, un paramétrage plus fin des accès.

Ca aussi, ça passe par des bundles de classe. Plus particulièrement, ces bundles étant destinés au serveur, leur typage lui aussi se distingue de ceux que nous avons vu jusqu’à présent : ils sont de type server.

Le mécanisme doit passer plusieurs étapes. Tout d’abord, l’authentification.

body server control
{
allowconnects=>{"127.0.0.1","::1" ...etc };
allowallconnects=>{"127.0.0.1","::1" ...etc };
trustkeysfrom=>{"127.0.0.1","::1" ...etc };
}

Vos armes pour tester l’authentification sont principalement cf-agent -v et cf-servd -v. Éventuellement, vous pouvez passer en mode debug avec -d2. N’oubliez pas que vous devez enlever le trustkeysfrom de l’hôte qui a fini avec succès l’échange de clé. En effet, une fois la transmission acceptée, la déclaration de cet hôte à cet endroit n’a plus aucun intérêt.

Petit point intéressant, si vous mettez en place sur une architecture conséquente, ça serait long de devoir se taper toutes les IPs à la main. On peut donc mettre, au lieu de notre trustkeysfrom un petit trustkey=> »true », ce qui aurait pour conséquence d’accepter toutes les sollicitations d’échange, et d’enregistrer toutes les clés associées.

Avec tout ce que cela peut impliquer. Donc à faire sur un réseau safe et surtout de penser à le désactiver une fois tous les hosts enregistrées, car cela peut être dangereux.

Ensuite, une fois que l’accès est ok, il vous faut spécifier qui a accès à quoi.

bundle server access_rules()
{
access:
"/var/lib/cfengine"
admit=>{"127.0.0.1","192.168.0.*"},
deny=>{"172\..*"};
}

Au final, votre configuration de serveur pourra éventuellement ressembler à ça :

#######################################################
# Server configuration
#######################################################
bundle server access_rules()
{
access:
"/var/lib/cfengine"
admit => { "127.0.0.1", "192.168.0.*" };
# Rule for cf-runagent
"/home/kbux/.cfagent/bin/cf-agent"
admit => { "127.0.0.1" };

# New in cf3 - RBAC with cf-runagent
roles:
".*" authorize => { "kbux" };
}

Il vous faut également, en plus du server.cf et du promises.cf, notre fameux update.cf qui donnera le ton aux cf-agents distants et un failsafe.cf.

L’update.cf devra être le plus stable possible dans le temps, ne jamais le modifier serait l’idéal. Dans le cas où vous êtes amené à le modifier, faites en sorte de vérifier qu’il fonctionne correctement. L’officiel met à disposition cette matrice d’update.cf. A vous de jouer !

#######################################################
#
# update.cf
#
#######################################################
bundle agent update
{
files:
"/var/cfengine/inputs"
perms => system("600"),
copy_from => mycopy("/home/mark/cfengine-inputs","localhost"),
depth_search => recurse("inf");
"/var/cfengine/bin"
perms => system("700"),
copy_from => mycopy("/usr/local/sbin","localhost"),
file_select => cf3_files,
depth_search => recurse("inf");
}
############################################
body perms system(p)
{
mode => "$(p)";
}
############################################
Chapter 4: A complete conguration 51
body file_select cf3_files
{
leaf_name => { "cf-.*" };
file_result => "leaf_name";
}
#########################################################
body copy_from mycopy(from,server)
{
source => "$(from)";
compare => "digest";
}

Le failsafe.cf, quant à lui, est destiné aux cas où Cfengine n’arrive pas à lire votre configuration principale. C’est votre porte de sortie, notamment lorsqu’une migration ne s’est pas exactement passée comme prévue.

Lui aussi devra subir aussi peu de changements que possible, et être testé très sérieusement pour éviter justement de ne pas vous en sortir en cas de pépin. Je vous donne également la matrice officielle du fichier.

#######################################################
#
# failsafe.cf
#
#######################################################
body common control
{
bundlesequence => { "update" };
}
#########################################################
bundle agent update
{
files:
"/var/cfengine/inputs"
perms => system,
copy_from => mycopy("/home/mark/cfengine-inputs","localhost"),
file_select => cf3_files,
depth_search => recurse("inf");
"/var/cfengine/bin"
perms => system,
copy_from => mycopy("/usr/local/sbin","localhost"),
file_select => cf3_files,
depth_search => recurse("inf");
}
#########################################################
body perms system
{
mode => "0700";
}
#########################################################
body depth_search recurse(d)
{
depth => "$(d)";
}
############################################
body file_select cf3_files
{
leaf_name => { "cf-.*" };
file_result => "leaf_name";
}
#########################################################
body copy_from mycopy(from,server)
{
source => "$(from)";
servers => { "$(server)" , "failover.domain.tld" };
#copy_backup => "true";
#trustkey => "true";
encrypt => "true";
}

Voilà. Normalement, vous êtes parés niveau configuration. Il ne vous reste plus qu’à tester, piocher sur les expressions régulières et les détails de variables, à savoir les listes, les scalaires, etc, etc, et vous serez vite enclin à adopter la solution.

Allez-y doucement tout de même et n’oubliez pas d’exclure votre policy host des hosts qui doivent mettre leurs configurations à jour.

Vous pouvez retrouver des ensembles consistants de fichier de configuration directement en lisant les documents officiels.

3408494K

3 Responses to Cfengine, cet outil qui facilite la vie des ASR…

  1. Nicolas C. dit :

    Article très interressant. Juste une question : comment est ce que cfengine 3 a été installé ?
    Je ne reconnais pas l’arborescence traditionnelle (/var/cfengine/bin, inputs, …)

  2. K-TUX dit :

    Bonjour,

    Merci pour ton intérêt.

    Cfengine3 a été installé de manière régulière, via les packages mis à disposition pour la distribution de ma machine de test (Ubuntu, donc via apt-get).
    Je dois dire que moi aussi, j’ai été un peu choquée que l’ancienne arborescence ne soit pas reprise, mais je pense que cela s’inscrit dans la politique de coexistence des 2 instances (cfengine 2 et cfengine 3). Si tu as un retour d’expérience concernant cfengine3, son installation comme son exploitation, n’hésite pas à en faire part à la Communauté, c’est toujours très intéressant d’avoir plusieurs cas de figure sur une solution donnée :)

    Bonne journée !

  3. Nicolas C. dit :

    Je ne connais que cfengine 3, que j’utilise quotidiennement ou presque; et j’en suis très satisfait. Je n’ai jamais vu cette arborescence, mais il est vrai que j’ai toujours installé à partir des sources. Et les preconisations que tu fais (surtout sur le failsafe) sont très bonnes

    Pour infos, cfengine fourni maintenant les packages pour toutes les distributions sur son site commercial : https://cfengine.com/inside/myspace
    Il suffit de se creer un compte (gratuit) pour avoir les versions officielles. Je ne les ai pas encore testés (elles ont été liberé il y a a peine une semaine) mais je suppose que l’arborescence est la bonne

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>