| Home Profile Fun |
#130 Linux 03.06.2007
Gentoo configuration management with Cfengine and SubversionCfengine is an autonomous agent. It allows centralized setup and maintenance of a large number of hosts. Another aspect of Cfengine is that it makes these hosts more independent of system administrators. With every run of Cfengine the hosts get closer to a defined state. This state is described through policy rules inside the Cfengine configuration files. The article describes a very basic setup of Cfengine. The main purpose is to show how to get things up and running. Of course there are alternative scenarios possible. Cfengine is very flexible. We will have one policy master and one client machine. Additionally Subversion and Bind will be installed on the master. Subversion is great to have all our policies version controlled. In case there are problems with a configuration we can simply run an old version and thus have something which acts as a rollback. The involved servers are master1.home, 192.168.1.34 client1.home, 192.168.1.33 A typical workflow may look like this: +On the master make changes to the Cfengine configuration files inside the working directory (/home/cfwork) +Commit these changes to SVN +Update the Cfengine configuration files inside the master repository from SVN (/var/cfengine/masterfiles/inputs) +Run cfagent to distribute and apply these new files from the repository to the clients, and to the master itself, as it is also controlled by Cfengine. (/var/cfengine/inputs) Some background info may be necessary to understand how Cfengine works: The main configuration files are cfservd.conf, update.conf, cfagent.conf. First the file update.conf is used to transfer all configuration files from the master to the client. This file should only be changed during the initial setup. Later on all configuration is done inside cfagent.conf. The reason is to make sure that a correct cfagent.conf can always be copied to the client. Imagine there were syntax errors in cfagent.conf which would prevent cfagent from running at all. In this scenario a corrected version of cfagent.conf could not be fetched from the master any more. cfservd ist the main daemon. It serves as a file server on the master host. On the clients it is needed to access cfagent from outside. Overview of the necessary steps to set up Cfengine 1. Set up Cfengine on the master 2. Set up SVN on the master (3. Set up Bind on the master) 4. Set up Cfengine on the client 1. Set up Cfengine on the master emerge -va cfengine mkdir /var/cfengine/masterfiles 2. Set up SVN on the master emerge -va subversion Create SVN repository: mkdir /usr/local/svn svnadmin create /usr/local/svn/repos mkdir /tmp/cfwork Create all necessary Cfengine configuration files in here: #cfagent.conf
control:
actionsequence = ( tidy copy shellcommands )
domain = ( home )
policyhost = ( 192.168.1.34 )
access = ( root )
cfrunCommand = ( "/usr/sbin/cfagent" )
IfElapsed = ( 0 )
groups:
master = ( mobile1 )
clients = ( client1 )
tidy:
/tmp pattern=*.txt age=3 recurse=inf
copy:
/tmp/test.txt dest=/tmp/test1.txt
shellcommands:
master::
"/bin/sh -c 'svn update /var/cfengine/masterfiles/inputs >/dev/null 2>/dev/null'"
#cfrun.hosts domain = home master1.home client1.home #cfservd.conf
groups:
policyhost = ( master1 )
control:
domain = ( home )
TrustKeysFrom = ( 192.168.1 )
AllowUsers = ( root )
AllowAccessFrom = ( 192.168.1 )
AllowMultipleConnectiosFrom = ( 192.168.1 )
#Use this in dhcp environment
#DynamicAddresses = ( 192.168.1 )
cfrunCommand = ( "/var/cfengine/bin/cfagent" )
IfElapsed = ( 0 )
ExpireAfter = ( 15 )
MaxConnections = ( 50 )
MultipleConnections = ( true )
grant:
any::
/var/cfengine/inputs *.home
/var/cfengine/masterfiles/inputs *.home
/var/cfengine/bin/cfagent *.home
/var/cfengine *.home
/tmp 192.168.1.0/24
#update.conf
control:
actionsequence = ( copy )
domain = ( home )
policyhost = ( master1 )
IfElapsed = ( 0 )
master_cfinput = ( /var/cfengine/masterfiles/inputs )
repository = ( /var/cfengine/outputs )
copy:
$(master_cfinput)/cfagent.conf dest=/var/cfengine/inputs/cfagent.conf
mode=600
server=$(policyhost)
force=true
trustkey=true
$(master_cfinput)/update.conf dest=/var/cfengine/inputs/update.conf
server=$(policyhost)
force=true
trustkey=true
$(master_cfinput)/cfservd.conf dest=/var/cfengine/inputs/cfservd.conf
server=$(policyhost)
force=true
trustkey=true
$(master_cfinput)/cfrun.hosts dest=/var/cfengine/inputs/cfrun.hosts
server=$(policyhost)
force=true
trustkey=true
Initially import these files into SVN: svn import /tmp/cfwork file:///usr/local/svn/repos/cfwork -m "Initial import" Create our working directory: cd /home svn checkout file:///usr/local/svn/repos/cfwork Initially checkout the files to /var/cfengine/masterfiles/inputs: cd /var/cfengine/masterfiles svn checkout file:///usr/local/svn/repos/cfwork mv cfwork inputs Later on we go to /home/cfwork, work on the configuration files and commit/update everything like this: cd /home/cfwork svn commit -m "Test" svn update /var/cfengine/masterfiles/inputs (3. Set up Bind on the master) Bind is not necessarily needed for this example environment. Just in case you want to use it, here is how to create a basic setup. emerge -va bind Edit/create the following three files /etc/bind/named.conf:
options {
directory "/var/bind";
listen-on-v6 { none; };
listen-on { 127.0.0.1; 192.168.1.34; };
pid-file "/var/run/named/named.pid";
};
zone "." IN {
type hint;
file "named.ca";
};
zone "localhost" IN {
type master;
file "pri/localhost.zone";
allow-update { none; };
notify no;
};
zone "127.in-addr.arpa" IN {
type master;
file "pri/127.zone";
allow-update { none; };
notify no;
};
zone "home" IN {
type master;
file "pri/home";
allow-update { none; };
notify no;
};
zone "0.168.192.in-addr.arpa" IN {
type master;
file "pri/home_rev";
allow-update { none; };
notify no;
};
The last two entries are relevant for our setup. The first one defines the forward zone which is configured in /etc/bind/pri/home. The second one defines the reverse zone which is configured in /etc/bind/pri/home_rev. /etc/bind/pri/home: $TTL 1W
@ IN SOA ns.localhost. root.localhost. (
2007060201 ; Serial
28800 ; Refresh
14400 ; Retry
604800 ; Expire - 1 week
86400 ) ; Minimum
@ IN NS ns
ns IN A 127.0.0.1
master1 IN A 192.168.1.34
client1 IN A 192.168.1.33
Don't forget to set the actual Serial: yearmonthdaynr /etc/bind/pri/home_rev: $TTL 1W
@ IN SOA ns.localhost. root.localhost. (
2007060201 ; Serial
28800 ; Refresh
14400 ; Retry
604800 ; Expire - 1 week
86400 ) ; Minimum
@ IN NS ns
ns IN A 127.0.0.1
34 IN PTR master1.home.
33 IN PTR client1.home.
Also here, don't forget the Serial and the dots at the end of the PTR records. If you want to use Bind /etc/resolv.conf must contain the IP of master1. nameserver 192.168.1.34 /etc/hosts must contain these records: 127.0.0.1 localhost 192.168.1.34 master1.home master1 192.168.1.33 client1.home client1 Now let's (re)start the daemons: /etc/init.d/named restart /etc/init.d/svnserve restart For the initial start of cfservd we copy the configuration files manually from the repository to /var/cfengine/inputs: cp /var/cfengine/masterfiles/inputs/* /var/cfengine/inputs cfservd -f /var/cfengine/inputs/cfservd.conf(cfservd is the main daemon. It runs on the master and all clients by using the files in /var/cfengine/inputs.) 4. Set up Cfengine on the client The last step is to set up Cfengine on the client machine. emerge -va cfengine For the initial start of cfservd we copy the configuration files manually from the repository of the master: scp 192.168.1.34:/var/cfengine/masterfiles/inputs/* /var/cfengine/inputs/and start the daemon cfservd -f /var/cfengine/inputs/cfservd.conf If you use Bind /etc/resolv.conf must contain the IP of master1. nameserver 192.168.134 /etc/hosts must contain these records: 127.0.0.1 localhost 192.168.1.34 master1.home master1 192.168.1.33 client1.home client1 Now all is set up and we can make some tests. First we go back to the master and change a policy. Then we check if it is processed on the master: cd /home/cfwork vi cfagent.conf In this example we want the file /tmp/test.txt to be copied to /tmp/test2.txt instead of /tmp/test1.txt. So we change the following line accordingly: /tmp/test.txt dest=/tmp/test2.txt Then create the source file /tmp/test.txt. echo "Test" > /tmp/test.txt (Make sure that /tmp/test2.txt does no exist.) Commit und update with SVN: svn commit -m "Test" svn update /var/cfengine/masterfiles/inputs Execute cfagent to apply the new policy: cfagent -v The output shows clearly that the policy rule was applied. Thus we find test2.txt in /tmp: ********************************************************************* Main Tree Sched: copy pass 1 @ Sat Jun 2 09:02:30 2007 ********************************************************************* Checking copy from localhost:/tmp/test.txt to /tmp/test2.txt cfengine:mobile1: /tmp/test2.txt wasn't at destination (copying) cfengine:mobile1: Copying from localhost:/tmp/test.txt cfengine:mobile1: Object /tmp/test2.txt had permission 600, changed it to 644 Ok, on the master it works as expected. Finally we want cfagent to run on all clients. One possibility is to manually execute cfrun: cfrun -d -f /var/cfengine/inputs/cfrun.hostsThis command executes cfagent on all clients defined in cfrun.hosts (on the master as well as it is listed in cfrun.hosts). As this is the first communication between master and client, you will be prompted to accept the public keys of the clients. On client1 we see that cfagent has copied /tmp/test.txt to /tmp/test2.txt which is exactly what we wanted. It shows that the communication between master and client works and that the client can now be controlled by Cfengine. The next steps are: +Securing the installation, now it's completely open! +Use cfenvd for anomaly detection +Use cron or cfexecd to automatically run cfagent at certain intervals So far for the example Cfengine setup. From here it should be easy to further explore the world of configuration management with cfengine. The best resource I found is the new booklet from sage.org. It's really great and makes things very clear. Alternatives for Cfengine are Puppet and bcfg. |